Repository: postcss/postcss-import
Pull request #548 (2023-12-09 18:48:03)

implement `supports` conditions
supersedes : https://github.com/postcss/postcss-import/pull/532
fixes : https://github.com/postcss/postcss-import/issues/529

----------

The core issue is how multiple statements are combined.

The previous implementation tried to squash media queries and layer conditions.

- `layer(foo) screen`
- `layer(bar) (min-width: 1000px)`

becomes :
```css
@media screen and (min-width: 1000px) {
  @layer foo.bar {}
}
```

In this example the `foo` layer is only defined when the media is `screen` and has a width of `1000px`. While it should actually be defined even when the media is `screen` and smaller.

- `screen`
- `not print`

becomes :
```css
@media screen and not print {}
```

In this example the media query becomes invalid because `and` can not be used to combine `screen` and `not print`

-------

The implementation for this feature was also fairly complex.

Adding `supports` into this would have made the implementation very unwieldy and it would have increased the chance that users experience bugs.

-------

The correct way to combine is actually much simpler.

- `layer(foo) screen`
- `layer(bar) (min-width: 1000px)`

becomes :
```css
@media screen {
  @layer foo {
    @media (min-width: 1000px) {
      @layer foo.bar {}
    }
  }
}
```

- `screen`
- `not print`

becomes :
```css
@media screen {
  @media not print {}
}
```

--------

By using nested statements we ensure that the order of declaration is respected and that no invalid statements are produced.

At this point it also becomes trivial to add support for `supports` conditions.

--------

This method does break one aspect.

`@import` statements can not be nested syntactically in CSS.
They must appear first at the root of the AST.


valid :
```css
@imports "http://something-external.com" screen;
````

invalid :
```css
@media screen {
  @imports "http://something-external.com";
}
````

To restore support for this we can use nested stylesheets.
`@import` statement urls can be base64 encoded data urls.
This doesn't actually have to be base64 encoded, but encoding makes it less prone to errors.

Without encoding it looks like this :

```css
@import url("data:text/css,@imports url('http://something-external.com') not print;") screen;
```

- import this : `@imports url('http://something-external.com') not print;`
- apply a `screen` media condition

_This technique was first thought of by the maintainer of esbuild while fixing issues brought to light by https://github.com/romainmenke/css-import-tests_


--------------

This implementation change fixes a lot of subtle bugs and as far as I can tell the output is almost always smaller.

It also eliminates some complexity for users.
They no longer need to define things like `nameLayer` for anonymous layers to work correctly. The issue for which we introduced that option no longer exists in this implementation.

So I think this is a pure win :)

romainmenke: Thank you for the review 🙇 

> Just to clarify, base64 encoding is only used for external URL imports, right?

Yes. 
Only when an import can not be inlined and conditions or cascade layers need to be applied.



Patch:
diff --git a/README.md b/README.md
index c7e4081..b010323 100644
--- a/README.md
+++ b/README.md
@@ -202,17 +202,6 @@ This option is only for adding additional directories to default resolver. If
 you provide your own resolver via the `resolve` configuration option above, then
 this value will be ignored.
 
-#### `nameLayer`
-
-Type: `Function`  
-Default: `null`
-
-You can provide a custom naming function for anonymous layers (`@import 'baz.css' layer;`).  
-This function gets `(index, rootFilename)` arguments and should return a unique string.
-
-This option only influences imports without a layer name.  
-Without this option the plugin will warn on anonymous layers.
-
 #### `warnOnEmpty`
 
 Type: `Boolean`  
diff --git a/index.js b/index.js
index 59ed776..b9f9124 100755
--- a/index.js
+++ b/index.js
@@ -3,7 +3,7 @@
 const path = require("path")
 
 // internal tooling
-const applyMedia = require("./lib/apply-media")
+const applyConditions = require("./lib/apply-conditions")
 const applyRaws = require("./lib/apply-raws")
 const applyStyles = require("./lib/apply-styles")
 const loadContent = require("./lib/load-content")
@@ -19,7 +19,6 @@ function AtImport(options) {
     load: loadContent,
     plugins: [],
     addModulesDirectories: [],
-    nameLayer: null,
     warnOnEmpty: true,
     ...options,
   }
@@ -39,12 +38,9 @@ function AtImport(options) {
       const state = {
         importedFiles: {},
         hashFiles: {},
-        rootFilename: null,
-        anonymousLayerCounter: 0,
       }
 
       if (styles.source?.input?.file) {
-        state.rootFilename = styles.source.input.file
         state.importedFiles[styles.source.input.file] = {}
       }
 
@@ -52,10 +48,6 @@ function AtImport(options) {
         throw new Error("plugins option must be an array")
       }
 
-      if (options.nameLayer && typeof options.nameLayer !== "function") {
-        throw new Error("nameLayer option must be a function")
-      }
-
       const bundle = await parseStyles(
         result,
         styles,
@@ -63,12 +55,11 @@ function AtImport(options) {
         state,
         [],
         [],
-        [],
         postcss
       )
 
       applyRaws(bundle)
-      applyMedia(bundle, options, state, atRule)
+      applyConditions(bundle, atRule)
       applyStyles(bundle, styles)
     },
   }
