Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix import with layer #496

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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;
}
}
}