diff --git a/package-lock.json b/package-lock.json index 9593224..ac20b82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7045,12 +7045,12 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -14744,12 +14744,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, diff --git a/src/dom.js b/src/dom.js index 6211b05..12c1b38 100644 --- a/src/dom.js +++ b/src/dom.js @@ -76,12 +76,10 @@ const replacePartials = (source, destination, tag) => { class Dom { constructor(html, {minify = true, noscript = 'body'} = {}) { - const jsdom = new JSDOM(html); - + const jsdom = new JSDOM(html.trim()); const {window} = jsdom; const {document} = window; document.$jsdom = jsdom; - this.noscriptPosition = noscript; this.minify = minify; this.html = html; @@ -93,7 +91,6 @@ class Dom { this.bodyElements = []; this.indent = detectIndent(html); - this.headIndent = detectIndent(this.document.querySelector('head').innerHTML); } serialize() { @@ -115,11 +112,19 @@ class Dom { : [...this.bodyElements]; if (head.length > 0) { - result = result.replaceAll(/^([\s\t]*)(<\/\s*head>)/gim, `$1$1${head.join('\n$1$1')}\n$1$2`); + const [, match] = /^([^\S\r\n]*)<\/\s*head>/gim.exec(result) || ['', null]; + const nl = match === null ? '' : `\n`; + const headContent = `${this.indent.indent}${this.indent.indent}${head.join(`${nl}${this.indent.indent}${this.indent.indent}`)}`; + + result = result.replaceAll(`${match || ''}`, `${headContent}${nl}${this.indent.indent}`); } if (body.length > 0) { - result = result.replaceAll(/^([\s\t]*)(<\/\s*body>)/gim, `$1$1${body.join('\n$1$1')}\n$1$2`); + const [, match] = /^([^\S\r\n]*)<\/\s*body>/gim.exec(result) || ['', null]; + const nl = match === null ? '' : `\n`; + const bodyContent = `${this.indent.indent}${this.indent.indent}${body.join(`${nl}${this.indent.indent}${this.indent.indent}`)}`; + + result = result.replaceAll(`${match || ''}`, `${bodyContent}${nl}${this.indent.indent}`); } return result; @@ -183,8 +188,8 @@ class Dom { appendStyles(css, referenceNode) { const styles = this.createStyleNode(css); referenceNode.append(styles); - styles.before(this.document.createTextNode(this.headIndent.indent)); - styles.after(this.document.createTextNode(`\n${this.headIndent.indent}`)); + styles.before(this.document.createTextNode(this.indent.indent)); + styles.after(this.document.createTextNode(`\n${this.indent.indent}`)); } addNoscript(link) { diff --git a/test/expected/index-inlined-async-integrity-print-default.html b/test/expected/index-inlined-async-integrity-print-default.html index aaa1cc9..b98f711 100644 --- a/test/expected/index-inlined-async-integrity-print-default.html +++ b/test/expected/index-inlined-async-integrity-print-default.html @@ -49,7 +49,7 @@

Bootstrap

♥ from the Yeoman team

- - - + + + diff --git a/test/helper/index.js b/test/helper/index.js index 84a31ec..803f8c5 100644 --- a/test/helper/index.js +++ b/test/helper/index.js @@ -26,7 +26,8 @@ export const checkAndDelete = (file) => { } }; -export const strip = (string) => nn(string.replaceAll(/[\r\n]+/gm, ' ').replaceAll(/\s+/gm, '')); +export const strip = (string, safe) => + nn(string.replaceAll(/[\r\n]+/gm, ' ').replaceAll(/\s+/gm, safe ? ' ' : '')).replaceAll(/>\s+<'); export const getBinary = async () => { const {packageJson} = await readPackageUp(); diff --git a/test/index.test.js b/test/index.test.js index 078afba..2a07900 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -721,16 +721,6 @@ test('Keep existing integrity attribute on style tags with media=print', async ( expect(out.toString()).toBe(expected); }); -test('Keep existing integrity attribute on style tags with media=print', async () => { - const html = await read('fixtures/index-integrity.html'); - const css = await read('fixtures/critical.css'); - - const expected = await read('expected/index-inlined-async-integrity-print-default.html'); - const out = inline(html, css); - - expect(out.toString()).toBe(expected); -}); - test('Replace stylesheets (default)', async () => { const html = await read('fixtures/replace-stylesheets.html'); const css = await read('fixtures/css/simple.css'); @@ -808,6 +798,59 @@ test('Replace stylesheets (swap)', async () => { expect(out.toString()).toBe(expected); }); +test('Replace stylesheets (minified, polyfill)', async () => { + const html = await read('fixtures/replace-stylesheets.html'); + const css = await read('fixtures/css/simple.css'); + + const expected = await read('expected/replace-stylesheets-polyfill.html'); + const out = inline(strip(html, true), css, { + strategy: 'polyfill', + replaceStylesheets: ['/css/replaced.css'], + }); + + expect(strip(out.toString())).toBe(strip(expected)); +}); + +test('Replace stylesheets (minified, body)', async () => { + const html = await read('fixtures/replace-stylesheets.html'); + const css = await read('fixtures/css/simple.css'); + + const expected = await read('expected/replace-stylesheets-body.html'); + const out = inline(strip(html, true), css, { + strategy: 'body', + replaceStylesheets: ['/css/replaced.css'], + }); + + expect(strip(out.toString())).toBe(strip(expected)); +}); + +test('Replace stylesheets (minified, media)', async () => { + const html = await read('fixtures/replace-stylesheets.html'); + const css = await read('fixtures/css/simple.css'); + + const expected = await read('expected/replace-stylesheets-media.html'); + const out = inline(strip(html, true), css, { + strategy: 'media', + replaceStylesheets: ['/css/replaced.css'], + }); + + console.log('DEBUG:', strip(html, true)); + expect(strip(out.toString())).toBe(strip(expected)); +}); + +test('Replace stylesheets (minified, swap)', async () => { + const html = await read('fixtures/replace-stylesheets.html'); + const css = await read('fixtures/css/simple.css'); + + const expected = await read('expected/replace-stylesheets-swap.html'); + const out = inline(strip(html, true), css, { + strategy: 'swap', + replaceStylesheets: ['/css/replaced.css'], + }); + + expect(strip(out.toString())).toBe(strip(expected)); +}); + test('Issue 300', async () => { const html = await read('fixtures/issue-300.html'); const css = await read('fixtures/critical.css');