Skip to content

Commit

Permalink
Fix import with layer; add nameLayer option (#496)
Browse files Browse the repository at this point in the history
* fix import with layer

* one more fix

* remove unreachable code

* fix anonymous layers

* fix anonymous layers

* basic implementation

* add warning

* cleanup and more tests

* use the rootFilename
  • Loading branch information
romainmenke committed Aug 30, 2022
1 parent 64867f4 commit 64dd1c2
Show file tree
Hide file tree
Showing 17 changed files with 608 additions and 11 deletions.
11 changes: 11 additions & 0 deletions README.md
Expand Up @@ -190,6 +190,17 @@ 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.

#### Example with some options

```js
Expand Down
76 changes: 69 additions & 7 deletions index.js
Expand Up @@ -19,6 +19,7 @@ function AtImport(options) {
load: loadContent,
plugins: [],
addModulesDirectories: [],
nameLayer: null,
...options,
}

Expand All @@ -37,16 +38,23 @@ function AtImport(options) {
const state = {
importedFiles: {},
hashFiles: {},
rootFilename: null,
anonymousLayerCounter: 0,
}

if (styles.source && styles.source.input && styles.source.input.file) {
state.rootFilename = styles.source.input.file
state.importedFiles[styles.source.input.file] = {}
}

if (options.plugins && !Array.isArray(options.plugins)) {
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")
}

return parseStyles(result, styles, options, state, [], []).then(
bundle => {
applyRaws(bundle)
Expand Down Expand Up @@ -81,7 +89,30 @@ function AtImport(options) {
if (stmt.type === "import") {
stmt.node.params = `${stmt.fullUri} ${stmt.media.join(", ")}`
} else if (stmt.type === "media") {
stmt.node.params = stmt.media.join(", ")
if (stmt.layer.length) {
const layerNode = atRule({
name: "layer",
params: stmt.layer.filter(layer => layer !== "").join("."),
source: stmt.node.source,
})

if (stmt.parentMedia && 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)
stmt.node = layerNode
}
} else {
stmt.node.params = stmt.media.join(", ")
}
} else {
const { nodes } = stmt
const { parent } = nodes[0]
Expand Down Expand Up @@ -170,6 +201,7 @@ function AtImport(options) {
return stmts.reduce((promise, stmt) => {
return promise.then(() => {
stmt.media = joinMedia(media, stmt.media || [])
stmt.parentMedia = media
stmt.layer = joinLayer(layer, stmt.layer || [])

// skip protocol base uri (protocol://url) or protocol-relative
Expand Down Expand Up @@ -283,18 +315,38 @@ function AtImport(options) {
function loadImportContent(result, stmt, filename, options, state) {
const atRule = stmt.node
const { media, layer } = stmt
layer.forEach((layerPart, i) => {
if (layerPart === "") {
if (options.nameLayer) {
layer[i] = options
.nameLayer(state.anonymousLayerCounter++, state.rootFilename)
.toString()
} else {
throw atRule.error(
`When using anonymous layers in @import you must also set the "nameLayer" plugin option`
)
}
}
})

if (options.skipDuplicates) {
// skip files already imported at the same scope
if (
state.importedFiles[filename] &&
state.importedFiles[filename][media]
state.importedFiles[filename][media] &&
state.importedFiles[filename][media][layer]
) {
return
}

// save imported files to skip them next time
if (!state.importedFiles[filename]) state.importedFiles[filename] = {}
state.importedFiles[filename][media] = true
if (!state.importedFiles[filename]) {
state.importedFiles[filename] = {}
}
if (!state.importedFiles[filename][media]) {
state.importedFiles[filename][media] = {}
}
state.importedFiles[filename][media][layer] = true
}

return Promise.resolve(options.load(filename, options)).then(
Expand All @@ -305,8 +357,13 @@ function AtImport(options) {
}

// skip previous imported files not containing @import rules
if (state.hashFiles[content] && state.hashFiles[content][media])
if (
state.hashFiles[content] &&
state.hashFiles[content][media] &&
state.hashFiles[content][media][layer]
) {
return
}

return processContent(
result,
Expand All @@ -324,8 +381,13 @@ function AtImport(options) {
})
if (!hasImport) {
// save hash files to skip them next time
if (!state.hashFiles[content]) state.hashFiles[content] = {}
state.hashFiles[content][media] = true
if (!state.hashFiles[content]) {
state.hashFiles[content] = {}
}
if (!state.hashFiles[content][media]) {
state.hashFiles[content][media] = {}
}
state.hashFiles[content][media][layer] = true
}
}

Expand Down
33 changes: 33 additions & 0 deletions test/fixtures/imports/layer-anonymous.css
@@ -0,0 +1,33 @@
@import url("layer-level-2-anonymous.css") layer;

body {
order: 1;
}

@media (min-width: 50rem) {
body {
order: 2;
}
}

@keyframes RED_TO_BLUE {
0% {
background-color: red;
}
100% {
background-color: blue;
}
}

@supports (display: grid) {
body {
display: grid;
order: 3;
}
}

@layer A {
body {
order: 4;
}
}
7 changes: 7 additions & 0 deletions test/fixtures/imports/layer-level-2-anonymous.css
@@ -0,0 +1,7 @@
@import url("layer-level-3.css") layer (min-width: 320px);

@layer Y {
body {
color: purple;
}
}
7 changes: 7 additions & 0 deletions test/fixtures/imports/layer-level-2.css
@@ -0,0 +1,7 @@
@import url("layer-level-3.css") layer(level-3) (min-width: 320px);

@layer Y {
body {
color: purple;
}
}
5 changes: 5 additions & 0 deletions test/fixtures/imports/layer-level-3.css
@@ -0,0 +1,5 @@
@layer Z {
body {
color: cyan;
}
}
13 changes: 13 additions & 0 deletions test/fixtures/imports/layer-rule-grouping.css
@@ -0,0 +1,13 @@
rule-one {}

rule-two {}

@media (min-width: 50rem) {
rule-three {}

rule-four {}
}

rule-five {}

rule-six {}
33 changes: 33 additions & 0 deletions test/fixtures/imports/layer.css
@@ -0,0 +1,33 @@
@import url("layer-level-2.css") layer(level-2);

body {
order: 1;
}

@media (min-width: 50rem) {
body {
order: 2;
}
}

@keyframes RED_TO_BLUE {
0% {
background-color: red;
}
100% {
background-color: blue;
}
}

@supports (display: grid) {
body {
display: grid;
order: 3;
}
}

@layer A {
body {
order: 4;
}
}
3 changes: 3 additions & 0 deletions test/fixtures/layer-import-atrules-anonymous.css
@@ -0,0 +1,3 @@
@import url("layer-anonymous.css") layer screen;

@import url("layer-anonymous.css") layer;
127 changes: 127 additions & 0 deletions test/fixtures/layer-import-atrules-anonymous.expected.css
@@ -0,0 +1,127 @@
@media screen and (min-width: 320px) {
@layer import-anon-layer-0.import-anon-layer-1.import-anon-layer-2 {
@layer Z {
body {
color: cyan;
}
}
}
}

@media screen {
@layer import-anon-layer-0.import-anon-layer-1 {

@layer Y {
body {
color: purple;
}
}
}
}

@media screen {
@layer import-anon-layer-0 {

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% {
background-color: red;
}
100% {
background-color: blue;
}
}

@supports (display: grid) {
body {
display: grid;
order: 3;
}
}

@layer A {
body {
order: 4;
}
}
}
}

@media (min-width: 320px) {
@layer import-anon-layer-3.import-anon-layer-4.import-anon-layer-5 {
@layer Z {
body {
color: cyan;
}
}
}
}

@layer import-anon-layer-3.import-anon-layer-4 {

@layer Y {
body {
color: purple;
}
}
}

@layer import-anon-layer-3 {

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% {
background-color: red;
}
100% {
background-color: blue;
}
}

@supports (display: grid) {
body {
display: grid;
order: 3;
}
}

@layer A {
body {
order: 4;
}
}
}

0 comments on commit 64dd1c2

Please sign in to comment.