From d25fc8d0b6fa48a2d9addd2805f18a760ea4129b Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Thu, 24 Sep 2026 20:05:56 +0200 Subject: [PATCH] postcss-bundler --- plugin-packs/postcss-bundler/CHANGELOG.md | 1 + plugin-packs/postcss-bundler/dist/index.mjs | 2 +- .../src/postcss-import/lib/parse-stylesheet.ts | 6 ++++++ plugin-packs/postcss-bundler/test/_tape.mjs | 3 +++ .../001-core-features/forwards-compat/009/green.css | 3 +++ .../001-core-features/forwards-compat/009/red.css | 3 +++ .../001-core-features/forwards-compat/009/style.css | 2 ++ .../001-core-features/forwards-compat/009/style.expect.css | 4 ++++ 8 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/green.css create mode 100644 plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/red.css create mode 100644 plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.css create mode 100644 plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.expect.css diff --git a/plugin-packs/postcss-bundler/CHANGELOG.md b/plugin-packs/postcss-bundler/CHANGELOG.md index f0cb724faf..1f19f325e5 100644 --- a/plugin-packs/postcss-bundler/CHANGELOG.md +++ b/plugin-packs/postcss-bundler/CHANGELOG.md @@ -2,6 +2,7 @@ ### Unreleased (patch) +- Ignore `@import {}` blocks - Updated [`@csstools/css-tokenizer`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer) to [`4.0.1`](https://github.com/csstools/postcss-plugins/tree/main/packages/css-tokenizer/CHANGELOG.md#401) (patch) ### 3.0.0 diff --git a/plugin-packs/postcss-bundler/dist/index.mjs b/plugin-packs/postcss-bundler/dist/index.mjs index 2f434aa676..a12f972f3f 100644 --- a/plugin-packs/postcss-bundler/dist/index.mjs +++ b/plugin-packs/postcss-bundler/dist/index.mjs @@ -1 +1 @@ -import{parseListOfComponentValues as e,isWhiteSpaceOrCommentNode as t,isTokenNode as s,isFunctionNode as o,stringify as n,isSimpleBlockNode as r,SimpleBlockNode as i}from"@csstools/css-parser-algorithms";import{tokenize as a,isTokenString as c,isTokenURL as p,isTokenIdent as u,isTokenOpenParen as m,TokenType as l}from"@csstools/css-tokenizer";import d from"node:path";import f from"node:module";import h from"node:fs/promises";import y from"@csstools/postcss-rebase-url";function isWarning(e){return"warning"===e.type}function isNodesStatement(e){return"nodes"===e.type}function isImportStatement(e){return"import"===e.type}function isPreImportStatement(e){return"pre-import"===e.type}const g=/^data:text\/css(?:;(?:base64|plain))?,/i,v=/^data:text\/css;base64,/i,I=/^data:text\/css;plain,/i;function isValidDataURL(e){return!!e&&g.test(e)}const S=/^charset$/i,N=/^import$/i,b=/^url$/i,$=/^layer$/i,w=/^supports$/i,C=/^scope$/i;function parseAtImport(r){const i=a({css:r});if(2===i.length&&(c(i[0])||p(i[0]))){let e=i[0][4].value;return e=stripHash(e),!!e&&{uri:e,fullUri:i[0][1]}}const m=e(i);let l,d,f,h,y="",g="";for(let e=0;e!t(e)))return!1;l=n([r.value])}else if(o(r)&&w.test(r.getName())){if(void 0!==f)return!1;f=n([r.value])}else{if(!o(r)||!C.test(r.getName())){d=n([m.slice(e)]);break}if(void 0!==h)return!1;h=n([wrapInParenthesisIfNeeded(r.value)])}}}return y=stripHash(y),!!y&&{uri:y,fullUri:g,layer:l,media:d,supports:f,scope:h}}function wrapInParenthesisIfNeeded(e){for(let s=0;s{const i=parseStylesheet(e,t,s,o,n);if(r.charset&&i.charset&&r.charset.params.toLowerCase()!==i.charset.params.toLowerCase())throw i.charset.error(`Incompatible @charset statements:\n ${i.charset.params} specified in ${i.charset.source?.input.file}\n ${r.charset.params} specified in ${r.charset.source?.input.file}`);!r.charset&&i.charset&&(r.charset=i.charset),r.statements.push(...i.statements)}),r;let i,a,c=[],p=[];for(let r=0;r({postcssPlugin:"noop-plugin",Once(){}});async function parseStyles(e,t,s,o,n,r){const i=parseStylesheet(e,t,s,o,n);{let t,s,o;const n=[];for(const a of i.statements)isImportStatement(a)&&isProcessableURL(a.uri)&&(t&&s&&o||([t,s,o]=createRequire(a.node,e),t&&s&&o))&&n.push(resolveImportId(e,a,r,t,s,o));n.length&&await Promise.all(n)}for(let e=0;e{if(isWarning(s)||isPreImportStatement(s)||!s.conditions?.length)return;if(isImportStatement(s))return void(s.node.params=base64EncodedConditionalImport(s.fullUri,s.conditions));const n=s.nodes;if(!n.length)return;const r=n[0].parent;if(!r)return;const i=[];for(const e of s.conditions){if(void 0!==e.media){const o=t({name:"media",params:e.media,source:s.importingNode?.source??r.source});i.push(o)}if(void 0!==e.scope){const o=t({name:"scope",params:e.scope,source:s.importingNode?.source??r.source});i.push(o)}if(void 0!==e.supports){const o=t({name:"supports",params:"("+e.supports+")",source:s.importingNode?.source??r.source});i.push(o)}if(void 0!==e.layer){const o=t({name:"layer",params:e.layer,source:s.importingNode?.source??r.source});i.push(o)}}const a=i[0];if(!a)return;for(let e=0;e{e.parent=void 0}),c.append(n),e.statements[o]={type:"nodes",nodes:[a],conditions:s.conditions,from:s.from,importingNode:s.importingNode}})}function applyStyles(e,t){t.nodes=[],e.charset&&(e.charset.parent=void 0,t.append(e.charset)),e.statements.forEach(e=>{isImportStatement(e)?(e.node.parent=void 0,t.append(e.node)):isNodesStatement(e)&&e.nodes.forEach(e=>{e.parent=void 0,t.append(e)})})}function postProcess(e,t,s){let o=-1,n=-1;for(let t=0;t"atrule"===e.type&&!e.nodes))&&!(r"comment"===e.type))&&r{e.parent=void 0,o.append(e)});const n="data:text/css;base64,"+Buffer.from(o.toString()).toString("base64"),a={type:"import",uri:n,fullUri:'"'+n+'"',node:t({name:"import",params:'"'+n+'"',source:i.importingNode?.source??i.nodes[0]?.source}),conditions:i.conditions,from:i.from,importingNode:i.importingNode};e.statements.splice(r,1,a)}}}const creator$1=()=>({postcssPlugin:"postcss-bundler",async Once(e,{result:t,atRule:s,root:o,postcss:n}){const r=await parseStyles(t,e,null,[],[],n);postProcess(r,s,o),applyConditions(r,s),applyStyles(r,e)}});creator$1.postcss=!0;const creator=()=>({postcssPlugin:"postcss-bundler",plugins:[creator$1(),y()]});creator.postcss=!0;export{creator as default,creator as"module.exports"}; +import{parseListOfComponentValues as e,isWhiteSpaceOrCommentNode as t,isTokenNode as s,isFunctionNode as o,stringify as n,isSimpleBlockNode as r,SimpleBlockNode as i}from"@csstools/css-parser-algorithms";import{tokenize as a,isTokenString as c,isTokenURL as p,isTokenIdent as u,isTokenOpenParen as m,TokenType as l}from"@csstools/css-tokenizer";import d from"node:path";import f from"node:module";import h from"node:fs/promises";import y from"@csstools/postcss-rebase-url";function isWarning(e){return"warning"===e.type}function isNodesStatement(e){return"nodes"===e.type}function isImportStatement(e){return"import"===e.type}function isPreImportStatement(e){return"pre-import"===e.type}const g=/^data:text\/css(?:;(?:base64|plain))?,/i,v=/^data:text\/css;base64,/i,I=/^data:text\/css;plain,/i;function isValidDataURL(e){return!!e&&g.test(e)}const S=/^charset$/i,b=/^import$/i,N=/^url$/i,$=/^layer$/i,w=/^supports$/i,C=/^scope$/i;function parseAtImport(r){const i=a({css:r});if(2===i.length&&(c(i[0])||p(i[0]))){let e=i[0][4].value;return e=stripHash(e),!!e&&{uri:e,fullUri:i[0][1]}}const m=e(i);let l,d,f,h,y="",g="";for(let e=0;e!t(e)))return!1;l=n([r.value])}else if(o(r)&&w.test(r.getName())){if(void 0!==f)return!1;f=n([r.value])}else{if(!o(r)||!C.test(r.getName())){d=n([m.slice(e)]);break}if(void 0!==h)return!1;h=n([wrapInParenthesisIfNeeded(r.value)])}}}return y=stripHash(y),!!y&&{uri:y,fullUri:g,layer:l,media:d,supports:f,scope:h}}function wrapInParenthesisIfNeeded(e){for(let s=0;s{const i=parseStylesheet(e,t,s,o,n);if(r.charset&&i.charset&&r.charset.params.toLowerCase()!==i.charset.params.toLowerCase())throw i.charset.error(`Incompatible @charset statements:\n ${i.charset.params} specified in ${i.charset.source?.input.file}\n ${r.charset.params} specified in ${r.charset.source?.input.file}`);!r.charset&&i.charset&&(r.charset=i.charset),r.statements.push(...i.statements)}),r;let i,a,c=[],p=[];for(let r=0;r({postcssPlugin:"noop-plugin",Once(){}});async function parseStyles(e,t,s,o,n,r){const i=parseStylesheet(e,t,s,o,n);{let t,s,o;const n=[];for(const a of i.statements)isImportStatement(a)&&isProcessableURL(a.uri)&&(t&&s&&o||([t,s,o]=createRequire(a.node,e),t&&s&&o))&&n.push(resolveImportId(e,a,r,t,s,o));n.length&&await Promise.all(n)}for(let e=0;e{if(isWarning(s)||isPreImportStatement(s)||!s.conditions?.length)return;if(isImportStatement(s))return void(s.node.params=base64EncodedConditionalImport(s.fullUri,s.conditions));const n=s.nodes;if(!n.length)return;const r=n[0].parent;if(!r)return;const i=[];for(const e of s.conditions){if(void 0!==e.media){const o=t({name:"media",params:e.media,source:s.importingNode?.source??r.source});i.push(o)}if(void 0!==e.scope){const o=t({name:"scope",params:e.scope,source:s.importingNode?.source??r.source});i.push(o)}if(void 0!==e.supports){const o=t({name:"supports",params:"("+e.supports+")",source:s.importingNode?.source??r.source});i.push(o)}if(void 0!==e.layer){const o=t({name:"layer",params:e.layer,source:s.importingNode?.source??r.source});i.push(o)}}const a=i[0];if(!a)return;for(let e=0;e{e.parent=void 0}),c.append(n),e.statements[o]={type:"nodes",nodes:[a],conditions:s.conditions,from:s.from,importingNode:s.importingNode}})}function applyStyles(e,t){t.nodes=[],e.charset&&(e.charset.parent=void 0,t.append(e.charset)),e.statements.forEach(e=>{isImportStatement(e)?(e.node.parent=void 0,t.append(e.node)):isNodesStatement(e)&&e.nodes.forEach(e=>{e.parent=void 0,t.append(e)})})}function postProcess(e,t,s){let o=-1,n=-1;for(let t=0;t"atrule"===e.type&&!e.nodes))&&!(r"comment"===e.type))&&r{e.parent=void 0,o.append(e)});const n="data:text/css;base64,"+Buffer.from(o.toString()).toString("base64"),a={type:"import",uri:n,fullUri:'"'+n+'"',node:t({name:"import",params:'"'+n+'"',source:i.importingNode?.source??i.nodes[0]?.source}),conditions:i.conditions,from:i.from,importingNode:i.importingNode};e.statements.splice(r,1,a)}}}const creator$1=()=>({postcssPlugin:"postcss-bundler",async Once(e,{result:t,atRule:s,root:o,postcss:n}){const r=await parseStyles(t,e,null,[],[],n);postProcess(r,s,o),applyConditions(r,s),applyStyles(r,e)}});creator$1.postcss=!0;const creator=()=>({postcssPlugin:"postcss-bundler",plugins:[creator$1(),y()]});creator.postcss=!0;export{creator as default,creator as"module.exports"}; diff --git a/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-stylesheet.ts b/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-stylesheet.ts index eb70fc4f5a..16c4c43df8 100644 --- a/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-stylesheet.ts +++ b/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-stylesheet.ts @@ -216,6 +216,12 @@ function consumeLayers(nodes: Array, conditions: Array, cu } function parseImport(result: Result, atRule: AtRule, importingNode: AtRule | null, conditions: Array, from: Array): Warning | ImportStatement { + if (atRule.nodes) { + return result.warn(`Invalid @import block in '${atRule.toString()}'`, { + node: atRule, + }); + } + const parsed = parseAtImport(atRule.params); if (!parsed) { return result.warn(`Invalid @import statement in '${atRule.toString()}'`, { diff --git a/plugin-packs/postcss-bundler/test/_tape.mjs b/plugin-packs/postcss-bundler/test/_tape.mjs index 323bccf6c7..e1ad96cd19 100644 --- a/plugin-packs/postcss-bundler/test/_tape.mjs +++ b/plugin-packs/postcss-bundler/test/_tape.mjs @@ -69,6 +69,9 @@ const testCases = { case 'test/css-import-tests/001-core-features/forwards-compat/004': testCases[key].warnings = 1; break; + case 'test/css-import-tests/001-core-features/forwards-compat/009': + testCases[key].warnings = 1; + break; case 'test/css-import-tests/002-sub-features/003-at-layer/013': testCases[key].warnings = 1; break; diff --git a/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/green.css b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/green.css new file mode 100644 index 0000000000..bbd0501544 --- /dev/null +++ b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/green.css @@ -0,0 +1,3 @@ +.box { + background-color: green; +} diff --git a/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/red.css b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/red.css new file mode 100644 index 0000000000..47b0ac01e1 --- /dev/null +++ b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/red.css @@ -0,0 +1,3 @@ +.box { + background-color: red; +} diff --git a/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.css b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.css new file mode 100644 index 0000000000..2656d3cf81 --- /dev/null +++ b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.css @@ -0,0 +1,2 @@ +@import url("green.css"); +@import url("red.css") {} diff --git a/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.expect.css b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.expect.css new file mode 100644 index 0000000000..4d17baf2b9 --- /dev/null +++ b/plugin-packs/postcss-bundler/test/css-import-tests/001-core-features/forwards-compat/009/style.expect.css @@ -0,0 +1,4 @@ +/* green.css */ +.box { + background-color: green; +}