From 37229dbd9d843450ff84ed9ea48f2de12c150958 Mon Sep 17 00:00:00 2001 From: Ferit Date: Fri, 9 Oct 2026 23:07:19 +0300 Subject: [PATCH 1/4] Recognize case-insensitive import and layer at-rules --- lib/parse-statements.js | 17 +++++++---------- test/at-rule-case.js | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 test/at-rule-case.js diff --git a/lib/parse-statements.js b/lib/parse-statements.js index 5e812fe..6914ca1 100644 --- a/lib/parse-statements.js +++ b/lib/parse-statements.js @@ -14,15 +14,11 @@ module.exports = function parseStatements(result, styles, conditions, from) { styles.each(node => { let stmt if (node.type === "atrule") { - if (node.name === "import") - stmt = parseImport(result, node, conditions, from) - else if (node.name === "charset") + const name = node.name.toLowerCase() + if (name === "import") stmt = parseImport(result, node, conditions, from) + else if (name === "charset") stmt = parseCharset(result, node, conditions, from) - else if ( - node.name === "layer" && - !encounteredNonImportNodes && - !node.nodes - ) + else if (name === "layer" && !encounteredNonImportNodes && !node.nodes) stmt = parseLayer(result, node, conditions, from) } @@ -78,7 +74,7 @@ function parseImport(result, atRule, conditions, from) { do { if ( prev.type === "comment" || - (prev.type === "atrule" && prev.name === "import") + (prev.type === "atrule" && prev.name.toLowerCase() === "import") ) { prev = prev.prev() continue @@ -95,7 +91,8 @@ function parseImport(result, atRule, conditions, from) { if ( prev.type === "comment" || (prev.type === "atrule" && - (prev.name === "charset" || (prev.name === "layer" && !prev.nodes))) + (prev.name.toLowerCase() === "charset" || + (prev.name.toLowerCase() === "layer" && !prev.nodes))) ) { prev = prev.prev() continue diff --git a/test/at-rule-case.js b/test/at-rule-case.js new file mode 100644 index 0000000..1404246 --- /dev/null +++ b/test/at-rule-case.js @@ -0,0 +1,27 @@ +"use strict" + +const path = require("path") +const { default: test } = require("ava") +const postcss = require("postcss") +const atImport = require("..") + +for (const input of [ + '@IMPORT "a.css";', + '@ImPoRt "a.css"; @import "b.css";', + '@LAYER base; @IMPORT "a.css";', +]) { + test(`should recognize case-insensitive at-rule names: ${input}`, async t => { + const result = await postcss([ + atImport({ + resolve: id => path.resolve(id), + load: () => "a { color: red; }", + }), + ]).process(input, { from: undefined }) + + t.is(result.warnings().length, 0) + t.false(/@import/i.test(result.css)) + t.true(result.css.includes("color: red")) + if (input.startsWith("@LAYER")) + t.true(result.css.startsWith("@LAYER base;")) + }) +} From a98dbccc94b031d76604ccbc1e8b599812747e04 Mon Sep 17 00:00:00 2001 From: Ferit Date: Fri, 9 Oct 2026 23:50:49 +0300 Subject: [PATCH 2/4] fix: preserve charset case and reduce at-rule diff --- lib/parse-statements.js | 14 +++++++++----- test/lint.js | 12 ++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/parse-statements.js b/lib/parse-statements.js index 6914ca1..f806a0e 100644 --- a/lib/parse-statements.js +++ b/lib/parse-statements.js @@ -14,11 +14,15 @@ module.exports = function parseStatements(result, styles, conditions, from) { styles.each(node => { let stmt if (node.type === "atrule") { - const name = node.name.toLowerCase() - if (name === "import") stmt = parseImport(result, node, conditions, from) - else if (name === "charset") + if (node.name.toLowerCase() === "import") + stmt = parseImport(result, node, conditions, from) + else if (node.name === "charset") stmt = parseCharset(result, node, conditions, from) - else if (name === "layer" && !encounteredNonImportNodes && !node.nodes) + else if ( + node.name.toLowerCase() === "layer" && + !encounteredNonImportNodes && + !node.nodes + ) stmt = parseLayer(result, node, conditions, from) } @@ -91,7 +95,7 @@ function parseImport(result, atRule, conditions, from) { if ( prev.type === "comment" || (prev.type === "atrule" && - (prev.name.toLowerCase() === "charset" || + (prev.name === "charset" || (prev.name.toLowerCase() === "layer" && !prev.nodes))) ) { prev = prev.prev() diff --git a/test/lint.js b/test/lint.js index ffd3602..43772b5 100644 --- a/test/lint.js +++ b/test/lint.js @@ -8,6 +8,18 @@ const atImport = require("..") const processor = postcss().use(atImport()) +test("uppercase charset remains an ordinary at-rule before import", async t => { + const result = await processor.process( + '@CHARSET "utf-8"; @IMPORT "test/fixtures/imports/foo.css";', + { + from: undefined, + }, + ) + t.is(result.root.first.name, "CHARSET") + t.is(result.warnings().length, 1) + t.true(result.warnings()[0].text.includes("must precede")) +}) + test("should warn when not @charset and not @import statement before", t => { return Promise.all([ processor.process(`a {} @import "";`, { from: undefined }), From 4abb4881fb294e1a2fed5a2b8ce369bec69ab1a8 Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman Date: Mon, 12 Oct 2026 15:30:59 -0400 Subject: [PATCH 3/4] Cleanup tests for consistency Co-authored-by: Ryan Zimmerman --- test/lint.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/lint.js b/test/lint.js index 43772b5..834e695 100644 --- a/test/lint.js +++ b/test/lint.js @@ -8,16 +8,19 @@ const atImport = require("..") const processor = postcss().use(atImport()) -test("uppercase charset remains an ordinary at-rule before import", async t => { +test("should warn if uppercase @CHARSET before @import", async t => { const result = await processor.process( '@CHARSET "utf-8"; @IMPORT "test/fixtures/imports/foo.css";', { from: undefined, }, ) - t.is(result.root.first.name, "CHARSET") - t.is(result.warnings().length, 1) - t.true(result.warnings()[0].text.includes("must precede")) + const warnings = result.warnings() + t.is(warnings.length, 1) + t.true( + warnings[0].text, + "@import statements must precede all other statements (besides @charset or empty @layer) and be consecutive", + ) }) test("should warn when not @charset and not @import statement before", t => { From c77ed0bbc29b8378f581e7ef1943d2c73d8f54fb Mon Sep 17 00:00:00 2001 From: Ryan Zimmerman Date: Mon, 12 Oct 2026 15:36:13 -0400 Subject: [PATCH 4/4] Fix tests --- test/lint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lint.js b/test/lint.js index 834e695..d437dec 100644 --- a/test/lint.js +++ b/test/lint.js @@ -17,7 +17,7 @@ test("should warn if uppercase @CHARSET before @import", async t => { ) const warnings = result.warnings() t.is(warnings.length, 1) - t.true( + t.is( warnings[0].text, "@import statements must precede all other statements (besides @charset or empty @layer) and be consecutive", )