diff --git a/lib/apply-conditions.js b/lib/apply-conditions.js
new file mode 100644
index 0000000..5497cea
--- /dev/null
+++ b/lib/apply-conditions.js
@@ -0,0 +1,85 @@
+"use strict"
+
+const base64EncodedConditionalImport = require("./base64-encoded-import")
+
+module.exports = function applyConditions(bundle, atRule) {
+  bundle.forEach(stmt => {
+    if (
+      stmt.type === "charset" ||
+      stmt.type === "warning" ||
+      !stmt.conditions?.length
+    ) {
+      return
+    }
+
+    if (stmt.type === "import") {
+      stmt.node.params = base64EncodedConditionalImport(
+        stmt.fullUri,
+        stmt.conditions
+      )
+      return
+    }
+
+    const { nodes } = stmt
+    const { parent } = nodes[0]
+
+    const atRules = []
+
+    // Convert conditions to at-rules
+    for (const condition of stmt.conditions) {
+      if (typeof condition.media !== "undefined") {
+        const mediaNode = atRule({
+          name: "media",
+          params: condition.media,
+          source: parent.source,
+        })
+
+        atRules.push(mediaNode)
+      }
+
+      if (typeof condition.supports !== "undefined") {
+        const supportsNode = atRule({
+          name: "supports",
+          params: `(${condition.supports})`,
+          source: parent.source,
+        })
+
+        atRules.push(supportsNode)
+      }
+
+      if (typeof condition.layer !== "undefined") {
+        const layerNode = atRule({
+          name: "layer",
+          params: condition.layer,
+          source: parent.source,
+        })
+
+        atRules.push(layerNode)
+      }
+    }
+
+    // Add nodes to AST
+    const outerAtRule = atRules.shift()
+    const innerAtRule = atRules.reduce((previous, next) => {
+      previous.append(next)
+      return next
+    }, outerAtRule)
+
+    parent.insertBefore(nodes[0], outerAtRule)
+
+    // remove nodes
+    nodes.forEach(node => {
+      node.parent = undefined
+    })
+
+    // better output
+    nodes[0].raws.before = nodes[0].raws.before || "\n"
+
+    // wrap new rules with media query and/or layer at rule
+    innerAtRule.append(nodes)
+
+    stmt.type = "nodes"
+    stmt.nodes = [outerAtRule]
+    delete stmt.node
+  })
+}
diff --git a/lib/apply-media.js b/lib/apply-media.js
deleted file mode 100644
index 08f5f1e..0000000
--- a/lib/apply-media.js
+++ /dev/null
@@ -1,113 +0,0 @@
-"use strict"
-
-const assignLayerNames = require("./assign-layer-names")
-
-module.exports = function applyMedia(bundle, options, state, atRule) {
-  bundle.forEach(stmt => {
-    if ((!stmt.media.length && !stmt.layer.length) || stmt.type === "charset") {
-      return
-    }
-
-    if (stmt.layer.length > 1) {
-      assignLayerNames(stmt.layer, stmt.node, state, options)
-    }
-
-    if (stmt.type === "import") {
-      const parts = [stmt.fullUri]
-
-      const media = stmt.media.join(", ")
-
-      if (stmt.layer.length) {
-        const layerName = stmt.layer.join(".")
-
-        let layerParams = "layer"
-        if (layerName) {
-          layerParams = `layer(${layerName})`
-        }
-
-        parts.push(layerParams)
-      }
-
-      if (media) {
-        parts.push(media)
-      }
-
-      stmt.node.params = parts.join(" ")
-    } else if (stmt.type === "media") {
-      if (stmt.layer.length) {
-        const layerNode = atRule({
-          name: "layer",
-          params: stmt.layer.join("."),
-          source: stmt.node.source,
-        })
-
-        if (stmt.parentMedia?.length) {
-          const mediaNode = atRule({
-            name: "media",
-            params: stmt.parentMedia.join(", "),
-            source: stmt.node.source,
-          })
-
-          mediaNode.append(layerNode)
-          layerNode.append(stmt.node)
-          stmt.node = mediaNode
-        } else {
-          layerNode.append(stmt.node)
-          delete stmt.node
-          stmt.nodes = [layerNode]
-          stmt.type = "nodes"
-        }
-      } else {
-        stmt.node.params = stmt.media.join(", ")
-      }
-    } else {
-      const { nodes } = stmt
-      const { parent } = nodes[0]
-
-      const atRules = []
-
-      if (stmt.media.length) {
-        const mediaNode = atRule({
-          name: "media",
-          params: stmt.media.join(", "),
-          source: parent.source,
-        })
-
-        atRules.push(mediaNode)
-      }
-
-      if (stmt.layer.length) {
-        const layerNode = atRule({
-          name: "layer",
-          params: stmt.layer.join("."),
-          source: parent.source,
-        })
-
-        atRules.push(layerNode)
-      }
-
-      const outerAtRule = atRules.shift()
-      const innerAtRule = atRules.reduce((previous, next) => {
-        previous.append(next)
-        return next
-      }, outerAtRule)
-
-      parent.insertBefore(nodes[0], outerAtRule)
-
-      // remove nodes
-      nodes.forEach(node => {
-        node.parent = undefined
-      })
-
-      // better output
-      nodes[0].raws.before = nodes[0].raws.before || "\n"
-
-      // wrap new rules with media query and/or layer at rule
-      innerAtRule.append(nodes)
-
-      stmt.type = "media"
-      stmt.node = outerAtRule
-      delete stmt.nodes
-    }
-  })
-}
diff --git a/lib/apply-styles.js b/lib/apply-styles.js
index 2ed2c69..ca961b1 100644
--- a/lib/apply-styles.js
+++ b/lib/apply-styles.js
@@ -5,7 +5,7 @@ module.exports = function applyStyles(bundle, styles) {
 
   // Strip additional statements.
   bundle.forEach(stmt => {
-    if (["charset", "import", "media"].includes(stmt.type)) {
+    if (["charset", "import"].includes(stmt.type)) {
       stmt.node.parent = undefined
       styles.append(stmt.node)
     } else if (stmt.type === "nodes") {
diff --git a/lib/assign-layer-names.js b/lib/assign-layer-names.js
deleted file mode 100644
index 5e0bef4..0000000
--- a/lib/assign-layer-names.js
+++ /dev/null
@@ -1,17 +0,0 @@
-"use strict"
-
-module.exports = function assignLayerNames(layer, node, state, options) {
-  layer.forEach((layerPart, i) => {
-    if (layerPart.trim() === "") {
-      if (options.nameLayer) {
-        layer[i] = options
-          .nameLayer(state.anonymousLayerCounter++, state.rootFilename)
-          .toString()
-      } else {
-        throw node.error(
-          `When using anonymous layers in @import you must also set the "nameLayer" plugin option`
-        )
-      }
-    }
-  })
-}
diff --git a/lib/base64-encoded-import.js b/lib/base64-encoded-import.js
new file mode 100644
index 0000000..d34901c
--- /dev/null
+++ b/lib/base64-encoded-import.js
@@ -0,0 +1,30 @@
+"use strict"
+
+const formatImportPrelude = require("./format-import-prelude")
+
+// Base64 encode an import with conditions
+// The order of conditions is important and is interleaved with cascade layer declarations
+// Each group of conditions and cascade layers needs to be interpreted in order
+// To achieve this we create a list of base64 encoded imports, where each import contains a stylesheet with another import.
+// Each import can define a single group of conditions and a single cascade layer.
+module.exports = function base64EncodedConditionalImport(prelude, conditions) {
+  conditions.reverse()
+  const first = conditions.pop()
+  let params = `${prelude} ${formatImportPrelude(
+    first.layer,
+    first.media,
+    first.supports
+  )}`
+
+  for (const condition of conditions) {
+    params = `'data:text/css;base64,${Buffer.from(`@import ${params}`).toString(
+      "base64"
+    )}' ${formatImportPrelude(
+      condition.layer,
+      condition.media,
+      condition.supports
+    )}`
+  }
+
+  return params
+}
diff --git a/lib/format-import-prelude.js b/lib/format-import-prelude.js
new file mode 100644
index 0000000..9003442
--- /dev/null
+++ b/lib/format-import-prelude.js
@@ -0,0 +1,24 @@
+"use strict"
+
+module.exports = function formatImportPrelude(layer, media, supports) {
+  const parts = []
+
+  if (typeof layer !== "undefined") {
+    let layerParams = "layer"
+    if (layer) {
+      layerParams = `layer(${layer})`
+    }
+
+    parts.push(layerParams)
+  }
+
+  if (typeof supports !== "undefined") {
+    parts.push(`supports(${supports})`)
+  }
+
+  if (typeof media !== "undefined") {
+    parts.push(media)
+  }
+
+  return parts.join(" ")
+}
diff --git a/lib/join-layer.js b/lib/join-layer.js
deleted file mode 100644
index 579c5f0..0000000
--- a/lib/join-layer.js
+++ /dev/null
@@ -1,9 +0,0 @@
-"use strict"
-
-module.exports = function joinLayer(parentLayer, childLayer) {
-  if (!parentLayer.length && childLayer.length) return childLayer
-  if (parentLayer.length && !childLayer.length) return parentLayer
-  if (!parentLayer.length && !childLayer.length) return []
-
-  return parentLayer.concat(childLayer)
-}
diff --git a/lib/join-media.js b/lib/join-media.js
deleted file mode 100644
index 04ee827..0000000
--- a/lib/join-media.js
+++ /dev/null
@@ -1,30 +0,0 @@
-"use strict"
-
-const startsWithKeywordRegexp = /^(all|not|only|print|screen)/i
-
-module.exports = function joinMedia(parentMedia, childMedia) {
-  if (!parentMedia.length && childMedia.length) return childMedia
-  if (parentMedia.length && !childMedia.length) return parentMedia
-  if (!parentMedia.length && !childMedia.length) return []
-
-  const media = []
-
-  parentMedia.forEach(parentItem => {
-    const parentItemStartsWithKeyword = startsWithKeywordRegexp.test(parentItem)
-
-    childMedia.forEach(childItem => {
-      const childItemStartsWithKeyword = startsWithKeywordRegexp.test(childItem)
-      if (parentItem !== childItem) {
-        if (childItemStartsWithKeyword && !parentItemStartsWithKeyword) {
-          media.push(`${childItem} and ${parentItem}`)
-        } else {
-          media.push(`${parentItem} and ${childItem}`)
-        }
-      } else {
-        media.push(parentItem)
-      }
-    })
-  })
-
-  return media
-}
diff --git a/lib/parse-statements.js b/lib/parse-statements.js
index 00acfc3..1fb777e 100644
--- a/lib/parse-statements.js
+++ b/lib/parse-statements.js
@@ -20,16 +20,17 @@ function split(params, start) {
   return list
 }
 
-module.exports = function parseStatements(result, styles, from) {
+module.exports = function parseStatements(result, styles, conditions, from) {
   const statements = []
   let nodes = []
 
   styles.each(node => {
     let stmt
     if (node.type === "atrule") {
-      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 (node.name === "import")
+        stmt = parseImport(result, node, conditions, from)
+      else if (node.name === "charset")
+        stmt = parseCharset(result, node, conditions, from)
     }
 
     if (stmt) {
@@ -37,8 +38,7 @@ module.exports = function parseStatements(result, styles, from) {
         statements.push({
           type: "nodes",
           nodes,
-          media: [],
-          layer: [],
+          conditions: [...conditions],
           from,
         })
         nodes = []
@@ -51,8 +51,7 @@ module.exports = function parseStatements(result, styles, from) {
     statements.push({
       type: "nodes",
       nodes,
-      media: [],
-      layer: [],
+      conditions: [...conditions],
       from,
     })
   }
@@ -60,18 +59,7 @@ module.exports = function parseStatements(result, styles, from) {
   return statements
 }
 
-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, from) {
+function parseCharset(result, atRule, conditions, from) {
   if (atRule.prev()) {
     return result.warn("@charset must precede all other statements", {
       node: atRule,
@@ -80,13 +68,12 @@ function parseCharset(result, atRule, from) {
   return {
     type: "charset",
     node: atRule,
-    media: [],
-    layer: [],
+    conditions: [...conditions],
     from,
   }
 }
 
-function parseImport(result, atRule, from) {
+function parseImport(result, atRule, conditions, from) {
   let prev = atRule.prev()
 
   // `@import` statements may follow other `@import` statements.
@@ -138,12 +125,14 @@ function parseImport(result, atRule, from) {
     uri: "",
     fullUri: "",
     node: atRule,
-    media: [],
-    layer: [],
-    supports: [],
+    conditions: [...conditions],
     from,
   }
 
+  let layer
+  let media
+  let supports
+
   for (let i = 0; i < params.length; i++) {
     const node = params[i]
 
@@ -195,13 +184,13 @@ function parseImport(result, atRule, from) {
       (node.type === "word" || node.type === "function") &&
       /^layer$/i.test(node.value)
     ) {
-      if (stmt.layer.length > 0) {
+      if (typeof layer !== "undefined") {
         return result.warn(`Multiple layers in '${atRule.toString()}'`, {
           node: atRule,
         })
       }
 
-      if (stmt.supports.length > 0) {
+      if (typeof supports !== "undefined") {
         return result.warn(
           `layers must be defined before support conditions in '${atRule.toString()}'`,
           {
@@ -211,16 +200,16 @@ function parseImport(result, atRule, from) {
       }
 
       if (node.nodes) {
-        stmt.layer = [stringify(node.nodes)]
+        layer = stringify(node.nodes)
       } else {
-        stmt.layer = [""]
+        layer = ""
       }
 
       continue
     }
 
     if (node.type === "function" && /^supports$/i.test(node.value)) {
-      if (stmt.supports.length > 0) {
+      if (typeof supports !== "undefined") {
         return result.warn(
           `Multiple support conditions in '${atRule.toString()}'`,
           {
@@ -229,12 +218,12 @@ function parseImport(result, atRule, from) {
         )
       }
 
-      stmt.supports = [stringify(node.nodes)]
+      supports = stringify(node.nodes)
 
       continue
     }
 
-    stmt.media = split(params, i)
+    media = split(params, i)
     break
   }
 
@@ -244,13 +233,16 @@ function parseImport(result, atRule, from) {
     })
   }
 
-  if (stmt.supports.length) {
-    return result.warn(
-      `Supports conditions are not implemented at this time.`,
-      {
-        node: atRule,
-      }
-    )
+  if (
+    typeof media !== "undefined" ||
+    typeof layer !== "undefined" ||
+    typeof supports !== "undefined"
+  ) {
+    stmt.conditions.push({
+      layer,
+      media,
+      supports,
+    })
   }
 
   return stmt
diff --git a/lib/parse-styles.js b/lib/parse-styles.js
index 130ad4a..1fad483 100644
--- a/lib/parse-styles.js
+++ b/lib/parse-styles.js
@@ -2,31 +2,24 @@
 
 const path = require("path")
 
-const assignLayerNames = require("./assign-layer-names")
 const dataURL = require("./data-url")
-const joinLayer = require("./join-layer")
-const joinMedia = require("./join-media")
 const parseStatements = require("./parse-statements")
 const processContent = require("./process-content")
 const resolveId = require("./resolve-id")
+const formatImportPrelude = require("./format-import-prelude")
 
 async function parseStyles(
   result,
   styles,
   options,
   state,
-  media,
-  layer,
+  conditions,
   from,
   postcss
 ) {
-  const statements = parseStatements(result, styles, from)
+  const statements = parseStatements(result, styles, conditions, from)
 
   for (const stmt of statements) {
-    stmt.media = joinMedia(media, stmt.media || [])
-    stmt.parentMedia = media
-    stmt.layer = joinLayer(layer, stmt.layer || [])
-
     if (stmt.type !== "import" || !isProcessableURL(stmt.uri)) {
       continue
     }
@@ -70,7 +63,7 @@ async function parseStyles(
           if (index === 0) child.parent = stmt
         })
       } else imports.push(stmt)
-    } else if (stmt.type === "media" || stmt.type === "nodes") {
+    } else if (stmt.type === "nodes") {
       bundle.push(stmt)
     }
   })
@@ -148,13 +141,16 @@ async function loadImportContent(
   postcss
 ) {
   const atRule = stmt.node
-  const { media, layer, from } = stmt
-
-  assignLayerNames(layer, atRule, state, options)
+  const { conditions, from } = stmt
+  const stmtDuplicateCheckKey = conditions
+    .map(condition =>
+      formatImportPrelude(condition.layer, condition.media, condition.supports)
+    )
+    .join(":")
 
   if (options.skipDuplicates) {
     // skip files already imported at the same scope
-    if (state.importedFiles[filename]?.[media]?.[layer]) {
+    if (state.importedFiles[filename]?.[stmtDuplicateCheckKey]) {
       return
     }
 
@@ -162,10 +158,7 @@ async function loadImportContent(
     if (!state.importedFiles[filename]) {
       state.importedFiles[filename] = {}
     }
-    if (!state.importedFiles[filename][media]) {
-      state.importedFiles[filename][media] = {}
-    }
-    state.importedFiles[filename][media][layer] = true
+    state.importedFiles[filename][stmtDuplicateCheckKey] = true
   }
 
   if (from.includes(filename)) {
@@ -180,7 +173,10 @@ async function loadImportContent(
   }
 
   // skip previous imported files not containing @import rules
-  if (state.hashFiles[content]?.[media]?.[layer]) {
+  if (
+    options.skipDuplicates &&
+    state.hashFiles[content]?.[stmtDuplicateCheckKey]
+  ) {
     return
   }
 
@@ -204,10 +200,8 @@ async function loadImportContent(
       if (!state.hashFiles[content]) {
         state.hashFiles[content] = {}
       }
-      if (!state.hashFiles[content][media]) {
-        state.hashFiles[content][media] = {}
-      }
-      state.hashFiles[content][media][layer] = true
+
+      state.hashFiles[content][stmtDuplicateCheckKey] = true
     }
   }
 
@@ -217,8 +211,7 @@ async function loadImportContent(
     styles,
     options,
     state,
-    media,
-    layer,
+    conditions,
     [...from, filename],
     postcss
   )


Test patch:
diff --git a/test/fixtures/cyclical-skip-duplicates.expected.css b/test/fixtures/cyclical-skip-duplicates.expected.css
index 24c2d5e..ab8eac8 100644
--- a/test/fixtures/cyclical-skip-duplicates.expected.css
+++ b/test/fixtures/cyclical-skip-duplicates.expected.css
@@ -8,11 +8,14 @@
   color: blue;
 }
 
-@media screen and all {
+@media screen {
+
+  @media all {
 
 .a {
   color: aquamarine;
 }
+  }
 }
 
 @media screen {
diff --git a/test/fixtures/cyclical.expected.css b/test/fixtures/cyclical.expected.css
index 5779150..819ad6b 100644
--- a/test/fixtures/cyclical.expected.css
+++ b/test/fixtures/cyclical.expected.css
@@ -16,11 +16,14 @@
   color: red;
 }
 
-@media screen and all {
+@media screen {
+
+  @media all {
 
 .a {
   color: aquamarine;
 }
+  }
 }
 
 @media screen {
diff --git a/test/fixtures/filter-all.expected.css b/test/fixtures/filter-all.expected.css
index 1cbbdbe..11e2abd 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-ignore.expected.css b/test/fixtures/filter-ignore.expected.css
index 03093e4..25ec93d 100644
--- a/test/fixtures/filter-ignore.expected.css
+++ b/test/fixtures/filter-ignore.expected.css
@@ -1,5 +1,5 @@
 @import "http://css" (min-width: 25em);
-@import "http://css-screen" screen and (min-width: 25em);
+@import 'data:text/css;base64,QGltcG9ydCAiaHR0cDovL2Nzcy1zY3JlZW4iIChtaW4td2lkdGg6IDI1ZW0p' screen;
 @import "http://css";
 @import "https://css";
 @import 'http://css';
diff --git a/test/fixtures/filter-some.expected.css b/test/fixtures/filter-some.expected.css
index aa4dcf6..2a90662 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 dd97ad6..78a07a3 100644
--- a/test/fixtures/ignore.expected.css
+++ b/test/fixtures/ignore.expected.css
@@ -1,5 +1,5 @@
 @import "http://css" (min-width: 25em);
-@import "http://css-screen" screen and (min-width: 25em);
+@import 'data:text/css;base64,QGltcG9ydCAiaHR0cDovL2Nzcy1zY3JlZW4iIChtaW4td2lkdGg6IDI1ZW0p' screen;
 @import "http://css";
 @import "https://css";
 @import 'http://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/imports/layer-rule-grouping.css b/test/fixtures/imports/layer-rule-grouping.css
deleted file mode 100644
index c3febe5..0000000
--- a/test/fixtures/imports/layer-rule-grouping.css
+++ /dev/null
@@ -1,13 +0,0 @@
-rule-one {}
-
-rule-two {}
-
-@media (min-width: 50rem) {
-  rule-three {}
-
-  rule-four {}
-}
-
-rule-five {}
-
-rule-six {}
diff --git a/test/fixtures/imports/media-join-nested.css b/test/fixtures/imports/media-combine-nested.css
similarity index 100%
rename from test/fixtures/imports/media-join-nested.css
rename to test/fixtures/imports/media-combine-nested.css
diff --git a/test/fixtures/imports/supports-condition-level-2.css b/test/fixtures/imports/supports-condition-level-2.css
new file mode 100644
index 0000000..89dd108
--- /dev/null
+++ b/test/fixtures/imports/supports-condition-level-2.css
@@ -0,0 +1,3 @@
+@import "supports-condition-level-3.css" layer supports(color: green) screen;
+
+@supports (level: 2) {}
diff --git a/test/fixtures/imports/supports-condition-level-3.css b/test/fixtures/imports/supports-condition-level-3.css
new file mode 100644
index 0000000..c6335b4
--- /dev/null
+++ b/test/fixtures/imports/supports-condition-level-3.css
@@ -0,0 +1,1 @@
+@supports (level: 3) {}
diff --git a/test/fixtures/layer-duplicate-anonymous-imports-skip.css b/test/fixtures/layer-duplicate-anonymous-imports-skip.css
new file mode 100644
index 0000000..3c66793
--- /dev/null
+++ b/test/fixtures/layer-duplicate-anonymous-imports-skip.css
@@ -0,0 +1,3 @@
+@import url("foo.css") layer;
+@import url("bar.css") layer;
+@import url("foo.css") layer;
diff --git a/test/fixtures/layer-duplicate-anonymous-imports-skip.expected.css b/test/fixtures/layer-duplicate-anonymous-imports-skip.expected.css
new file mode 100644
index 0000000..0d325ea
--- /dev/null
+++ b/test/fixtures/layer-duplicate-anonymous-imports-skip.expected.css
@@ -0,0 +1,6 @@
+@layer{
+foo{}
+}
+@layer{
+bar{}
+}
diff --git a/test/fixtures/layer-duplicate-anonymous-imports.css b/test/fixtures/layer-duplicate-anonymous-imports.css
new file mode 100644
index 0000000..3c66793
--- /dev/null
+++ b/test/fixtures/layer-duplicate-anonymous-imports.css
@@ -0,0 +1,3 @@
+@import url("foo.css") layer;
+@import url("bar.css") layer;
+@import url("foo.css") layer;
diff --git a/test/fixtures/layer-duplicate-anonymous-imports.expected.css b/test/fixtures/layer-duplicate-anonymous-imports.expected.css
new file mode 100644
index 0000000..210194f
--- /dev/null
+++ b/test/fixtures/layer-duplicate-anonymous-imports.expected.css
@@ -0,0 +1,9 @@
+@layer{
+foo{}
+}
+@layer{
+bar{}
+}
+@layer{
+foo{}
+}
diff --git a/test/fixtures/layer-import-atrules-anonymous.expected.css b/test/fixtures/layer-import-atrules-anonymous.expected.css
index 83111e9..ae0bd67 100644
--- a/test/fixtures/layer-import-atrules-anonymous.expected.css
+++ b/test/fixtures/layer-import-atrules-anonymous.expected.css
@@ -1,12 +1,15 @@
-@import "http://css" layer(import-anon-layer-0.import-anon-layer-1.import-anon-layer-8) screen;
-@import "http://css-bar" layer(import-anon-layer-0.import-anon-layer-1.bar) screen;
-@import "http://css" layer(import-anon-layer-3.import-anon-layer-4.import-anon-layer-9);
-@import "http://css-bar" layer(import-anon-layer-3.import-anon-layer-4.bar);
-@import "http://css" layer(named-layer-1.import-anon-layer-6.import-anon-layer-10);
-@import "http://css-bar" layer(named-layer-1.import-anon-layer-6.bar);
+@import 'data:text/css;base64,QGltcG9ydCAnZGF0YTp0ZXh0L2NzcztiYXNlNjQsUUdsdGNHOXlkQ0FpYUhSMGNEb3ZMMk56Y3lJZ2JHRjVaWElnYzJOeVpXVnUnIGxheWVy' layer;
+@import 'data:text/css;base64,QGltcG9ydCAnZGF0YTp0ZXh0L2NzcztiYXNlNjQsUUdsdGNHOXlkQ0FpYUhSMGNEb3ZMMk56Y3kxaVlYSWlJR3hoZVdWeUlITmpjbVZsYmc9PScgbGF5ZXIoYmFyKQ==' layer;
+@import 'data:text/css;base64,QGltcG9ydCAnZGF0YTp0ZXh0L2NzcztiYXNlNjQsUUdsdGNHOXlkQ0FpYUhSMGNEb3ZMMk56Y3lJZ2JHRjVaWEk9JyBsYXllcg==' layer;
+@import 'data:text/css;base64,QGltcG9ydCAnZGF0YTp0ZXh0L2NzcztiYXNlNjQsUUdsdGNHOXlkQ0FpYUhSMGNEb3ZMMk56Y3kxaVlYSWlJR3hoZVdWeScgbGF5ZXIoYmFyKQ==' layer;
+@import 'data:text/css;base64,QGltcG9ydCAnZGF0YTp0ZXh0L2NzcztiYXNlNjQsUUdsdGNHOXlkQ0FpYUhSMGNEb3ZMMk56Y3lJZ2JHRjVaWElvYm1GdFpXUXRiR0Y1WlhJdE1Taz0nIGxheWVy' layer;
+@import 'data:text/css;base64,QGltcG9ydCAnZGF0YTp0ZXh0L2NzcztiYXNlNjQsUUdsdGNHOXlkQ0FpYUhSMGNEb3ZMMk56Y3kxaVlYSWlJR3hoZVdWeUtHNWhiV1ZrTFd4aGVXVnlMVEVwJyBsYXllcihiYXIp' layer;
 @import "http://css-top-level" layer;
-@media screen and (min-width: 320px){
-@layer import-anon-layer-0.import-anon-layer-1.import-anon-layer-2{
+@media screen{
+@layer{
+@layer{
+@media (min-width: 320px){
+@layer{
 @layer Z {
   body {
     color: cyan;
@@ -14,8 +17,12 @@
 }
 }
 }
+}
+}
+}
 @media screen{
-@layer import-anon-layer-0.import-anon-layer-1{
+@layer{
+@layer{
 
 @layer Y {
   body {
@@ -24,26 +31,19 @@
 }
 }
 }
+}
 @media screen{
-@layer import-anon-layer-0{
+@layer{
 
 body {
   order: 1;
 }
-}
-}
-@media screen{
-@layer import-anon-layer-0{
 
 @media (min-width: 50rem) {
   body {
     order: 2;
   }
 }
-}
-}
-@media screen{
-@layer import-anon-layer-0{
 
 @keyframes RED_TO_BLUE {
   0% {
@@ -68,8 +68,10 @@ body {
 }
 }
 }
+@layer{
+@layer{
 @media (min-width: 320px){
-@layer import-anon-layer-3.import-anon-layer-4.import-anon-layer-5{
+@layer{
 @layer Z {
   body {
     color: cyan;
@@ -77,7 +79,10 @@ body {
 }
 }
 }
-@layer import-anon-layer-3.import-anon-layer-4{
+}
+}
+@layer{
+@layer{
 
 @layer Y {
   body {
@@ -85,21 +90,18 @@ body {
   }
 }
 }
-@layer import-anon-layer-3{
+}
+@layer{
 
 body {
   order: 1;
 }
-}
-@layer import-anon-layer-3{
 
 @media (min-width: 50rem) {
   body {
     order: 2;
   }
 }
-}
-@layer import-anon-layer-3{
 
 @keyframes RED_TO_BLUE {
   0% {
@@ -123,8 +125,10 @@ body {
   }
 }
 }
+@layer named-layer-1{
+@layer{
 @media (min-width: 320px){
-@layer named-layer-1.import-anon-layer-6.import-anon-layer-7{
+@layer{
 @layer Z {
   body {
     color: cyan;
@@ -132,7 +136,10 @@ body {
 }
 }
 }
-@layer named-layer-1.import-anon-layer-6{
+}
+}
+@layer named-layer-1{
+@layer{
 
 @layer Y {
   body {
@@ -140,21 +147,18 @@ body {
   }
 }
 }
+}
 @layer named-layer-1{
 
 body {
   order: 1;
 }
-}
-@layer named-layer-1{
 
 @media (min-width: 50rem) {
   body {
     order: 2;
   }
 }
-}
-@layer named-layer-1{
 
 @keyframes RED_TO_BLUE {
   0% {
diff --git a/test/fixtures/layer-import-atrules.expected.css b/test/fixtures/layer-import-atrules.expected.css
index e4d6c19..636333b 100644
--- a/test/fixtures/layer-import-atrules.expected.css
+++ b/test/fixtures/layer-import-atrules.expected.css
@@ -1,5 +1,8 @@
-@media screen and (min-width: 320px) {
-@layer imported-with-media.level-2.level-3 {
+@media screen {
+@layer imported-with-media {
+@layer level-2 {
+@media (min-width: 320px) {
+@layer level-3 {
 @layer Z {
   body {
     color: cyan;
@@ -7,9 +10,13 @@
 }
 }
 }
+}
+}
+}
 
 @media screen {
-@layer imported-with-media.level-2 {
+@layer imported-with-media {
+@layer level-2 {
 
 @layer Y {
   body {
@@ -18,6 +25,7 @@
 }
 }
 }
+}
 
 @media screen {
 @layer imported-with-media {
@@ -25,22 +33,12 @@
 body {
   order: 1;
 }
-}
-}
-
-@media screen {
-@layer imported-with-media {
 
 @media (min-width: 50rem) {
   body {
     order: 2;
   }
 }
-}
-}
-
-@media screen {
-@layer imported-with-media {
 
 @keyframes RED_TO_BLUE {
   0% {
@@ -66,8 +64,10 @@ body {
 }
 }
 
+@layer imported-without-media {
+@layer level-2 {
 @media (min-width: 320px) {
-@layer imported-without-media.level-2.level-3 {
+@layer level-3 {
 @layer Z {
   body {
     color: cyan;
@@ -75,8 +75,11 @@ body {
 }
 }
 }
+}
+}
 
-@layer imported-without-media.level-2 {
+@layer imported-without-media {
+@layer level-2 {
 
 @layer Y {
   body {
@@ -84,24 +87,19 @@ body {
   }
 }
 }
+}
 
 @layer imported-without-media {
 
 body {
   order: 1;
 }
-}
-
-@layer imported-without-media {
 
 @media (min-width: 50rem) {
   body {
     order: 2;
   }
 }
-}
-
-@layer imported-without-media {
 
 @keyframes RED_TO_BLUE {
   0% {
diff --git a/test/fixtures/layer-rule-grouping.css b/test/fixtures/layer-rule-grouping.css
deleted file mode 100644
index afa489e..0000000
--- a/test/fixtures/layer-rule-grouping.css
+++ /dev/null
@@ -1,6 +0,0 @@
-@import url("layer-rule-grouping.css") screen;
-
-@import url("layer-rule-grouping.css") layer;
-@import url("layer-rule-grouping.css") layer;
-
-@import url("layer-rule-grouping.css") layer(named);
diff --git a/test/fixtures/layer-rule-grouping.expected.css b/test/fixtures/layer-rule-grouping.expected.css
index a4e34d0..9a0608d 100644
--- a/test/fixtures/layer-rule-grouping.expected.css
+++ b/test/fixtures/layer-rule-grouping.expected.css
@@ -3,61 +3,29 @@
 rule-one {}
 
 rule-two {}
-}
-
-@media screen and (min-width: 50rem) {
-  rule-three {}
-
-  rule-four {}
-}
-
-@media screen {
-
-rule-five {}
-
-rule-six {}
-}
-
-@layer import-anon-layer-0 {
-
-rule-one {}
-
-rule-two {}
-}
-
-@layer import-anon-layer-0 {
 
 @media (min-width: 50rem) {
   rule-three {}
 
   rule-four {}
 }
-}
-
-@layer import-anon-layer-0 {
 
 rule-five {}
 
 rule-six {}
 }
 
-@layer import-anon-layer-1 {
+@layer {
 
 rule-one {}
 
 rule-two {}
-}
-
-@layer import-anon-layer-1 {
 
 @media (min-width: 50rem) {
   rule-three {}
 
   rule-four {}
 }
-}
-
-@layer import-anon-layer-1 {
 
 rule-five {}
 
@@ -69,18 +37,12 @@ rule-six {}
 rule-one {}
 
 rule-two {}
-}
-
-@layer named {
 
 @media (min-width: 50rem) {
   rule-three {}
 
   rule-four {}
 }
-}
-
-@layer named {
 
 rule-five {}
 
diff --git a/test/fixtures/media-combine.css b/test/fixtures/media-combine.css
new file mode 100644
index 0000000..2d75713
--- /dev/null
+++ b/test/fixtures/media-combine.css
@@ -0,0 +1,4 @@
+@import "media-combine-nested.css" one-1;
+@import "media-combine-nested.css" or-left-1, or-right-1;
+@import "media-combine-nested.css" and-left-1 and and-right-1;
+@import "media-query-screen.css" screen;
diff --git a/test/fixtures/media-combine.expected.css b/test/fixtures/media-combine.expected.css
new file mode 100644
index 0000000..9cc53dd
--- /dev/null
+++ b/test/fixtures/media-combine.expected.css
@@ -0,0 +1,33 @@
+@media one-1 {
+
+@media one-2 {}
+
+@media or-left-2, or-right-2 {}
+
+@media and-left-2 and and-right-2 {}
+}
+
+@media or-left-1,or-right-1 {
+
+@media one-2 {}
+
+@media or-left-2, or-right-2 {}
+
+@media and-left-2 and and-right-2 {}
+}
+
+@media and-left-1 and and-right-1 {
+
+@media one-2 {}
+
+@media or-left-2, or-right-2 {}
+
+@media and-left-2 and and-right-2 {}
+}
+
+@media screen {
+
+@media screen {
+foo{}
+}
+}
diff --git a/test/fixtures/media-import.expected.css b/test/fixtures/media-import.expected.css
index 48ca240..784d09c 100644
--- a/test/fixtures/media-import.expected.css
+++ b/test/fixtures/media-import.expected.css
@@ -1,5 +1,5 @@
-@import "//" level-1 and level-2 and level-3;
+@import 'data:text/css;base64,QGltcG9ydCAnZGF0YTp0ZXh0L2NzcztiYXNlNjQsUUdsdGNHOXlkQ0FpTHk4aUlHeGxkbVZzTFRFPScgbGV2ZWwtMw==' level-2;
 
-@import "//" level-1 and level-2;
+@import 'data:text/css;base64,QGltcG9ydCAiLy8iIGxldmVsLTE=' level-2;
 
 @import "//" level-1;
diff --git a/test/fixtures/media-join.css b/test/fixtures/media-join.css
deleted file mode 100644
index 28ca38a..0000000
--- a/test/fixtures/media-join.css
+++ /dev/null
@@ -1,4 +0,0 @@
-@import "media-join-nested.css" one-1;
-@import "media-join-nested.css" or-left-1, or-right-1;
-@import "media-join-nested.css" and-left-1 and and-right-1;
-@import "media-query-screen.css" screen;
diff --git a/test/fixtures/media-join.expected.css b/test/fixtures/media-join.expected.css
deleted file mode 100644
index b733b0b..0000000
--- a/test/fixtures/media-join.expected.css
+++ /dev/null
@@ -1,21 +0,0 @@
-@media one-1 and one-2 {}
-
-@media one-1 and or-left-2, one-1 and or-right-2 {}
-
-@media one-1 and and-left-2 and and-right-2 {}
-
-@media or-left-1 and one-2, or-right-1 and one-2 {}
-
-@media or-left-1 and or-left-2, or-left-1 and or-right-2, or-right-1 and or-left-2, or-right-1 and or-right-2 {}
-
-@media or-left-1 and and-left-2 and and-right-2, or-right-1 and and-left-2 and and-right-2 {}
-
-@media and-left-1 and and-right-1 and one-2 {}
-
-@media and-left-1 and and-right-1 and or-left-2, and-left-1 and and-right-1 and or-right-2 {}
-
-@media and-left-1 and and-right-1 and and-left-2 and and-right-2 {}
-
-@media screen {
-foo{}
-}
diff --git a/test/fixtures/media-query.expected.css b/test/fixtures/media-query.expected.css
index 20ec66f..5c0a144 100644
--- a/test/fixtures/media-query.expected.css
+++ b/test/fixtures/media-query.expected.css
@@ -1,5 +1,12 @@
-@media level-1 and level-2 and level-3 {}
+@media level-1 {
+@media level-2 {
+@media level-3 {}
+}
+}
 
-@media level-1 and level-2 {}
+@media level-1 {
+
+@media level-2 {}
+}
 
 @media level-1 {}
diff --git a/test/fixtures/simple.expected.css b/test/fixtures/simple.expected.css
index 97d3d7a..6a9c1ca 100755
--- a/test/fixtures/simple.expected.css
+++ b/test/fixtures/simple.expected.css
@@ -15,7 +15,7 @@ foobar{}
 foobar{}
 }
 foobarbaz{}
-@media print, screen and (min-width: 25em){
+@media print,screen and (min-width: 25em){
 foobarbaz{}
 }
 content{}
diff --git a/test/fixtures/supports-condition-import.css b/test/fixtures/supports-condition-import.css
new file mode 100644
index 0000000..0424001
--- /dev/null
+++ b/test/fixtures/supports-condition-import.css
@@ -0,0 +1,3 @@
+@import "supports-condition-level-2" layer supports(filter: blur(1px)) (width > 100px);
+
+@supports (level: 1) {}
diff --git a/test/fixtures/supports-condition-import.expected.css b/test/fixtures/supports-condition-import.expected.css
new file mode 100644
index 0000000..0f40229
--- /dev/null
+++ b/test/fixtures/supports-condition-import.expected.css
@@ -0,0 +1,24 @@
+@media (width > 100px) {
+    @supports (filter: blur(1px)) {
+        @layer {
+            @media screen {
+                @supports (color: green) {
+                    @layer {
+@supports (level: 3) {}
+                    }
+                }
+            }
+        }
+    }
+}
+
+@media (width > 100px) {
+    @supports (filter: blur(1px)) {
+        @layer {
+
+@supports (level: 2) {}
+        }
+    }
+}
+
+@supports (level: 1) {}
diff --git a/test/layer.js b/test/layer.js
index 3414ff0..81b4ad9 100644
--- a/test/layer.js
+++ b/test/layer.js
@@ -1,13 +1,6 @@
 "use strict"
 // external tooling
 const test = require("ava")
-const postcss = require("postcss")
-
-const crypto = require("crypto")
-const cwd = process.cwd()
-
-// plugin
-const atImport = require("..")
 
 // internal tooling
 const checkFixture = require("./helpers/check-fixture")
@@ -17,66 +10,26 @@ test("should resolve layers of import statements", checkFixture, "layer")
 test(
   "should correctly wrap imported at rules in layers",
   checkFixture,
-  "layer-import-atrules",
-  {
-    nameLayer: hashLayerName,
-  }
+  "layer-import-atrules"
 )
 
 test(
   "should correctly wrap imported at rules in anonymous layers",
   checkFixture,
-  "layer-import-atrules-anonymous",
-  {
-    nameLayer: hashLayerName,
-  }
+  "layer-import-atrules-anonymous"
 )
 
-test("should group rules", checkFixture, "layer-rule-grouping", {
-  nameLayer: hashLayerName,
-})
-
-test("should pass the root file name to the nameLayer function", t => {
-  return postcss()
-    .use(atImport({ path: "test/fixtures/imports", nameLayer: hashLayerName }))
-    .process('@import "foo.css" layer;', { from: "layer.css" })
-    .then(result => {
-      t.is(result.css, `@layer import-anon-layer-52ff1597784c{\nfoo{}\n}`)
-    })
-})
-
-test("should error when value is not a function", t => {
-  return postcss()
-    .use(atImport({ nameLayer: "not a function" }))
-    .process("", { from: undefined })
-    .catch(error =>
-      t.regex(error.message, /nameLayer option must be a function/)
-    )
-})
-
-test("should throw when using anonymous layers without the nameLayer plugin option", t => {
-  return postcss()
-    .use(atImport({ path: "test/fixtures/imports" }))
-    .process('@import "foo.css" layer;', { from: undefined })
-    .catch(err => {
-      t.is(
-        err.message,
-        'postcss-import: <css input>:1:1: When using anonymous layers in @import you must also set the "nameLayer" plugin option'
-      )
-    })
-})
-
-function hashLayerName(index, rootFilename) {
-  if (!rootFilename) {
-    return `import-anon-layer-${index}`
+test(
+  "should correctly handle duplicate anonymous imports",
+  checkFixture,
+  "layer-duplicate-anonymous-imports",
+  {
+    skipDuplicates: false,
   }
+)
 
-  // A stable, deterministic and unique layer name:
-  // - layer index
-  // - relative rootFilename to current working directory
-  return `import-anon-layer-${crypto
-    .createHash("sha256")
-    .update(`${index}-${rootFilename.split(cwd)[1]}`)
-    .digest("hex")
-    .slice(0, 12)}`
-}
+test(
+  "should correctly handle duplicate anonymous imports and skip duplicates is true",
+  checkFixture,
+  "layer-duplicate-anonymous-imports-skip"
+)
diff --git a/test/lint.js b/test/lint.js
index fc6d9bf..3082400 100644
--- a/test/lint.js
+++ b/test/lint.js
@@ -224,24 +224,6 @@ test("should warn on duplicate url's", t => {
     })
 })
 
-test("should warn on unimplemented features", t => {
-  return processor
-    .process(
-      `
-      @import url('foo') supports(foo: bar);
-      `,
-      { from: undefined }
-    )
-    .then(result => {
-      const warnings = result.warnings()
-      t.is(warnings.length, 1)
-      t.is(
-        warnings[0].text,
-        `Supports conditions are not implemented at this time.`
-      )
-    })
-})
-
 test("should warn on multiple layer clauses", t => {
   return processor
     .process(
diff --git a/test/media.js b/test/media.js
index 3c0c030..fce94eb 100644
--- a/test/media.js
+++ b/test/media.js
@@ -25,4 +25,4 @@ test(
   "media-charset"
 )
 
-test("should join correctly media queries", checkFixture, "media-join")
+test("should correctly combine media queries", checkFixture, "media-combine")
diff --git a/test/supports-condition.js b/test/supports-condition.js
new file mode 100644
index 0000000..fc995f7
--- /dev/null
+++ b/test/supports-condition.js
@@ -0,0 +1,12 @@
+"use strict"
+// external tooling
+const test = require("ava")
+
+// internal tooling
+const checkFixture = require("./helpers/check-fixture")
+
+test(
+  "should resolve supports conditions of import statements",
+  checkFixture,
+  "supports-condition-import"
+)