Repository: postcss/postcss-import
Pull request #558 (2024-02-09 13:48:13)

ensure that media params remain string values
fixes : https://github.com/postcss/postcss-import/issues/557

romainmenke: Thank you for reviewing @RyanZim 🙇 

I've added a separate plugin that always runs after `postcss-import` and checks the types of all `AtRule` AST nodes. This way we get maximum coverage for this aspect as it runs with each test case.

It tests a few more things than just `atrule.params`, just in case.

Patch:
diff --git a/lib/parse-statements.js b/lib/parse-statements.js
index 328db89..788193a 100644
--- a/lib/parse-statements.js
+++ b/lib/parse-statements.js
@@ -6,20 +6,6 @@ const valueParser = require("postcss-value-parser")
 // extended tooling
 const { stringify } = valueParser
 
-function split(params, start) {
-  const list = []
-  const last = params.reduce((item, node, index) => {
-    if (index < start) return ""
-    if (node.type === "div" && node.value === ",") {
-      list.push(item)
-      return ""
-    }
-    return item + stringify(node)
-  }, "")
-  list.push(last)
-  return list
-}
-
 module.exports = function parseStatements(result, styles, conditions, from) {
   const statements = []
   let nodes = []
@@ -223,7 +209,7 @@ function parseImport(result, atRule, conditions, from) {
       continue
     }
 
-    media = split(params, i)
+    media = stringify(params.slice(i))
     break
   }
 


Test patch:
diff --git a/test/fixtures/filter-all.expected.css b/test/fixtures/filter-all.expected.css
index 11e2abd..1cbbdbe 100644
--- a/test/fixtures/filter-all.expected.css
+++ b/test/fixtures/filter-all.expected.css
@@ -7,5 +7,5 @@
 @import url("foobar.css");
 @import url("foobar.css") screen and (min-width: 25em);
 @import url('foobarbaz.css');
-@import url('foobarbaz.css') print,screen and (min-width: 25em);
+@import url('foobarbaz.css') print, screen and (min-width: 25em);
 content{}
diff --git a/test/fixtures/filter-some.expected.css b/test/fixtures/filter-some.expected.css
index 2a90662..aa4dcf6 100644
--- a/test/fixtures/filter-some.expected.css
+++ b/test/fixtures/filter-some.expected.css
@@ -14,7 +14,7 @@ baz{}
 baz{}
 }
 foobarbaz{}
-@media print,screen and (min-width: 25em){
+@media print, screen and (min-width: 25em){
 foobarbaz{}
 }
 content{}
diff --git a/test/fixtures/ignore.expected.css b/test/fixtures/ignore.expected.css
index 78a07a3..0baad26 100644
--- a/test/fixtures/ignore.expected.css
+++ b/test/fixtures/ignore.expected.css
@@ -15,8 +15,8 @@
 @import url(//css);
 @import "http://css" layer;
 @import "http://css" layer(bar);
-@import "http://css" layer screen and (min-width: 25em),print;
-@import "http://css" layer(bar) screen and (min-width: 25em),print;
+@import "http://css" layer screen and (min-width: 25em), print;
+@import "http://css" layer(bar) screen and (min-width: 25em), print;
 @media (min-width: 25em){
 ignore{}
 }
diff --git a/test/fixtures/media-combine.expected.css b/test/fixtures/media-combine.expected.css
index 9cc53dd..a932500 100644
--- a/test/fixtures/media-combine.expected.css
+++ b/test/fixtures/media-combine.expected.css
@@ -7,7 +7,7 @@
 @media and-left-2 and and-right-2 {}
 }
 
-@media or-left-1,or-right-1 {
+@media or-left-1, or-right-1 {
 
 @media one-2 {}
 
diff --git a/test/fixtures/simple.css b/test/fixtures/simple.css
index 3d304f2..2d3ba9e 100755
--- a/test/fixtures/simple.css
+++ b/test/fixtures/simple.css
@@ -7,6 +7,6 @@
 @import url("foobar.css");
 @import url("foobar.css") screen and (min-width: 25em);
 @import url('foobarbaz.css');
-@import url('foobarbaz.css') print, screen and (min-width: 25em);
+@import url('foobarbaz.css') print,screen and (min-width: 25em);
 
 content{}
diff --git a/test/helpers/ast-checker.js b/test/helpers/ast-checker.js
new file mode 100644
index 0000000..2bb78be
--- /dev/null
+++ b/test/helpers/ast-checker.js
@@ -0,0 +1,48 @@
+"use strict"
+
+const astCheckerPlugin = () => {
+  return {
+    postcssPlugin: "ast-checker-plugin",
+    OnceExit(root) {
+      root.walkAtRules(node => {
+        if (typeof node.params !== "string") {
+          throw node.error(
+            `Params must be of type 'string', found '${typeof node.params}' instead`,
+          )
+        }
+
+        if (typeof node.type !== "string") {
+          throw node.error(
+            `Type must be of type 'string', found '${typeof node.type}' instead`,
+          )
+        }
+
+        if (node.type !== "atrule") {
+          throw node.error(
+            `Type must be 'atrule', found '${node.type}' instead`,
+          )
+        }
+
+        if (typeof node.name !== "string") {
+          throw node.error(
+            `Name must be of type 'string', found '${typeof node.name}' instead`,
+          )
+        }
+
+        if (node.nodes && !Array.isArray(node.nodes)) {
+          throw node.error(
+            `Nodes must be of type 'Array' when it is present, found '${typeof node.nodes}' instead`,
+          )
+        }
+
+        if (!("parent" in node)) {
+          throw node.error("AtRule must have a 'parent' property")
+        }
+      })
+    },
+  }
+}
+
+astCheckerPlugin.postcss = true
+
+module.exports = astCheckerPlugin
diff --git a/test/helpers/check-fixture.js b/test/helpers/check-fixture.js
index 849588f..aa8a5d7 100644
--- a/test/helpers/check-fixture.js
+++ b/test/helpers/check-fixture.js
@@ -8,6 +8,7 @@ const postcss = require("postcss")
 
 // plugin
 const atImport = require("../..")
+const astCheckerPlugin = require("./ast-checker")
 
 function read(name, ext) {
   ext = ext || ".css"
@@ -20,7 +21,7 @@ module.exports = function (t, file, opts, postcssOpts, warnings) {
   if (typeof file === "string") file = { name: file, ext: ".css" }
   const { name, ext } = file
 
-  return postcss(atImport(opts))
+  return postcss([atImport(opts), astCheckerPlugin()])
     .process(read(name, ext), postcssOpts || {})
     .then(result => {
       const actual = result.css