diff --git a/plugin-packs/postcss-bundler/CHANGELOG.md b/plugin-packs/postcss-bundler/CHANGELOG.md index 11adc4f80..c3cf3d75d 100644 --- a/plugin-packs/postcss-bundler/CHANGELOG.md +++ b/plugin-packs/postcss-bundler/CHANGELOG.md @@ -1,5 +1,9 @@ # Changes to PostCSS Bundler +### Unreleased (patch) + +- Fix import parsing so that `@import "foo.css" layer()` is considered invalid syntax and emits warnings. + ### 2.0.5 _November 1, 2024_ diff --git a/plugin-packs/postcss-bundler/dist/index.cjs b/plugin-packs/postcss-bundler/dist/index.cjs index e6c12a9d2..61af429bc 100644 --- a/plugin-packs/postcss-bundler/dist/index.cjs +++ b/plugin-packs/postcss-bundler/dist/index.cjs @@ -1 +1 @@ -"use strict";var e=require("@csstools/css-parser-algorithms"),t=require("@csstools/css-tokenizer"),s=require("node:path"),o=require("node:module"),n=require("node:fs/promises"),r=require("@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 i=/^data:text\/css(?:;(?:base64|plain))?,/i,a=/^data:text\/css;base64,/i,c=/^data:text\/css;plain,/i;function isValidDataURL(e){return!!e&&i.test(e)}const p=/^charset$/i,u=/^import$/i,l=/^url$/i,m=/^layer$/i,d=/^supports$/i,f=/^scope$/i;function parseAtImport(s){const o=t.tokenize({css:s});if(2===o.length&&(t.isTokenString(o[0])||t.isTokenURL(o[0]))){let e=o[0][4].value;return e=stripHash(e),!!e&&{uri:e,fullUri:o[0][1]}}const n=e.parseListOfComponentValues(o);let r,i,a,c,p="",u="";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=[],l=[];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){let s=-1,o=-1,n=-1;for(let t=0;t({postcssPlugin:"postcss-bundler",async Once(e,{result:t,atRule:s,postcss:o}){const n=await parseStyles(t,e,null,[],[],o);postProcess(n,s),applyConditions(n,s),applyStyles(n,e)}});creator$1.postcss=!0;const creator=()=>({postcssPlugin:"postcss-bundler",plugins:[creator$1(),r()]});creator.postcss=!0,module.exports=creator; +"use strict";var e=require("@csstools/css-parser-algorithms"),t=require("@csstools/css-tokenizer"),s=require("node:path"),o=require("node:module"),n=require("node:fs/promises"),r=require("@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 i=/^data:text\/css(?:;(?:base64|plain))?,/i,a=/^data:text\/css;base64,/i,c=/^data:text\/css;plain,/i;function isValidDataURL(e){return!!e&&i.test(e)}const p=/^charset$/i,u=/^import$/i,l=/^url$/i,m=/^layer$/i,d=/^supports$/i,f=/^scope$/i;function parseAtImport(s){const o=t.tokenize({css:s});if(2===o.length&&(t.isTokenString(o[0])||t.isTokenURL(o[0]))){let e=o[0][4].value;return e=stripHash(e),!!e&&{uri:e,fullUri:o[0][1]}}const n=e.parseListOfComponentValues(o);let r,i,a,c,p="",u="";for(let s=0;s!e.isWhiteSpaceOrCommentNode(t))))return!1;r=e.stringify([o.value])}else if(e.isFunctionNode(o)&&d.test(o.getName())){if(void 0!==a)return!1;a=e.stringify([o.value])}else{if(!e.isFunctionNode(o)||!f.test(o.getName())){i=e.stringify([n.slice(s)]);break}if(void 0!==c)return!1;c=e.stringify([wrapInParenthesisIfNeeded(o.value)])}}}return p=stripHash(p),!!p&&{uri:p,fullUri:u,layer:r,media:i,supports:a,scope:c}}function wrapInParenthesisIfNeeded(s){for(let o=0;o{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=[],l=[];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){let s=-1,o=-1,n=-1;for(let t=0;t({postcssPlugin:"postcss-bundler",async Once(e,{result:t,atRule:s,postcss:o}){const n=await parseStyles(t,e,null,[],[],o);postProcess(n,s),applyConditions(n,s),applyStyles(n,e)}});creator$1.postcss=!0;const creator=()=>({postcssPlugin:"postcss-bundler",plugins:[creator$1(),r()]});creator.postcss=!0,module.exports=creator; diff --git a/plugin-packs/postcss-bundler/dist/index.mjs b/plugin-packs/postcss-bundler/dist/index.mjs index 9aab9802c..bcb95df66 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 r,isSimpleBlockNode as n,SimpleBlockNode as i}from"@csstools/css-parser-algorithms";import{tokenize as a,isTokenString as c,isTokenURL as p,isTokenIdent as u,isTokenOpenParen as l,TokenType as m}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(n){const i=a({css:n});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 l=e(i);let m,d,f,h,y="",g="";for(let e=0;e{const i=parseStylesheet(e,t,s,o,r);if(n.charset&&i.charset&&n.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 ${n.charset.params} specified in ${n.charset.source?.input.file}`);!n.charset&&i.charset&&(n.charset=i.charset),n.statements.push(...i.statements)})),n;let i,a,c=[],p=[];for(let n=0;n({postcssPlugin:"noop-plugin",Once(){}});async function parseStyles(e,t,s,o,r,n){const i=parseStylesheet(e,t,s,o,r);{let t,s,o;const r=[];for(const a of i.statements)isImportStatement(a)&&isProcessableURL(a.uri)&&(t&&s&&o||([t,s,o]=createRequire(a.node,e),t&&s&&o))&&r.push(resolveImportId(e,a,n,t,s,o));r.length&&await Promise.all(r)}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 r=s.nodes;if(!r.length)return;const n=r[0].parent;if(!n)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??n.source});i.push(o)}if(void 0!==e.scope){const o=t({name:"scope",params:e.scope,source:s.importingNode?.source??n.source});i.push(o)}if(void 0!==e.supports){const o=t({name:"supports",params:"("+e.supports+")",source:s.importingNode?.source??n.source});i.push(o)}if(void 0!==e.layer){const o=t({name:"layer",params:e.layer,source:s.importingNode?.source??n.source});i.push(o)}}const a=i[0];if(!a)return;for(let e=0;e{e.parent=void 0})),c.append(r),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){let s=-1,o=-1,r=-1;for(let t=0;t({postcssPlugin:"postcss-bundler",async Once(e,{result:t,atRule:s,postcss:o}){const r=await parseStyles(t,e,null,[],[],o);postProcess(r,s),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}; +import{parseListOfComponentValues as e,isWhiteSpaceOrCommentNode as t,isTokenNode as s,isFunctionNode as o,stringify as r,isSimpleBlockNode as n,SimpleBlockNode as i}from"@csstools/css-parser-algorithms";import{tokenize as a,isTokenString as c,isTokenURL as p,isTokenIdent as u,isTokenOpenParen as l,TokenType as m}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(n){const i=a({css:n});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 l=e(i);let m,d,f,h,y="",g="";for(let e=0;e!t(e))))return!1;m=r([n.value])}else if(o(n)&&w.test(n.getName())){if(void 0!==f)return!1;f=r([n.value])}else{if(!o(n)||!C.test(n.getName())){d=r([l.slice(e)]);break}if(void 0!==h)return!1;h=r([wrapInParenthesisIfNeeded(n.value)])}}}return y=stripHash(y),!!y&&{uri:y,fullUri:g,layer:m,media:d,supports:f,scope:h}}function wrapInParenthesisIfNeeded(e){for(let s=0;s{const i=parseStylesheet(e,t,s,o,r);if(n.charset&&i.charset&&n.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 ${n.charset.params} specified in ${n.charset.source?.input.file}`);!n.charset&&i.charset&&(n.charset=i.charset),n.statements.push(...i.statements)})),n;let i,a,c=[],p=[];for(let n=0;n({postcssPlugin:"noop-plugin",Once(){}});async function parseStyles(e,t,s,o,r,n){const i=parseStylesheet(e,t,s,o,r);{let t,s,o;const r=[];for(const a of i.statements)isImportStatement(a)&&isProcessableURL(a.uri)&&(t&&s&&o||([t,s,o]=createRequire(a.node,e),t&&s&&o))&&r.push(resolveImportId(e,a,n,t,s,o));r.length&&await Promise.all(r)}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 r=s.nodes;if(!r.length)return;const n=r[0].parent;if(!n)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??n.source});i.push(o)}if(void 0!==e.scope){const o=t({name:"scope",params:e.scope,source:s.importingNode?.source??n.source});i.push(o)}if(void 0!==e.supports){const o=t({name:"supports",params:"("+e.supports+")",source:s.importingNode?.source??n.source});i.push(o)}if(void 0!==e.layer){const o=t({name:"layer",params:e.layer,source:s.importingNode?.source??n.source});i.push(o)}}const a=i[0];if(!a)return;for(let e=0;e{e.parent=void 0})),c.append(r),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){let s=-1,o=-1,r=-1;for(let t=0;t({postcssPlugin:"postcss-bundler",async Once(e,{result:t,atRule:s,postcss:o}){const r=await parseStyles(t,e,null,[],[],o);postProcess(r,s),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}; diff --git a/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-at-import.ts b/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-at-import.ts index cb7acf904..d630e8649 100644 --- a/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-at-import.ts +++ b/plugin-packs/postcss-bundler/src/postcss-import/lib/parse-at-import.ts @@ -111,6 +111,10 @@ export function parseAtImport(params: string): false | { uri: string; fullUri: s return false; } + if (!componentValue.value.some((x) => !isWhiteSpaceOrCommentNode(x))) { + return false; + } + layer = stringify([componentValue.value]); continue; } diff --git a/plugin-packs/postcss-bundler/test/_tape.mjs b/plugin-packs/postcss-bundler/test/_tape.mjs index 7d48dda51..0675113d9 100644 --- a/plugin-packs/postcss-bundler/test/_tape.mjs +++ b/plugin-packs/postcss-bundler/test/_tape.mjs @@ -28,7 +28,7 @@ const testCases = { }, 'ignore': { message: 'ignores incorrect syntax', - warnings: 4, + warnings: 5, }, 'layer-before-external': { message: 'correctly handles layer statements before external resources', diff --git a/plugin-packs/postcss-bundler/test/ignore.css b/plugin-packs/postcss-bundler/test/ignore.css index 149b64750..70497b750 100644 --- a/plugin-packs/postcss-bundler/test/ignore.css +++ b/plugin-packs/postcss-bundler/test/ignore.css @@ -2,3 +2,4 @@ @import supports(foo) "imports/basic.css"; @import url('imports/basic.css' url-mod); @import url('imports/basic.css' 'imports/basic.css'); +@import url('imports/basic.css') layer(); diff --git a/plugins-stylelint/no-invalid-at-import-rules-when-bundling/CHANGELOG.md b/plugins-stylelint/no-invalid-at-import-rules-when-bundling/CHANGELOG.md index bcb1245e6..9e0844594 100644 --- a/plugins-stylelint/no-invalid-at-import-rules-when-bundling/CHANGELOG.md +++ b/plugins-stylelint/no-invalid-at-import-rules-when-bundling/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### Unreleased (patch) + +- Fix import parsing so that `@import "foo.css" layer()` is considered invalid syntax and ignored. + ### 3.0.4 _November 1, 2024_ diff --git a/plugins-stylelint/no-invalid-at-import-rules-when-bundling/parse-at-import.mjs b/plugins-stylelint/no-invalid-at-import-rules-when-bundling/parse-at-import.mjs index dcbef87aa..defddc40b 100644 --- a/plugins-stylelint/no-invalid-at-import-rules-when-bundling/parse-at-import.mjs +++ b/plugins-stylelint/no-invalid-at-import-rules-when-bundling/parse-at-import.mjs @@ -1,4 +1,4 @@ -import { isCommentNode, isFunctionNode, isTokenNode, isWhitespaceNode, parseListOfComponentValues, stringify, sourceIndices } from '@csstools/css-parser-algorithms'; +import { isCommentNode, isFunctionNode, isTokenNode, isWhitespaceNode, parseListOfComponentValues, stringify, sourceIndices, isWhiteSpaceOrCommentNode } from '@csstools/css-parser-algorithms'; import { TokenType, tokenize } from '@csstools/css-tokenizer'; const IS_LAYER = /^layer$/i; @@ -100,6 +100,10 @@ export function parseAtImport(params) { return false; } + if (!componentValue.value.some((x) => !isWhiteSpaceOrCommentNode(x))) { + return false; + } + layer = stringify([componentValue.value]); layerSourceIndices = sourceIndices(componentValue); continue;