Skip to content

Fix incorrect cascade layer order when some resources can not be inlined #574

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

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 39 additions & 6 deletions lib/apply-conditions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,38 @@
const base64EncodedConditionalImport = require("./base64-encoded-import")

module.exports = function applyConditions(bundle, atRule) {
bundle.forEach(stmt => {
const firstImportStatementIndex = bundle.findIndex(
stmt => stmt.type === "import",
)
const lastImportStatementIndex = bundle.findLastIndex(
stmt => stmt.type === "import",
)

bundle.forEach((stmt, index) => {
if (stmt.type === "charset" || stmt.type === "warning") {
return
}

if (
stmt.type === "charset" ||
stmt.type === "warning" ||
!stmt.conditions?.length
stmt.type === "layer" &&
((index < lastImportStatementIndex && stmt.conditions?.length) ||
(index > firstImportStatementIndex && index < lastImportStatementIndex))
) {
stmt.type = "import"
stmt.node = stmt.node.clone({
name: "import",
params: base64EncodedConditionalImport(
`'data:text/css;base64,${Buffer.from(stmt.node.toString()).toString(
"base64",
)}'`,
stmt.conditions,
),
})

return
}

if (!stmt.conditions?.length) {
return
}

Expand All @@ -20,8 +46,15 @@ module.exports = function applyConditions(bundle, atRule) {
return
}

const { nodes } = stmt
const { parent } = nodes[0]
let nodes
let parent
if (stmt.type === "layer") {
nodes = [stmt.node]
parent = stmt.node.parent
} else {
nodes = stmt.nodes
parent = nodes[0].parent
}

const atRules = []

Expand Down
2 changes: 1 addition & 1 deletion lib/apply-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = function applyStyles(bundle, styles) {

// Strip additional statements.
bundle.forEach(stmt => {
if (["charset", "import"].includes(stmt.type)) {
if (["charset", "import", "layer"].includes(stmt.type)) {
stmt.node.parent = undefined
styles.append(stmt.node)
} else if (stmt.type === "nodes") {
Expand Down
2 changes: 2 additions & 0 deletions lib/base64-encoded-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const formatImportPrelude = require("./format-import-prelude")
// 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) {
if (!conditions?.length) return prelude

conditions.reverse()
const first = conditions.pop()
let params = `${prelude} ${formatImportPrelude(
Expand Down
18 changes: 18 additions & 0 deletions lib/parse-statements.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const { stringify } = valueParser
module.exports = function parseStatements(result, styles, conditions, from) {
const statements = []
let nodes = []
let encounteredNonImportNodes = false

styles.each(node => {
let stmt
Expand All @@ -17,6 +18,14 @@ module.exports = function parseStatements(result, styles, conditions, from) {
stmt = parseImport(result, node, conditions, from)
else if (node.name === "charset")
stmt = parseCharset(result, node, conditions, from)
else if (
node.name === "layer" &&
!encounteredNonImportNodes &&
!node.nodes
)
stmt = parseLayer(result, node, conditions, from)
} else if (node.type !== "comment") {
encounteredNonImportNodes = true
}

if (stmt) {
Expand Down Expand Up @@ -233,3 +242,12 @@ function parseImport(result, atRule, conditions, from) {

return stmt
}

function parseLayer(result, atRule, conditions, from) {
return {
type: "layer",
node: atRule,
conditions: [...conditions],
from,
}
}
13 changes: 9 additions & 4 deletions lib/parse-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async function parseStyles(
}

let charset
const imports = []
const beforeBundle = []
const bundle = []

function handleCharset(stmt) {
Expand All @@ -56,19 +56,24 @@ async function parseStyles(
else if (stmt.type === "import") {
if (stmt.children) {
stmt.children.forEach((child, index) => {
if (child.type === "import") imports.push(child)
if (child.type === "import") beforeBundle.push(child)
else if (child.type === "layer") beforeBundle.push(child)
else if (child.type === "charset") handleCharset(child)
else bundle.push(child)
// For better output
if (index === 0) child.parent = stmt
})
} else imports.push(stmt)
} else beforeBundle.push(stmt)
} else if (stmt.type === "layer") {
beforeBundle.push(stmt)
} else if (stmt.type === "nodes") {
bundle.push(stmt)
}
})

return charset ? [charset, ...imports.concat(bundle)] : imports.concat(bundle)
return charset
? [charset, ...beforeBundle.concat(bundle)]
: beforeBundle.concat(bundle)
}

async function resolveImportId(result, stmt, options, state, postcss) {
Expand Down
4 changes: 4 additions & 0 deletions test/fixtures/imports/layer-followed-by-ignore.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/* a comment */

@layer layer-alpha;
@import "http://css";
1 change: 1 addition & 0 deletions test/fixtures/imports/layer-only.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@layer layer-beta;
2 changes: 2 additions & 0 deletions test/fixtures/layer-followed-by-ignore-with-conditions.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@import "layer-only.css" print;
@import "layer-followed-by-ignore.css" screen;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import 'data:text/css;base64,QGxheWVyIGxheWVyLWJldGE=' print;
@import 'data:text/css;base64,QGxheWVyIGxheWVyLWFscGhh' screen;
@import "http://css" screen;
@media screen{
/* a comment */
}
3 changes: 3 additions & 0 deletions test/fixtures/layer-followed-by-ignore-without-conditions.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import "http://css-a";
@import url("layer-only.css");
@import "http://css-b";
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import "http://css-a";
@import 'data:text/css;base64,QGxheWVyIGxheWVyLWJldGE=';
@import "http://css-b";
2 changes: 2 additions & 0 deletions test/fixtures/layer-followed-by-ignore.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@layer layer-alpha;
@import "http://css";
2 changes: 2 additions & 0 deletions test/fixtures/layer-followed-by-ignore.expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@layer layer-alpha;
@import "http://css";
1 change: 1 addition & 0 deletions test/fixtures/layer-statement-with-conditions.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import "layer-only.css" print;
3 changes: 3 additions & 0 deletions test/fixtures/layer-statement-with-conditions.expected.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@media print{
@layer layer-beta
}
24 changes: 24 additions & 0 deletions test/layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,27 @@ test(
checkFixture,
"layer-duplicate-anonymous-imports-skip",
)

test(
"should correctly handle layer statements followed by ignored imports",
checkFixture,
"layer-followed-by-ignore",
)

test(
"should correctly handle layer statements followed by ignored imports in conditional imports",
checkFixture,
"layer-followed-by-ignore-with-conditions",
)

test(
"should correctly handle layer statements followed by ignored imports in unconditional imports",
checkFixture,
"layer-followed-by-ignore-without-conditions",
)

test(
"should correctly handle layer statements in conditional imports",
checkFixture,
"layer-statement-with-conditions",
)