From 4a11a2c1665146057ea3efdf667d301d90f3e8b0 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Thu, 20 Jul 2023 14:19:18 +0200 Subject: [PATCH 1/2] extend and fix data url imports --- index.js | 1 + lib/data-url.js | 20 ++++++++++++++++---- lib/parse-statements.js | 19 ++++++++++++------- lib/parse-styles.js | 18 ++++++++++++++++-- test/fixtures/data-url.css | 4 ++++ test/fixtures/data-url.expected.css | 26 ++++---------------------- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/index.js b/index.js index cc1237da..1871a639 100755 --- a/index.js +++ b/index.js @@ -62,6 +62,7 @@ function AtImport(options) { state, [], [], + "", postcss ) diff --git a/lib/data-url.js b/lib/data-url.js index a59c5fb5..69c4ce94 100644 --- a/lib/data-url.js +++ b/lib/data-url.js @@ -1,14 +1,26 @@ "use strict" -const dataURLRegexp = /^data:text\/css;base64,/i +const anyDataURLRegexp = /^data:text\/css(?:;(base64|plain))?,/i +const base64DataURLRegexp = /^data:text\/css;base64,/i +const plainDataURLRegexp = /^data:text\/css;plain,/i function isValid(url) { - return dataURLRegexp.test(url) + return anyDataURLRegexp.test(url) } function contents(url) { - // "data:text/css;base64,".length === 21 - return Buffer.from(url.slice(21), "base64").toString() + if (base64DataURLRegexp.test(url)) { + // "data:text/css;base64,".length === 21 + return Buffer.from(url.slice(21), "base64").toString() + } + + if (plainDataURLRegexp.test(url)) { + // "data:text/css;plain,".length === 20 + return decodeURIComponent(url.slice(20)) + } + + // "data:text/css,".length === 14 + return decodeURIComponent(url.slice(14)) } module.exports = { diff --git a/lib/parse-statements.js b/lib/parse-statements.js index aa66e5bd..88a90ebc 100644 --- a/lib/parse-statements.js +++ b/lib/parse-statements.js @@ -20,16 +20,16 @@ function split(params, start) { return list } -module.exports = function parseStatements(result, styles) { +module.exports = function parseStatements(result, styles, from) { const statements = [] let nodes = [] styles.each(node => { let stmt if (node.type === "atrule") { - if (node.name === "import") stmt = parseImport(result, node) - else if (node.name === "media") stmt = parseMedia(result, node) - else if (node.name === "charset") stmt = parseCharset(result, node) + if (node.name === "import") stmt = parseImport(result, node, from) + else if (node.name === "media") stmt = parseMedia(result, node, from) + else if (node.name === "charset") stmt = parseCharset(result, node, from) } if (stmt) { @@ -39,6 +39,7 @@ module.exports = function parseStatements(result, styles) { nodes, media: [], layer: [], + from, }) nodes = [] } @@ -52,23 +53,25 @@ module.exports = function parseStatements(result, styles) { nodes, media: [], layer: [], + from, }) } return statements } -function parseMedia(result, atRule) { +function parseMedia(result, atRule, from) { const params = valueParser(atRule.params).nodes return { type: "media", node: atRule, media: split(params, 0), layer: [], + from, } } -function parseCharset(result, atRule) { +function parseCharset(result, atRule, from) { if (atRule.prev()) { return result.warn("@charset must precede all other statements", { node: atRule, @@ -79,10 +82,11 @@ function parseCharset(result, atRule) { node: atRule, media: [], layer: [], + from, } } -function parseImport(result, atRule) { +function parseImport(result, atRule, from) { let prev = atRule.prev() if (prev) { do { @@ -119,6 +123,7 @@ function parseImport(result, atRule) { media: [], layer: [], supports: [], + from, } for (let i = 0; i < params.length; i++) { diff --git a/lib/parse-styles.js b/lib/parse-styles.js index 50b74eba..45f53a2a 100644 --- a/lib/parse-styles.js +++ b/lib/parse-styles.js @@ -17,9 +17,10 @@ async function parseStyles( state, media, layer, + from, postcss ) { - const statements = parseStatements(result, styles) + const statements = parseStatements(result, styles, from) for (const stmt of statements) { stmt.media = joinMedia(media, stmt.media || []) @@ -93,6 +94,10 @@ async function resolveImportId(result, stmt, options, state, postcss) { return } + if (dataURL.isValid(stmt.from)) { + return + } + const atRule = stmt.node let sourceFile if (atRule.source?.input?.file) { @@ -201,7 +206,16 @@ async function loadImportContent( } // recursion: import @import from imported file - return parseStyles(result, styles, options, state, media, layer, postcss) + return parseStyles( + result, + styles, + options, + state, + media, + layer, + filename, + postcss + ) } module.exports = parseStyles diff --git a/test/fixtures/data-url.css b/test/fixtures/data-url.css index 6f1276ef..4b2b4b37 100644 --- a/test/fixtures/data-url.css +++ b/test/fixtures/data-url.css @@ -4,3 +4,7 @@ /* Mixed imports: */ @import url(data:text/css;base64,QGltcG9ydCB1cmwoZm9vLmNzcyk7CgpwIHsKICBjb2xvcjogYmx1ZTsKfQo=); @import url(data-url.css); + +/* url encoded: */ +@import url(data:text/css;plain,bar%20%7B%20color%3A%20green%20%7D); +@import url(data:text/css,bar%20%7B%20color%3A%20pink%20%7D); diff --git a/test/fixtures/data-url.expected.css b/test/fixtures/data-url.expected.css index 207253e4..af160fc5 100644 --- a/test/fixtures/data-url.expected.css +++ b/test/fixtures/data-url.expected.css @@ -1,24 +1,6 @@ -p { color: green; } +@import url(foo.css);p { color: green; }p { color: blue; }@media (min-width: 320px){@layer foo{ +p { color: green; } } }@media (min-width: 320px){@layer foo{ -p { color: blue; } - -@media (min-width: 320px) { - - @layer foo { -p { color: green; } } } - -@media (min-width: 320px) { - - @layer foo { - -p { color: blue; } } } - -/* Mixed imports: */ - -foo{} - -p { +p { color: blue; } } }/* Mixed imports: */p { color: blue; -} - -p { color: pink; } +}p { color: pink; }/* url encoded: */bar { color: green }bar { color: pink } From 48942c749bc5cf5d7b588ef1f975d95aa6ed38df Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Sat, 22 Jul 2023 22:52:50 +0200 Subject: [PATCH 2/2] explain the purpose of the code and couple bits that belong together --- lib/parse-styles.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/parse-styles.js b/lib/parse-styles.js index 45f53a2a..d021dd4d 100644 --- a/lib/parse-styles.js +++ b/lib/parse-styles.js @@ -92,9 +92,10 @@ async function resolveImportId(result, stmt, options, state, postcss) { ) return - } - - if (dataURL.isValid(stmt.from)) { + } else if (dataURL.isValid(stmt.from)) { + // Data urls can't be used a base url to resolve imports. + // When the parent statement has a data url + // and the current statement doesn't have a data url we ignore the statement. return }