From eddfcd1c246be7a43b9fb7acc66f297fb4fbaef0 Mon Sep 17 00:00:00 2001 From: Romain Menke Date: Sat, 9 Dec 2023 18:38:08 +0100 Subject: [PATCH] simplify --- lib/apply-conditions.js | 49 ++++++-------------------- lib/base64-encoded-import.js | 30 ++++++++++++++++ lib/format-import-prelude.js | 18 ++++------ lib/parse-statements.js | 24 +++++++------ lib/parse-styles.js | 18 ++++++---- test/fixtures/filter-all.expected.css | 2 +- test/fixtures/filter-some.expected.css | 2 +- test/fixtures/ignore.expected.css | 4 +-- test/fixtures/media-join.expected.css | 2 +- test/fixtures/simple.expected.css | 2 +- 10 files changed, 79 insertions(+), 72 deletions(-) create mode 100644 lib/base64-encoded-import.js diff --git a/lib/apply-conditions.js b/lib/apply-conditions.js index a343dfeb..fdebdfa0 100644 --- a/lib/apply-conditions.js +++ b/lib/apply-conditions.js @@ -1,6 +1,6 @@ "use strict" -const formatImportPrelude = require("./format-import-prelude") +const base64EncodedConditionalImport = require("./base64-encoded-import") module.exports = function applyConditions(bundle, atRule) { bundle.forEach(stmt => { @@ -13,34 +13,10 @@ module.exports = function applyConditions(bundle, atRule) { } if (stmt.type === "import") { - if (stmt.conditions.length === 1) { - stmt.node.params = `${stmt.fullUri} ${formatImportPrelude( - stmt.conditions[0].layer, - stmt.conditions[0].media, - stmt.conditions[0].supports - )}` - } else { - const reverseConditions = stmt.conditions.slice().reverse() - const first = reverseConditions.pop() - let params = `${stmt.fullUri} ${formatImportPrelude( - first.layer, - first.media, - first.supports - )}` - - for (const condition of reverseConditions) { - params = `'data:text/css;base64,${Buffer.from( - `@import ${params}` - ).toString("base64")}' ${formatImportPrelude( - condition.layer, - condition.media, - condition.supports - )}` - } - - stmt.node.params = params - } - + stmt.node.params = base64EncodedConditionalImport( + stmt.fullUri, + stmt.conditions + ) return } @@ -58,33 +34,30 @@ module.exports = function applyConditions(bundle, atRule) { // Convert conditions to at-rules for (const condition of stmt.conditions) { - if (condition.media.length > 0) { + if (typeof condition.media !== "undefined") { const mediaNode = atRule({ name: "media", - params: condition.media.join(", "), + params: condition.media, source: parent.source, }) atRules.push(mediaNode) } - if (condition.supports.length > 0) { + if (typeof condition.supports !== "undefined") { const supportsNode = atRule({ name: "supports", - params: - condition.supports.length === 1 - ? `(${condition.supports[0]})` - : condition.supports.map(x => `(${x})`).join(" and "), + params: `(${condition.supports})`, source: parent.source, }) atRules.push(supportsNode) } - if (condition.layer.length > 0) { + if (typeof condition.layer !== "undefined") { const layerNode = atRule({ name: "layer", - params: condition.layer.join("."), + params: condition.layer, source: parent.source, }) diff --git a/lib/base64-encoded-import.js b/lib/base64-encoded-import.js new file mode 100644 index 00000000..d34901cd --- /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 index add8e163..9003442c 100644 --- a/lib/format-import-prelude.js +++ b/lib/format-import-prelude.js @@ -3,25 +3,21 @@ module.exports = function formatImportPrelude(layer, media, supports) { const parts = [] - if (layer.length) { - const layerName = layer.join(".") - + if (typeof layer !== "undefined") { let layerParams = "layer" - if (layerName) { - layerParams = `layer(${layerName})` + if (layer) { + layerParams = `layer(${layer})` } parts.push(layerParams) } - if (supports.length === 1) { - parts.push(`supports(${supports[0]})`) - } else if (supports.length > 0) { - parts.push(`supports(${supports.map(x => `(${x})`).join(" and ")})`) + if (typeof supports !== "undefined") { + parts.push(`supports(${supports})`) } - if (media.length) { - parts.push(media.join(", ")) + if (typeof media !== "undefined") { + parts.push(media) } return parts.join(" ") diff --git a/lib/parse-statements.js b/lib/parse-statements.js index 7e27cbe7..1fb777e7 100644 --- a/lib/parse-statements.js +++ b/lib/parse-statements.js @@ -129,9 +129,9 @@ function parseImport(result, atRule, conditions, from) { from, } - let layer = [] - let media = [] - let supports = [] + let layer + let media + let supports for (let i = 0; i < params.length; i++) { const node = params[i] @@ -184,13 +184,13 @@ function parseImport(result, atRule, conditions, from) { (node.type === "word" || node.type === "function") && /^layer$/i.test(node.value) ) { - if (layer.length > 0) { + if (typeof layer !== "undefined") { return result.warn(`Multiple layers in '${atRule.toString()}'`, { node: atRule, }) } - if (supports.length > 0) { + if (typeof supports !== "undefined") { return result.warn( `layers must be defined before support conditions in '${atRule.toString()}'`, { @@ -200,16 +200,16 @@ function parseImport(result, atRule, conditions, from) { } if (node.nodes) { - layer = [stringify(node.nodes)] + layer = stringify(node.nodes) } else { - layer = [""] + layer = "" } continue } if (node.type === "function" && /^supports$/i.test(node.value)) { - if (supports.length > 0) { + if (typeof supports !== "undefined") { return result.warn( `Multiple support conditions in '${atRule.toString()}'`, { @@ -218,7 +218,7 @@ function parseImport(result, atRule, conditions, from) { ) } - supports = [stringify(node.nodes)] + supports = stringify(node.nodes) continue } @@ -233,7 +233,11 @@ function parseImport(result, atRule, conditions, from) { }) } - if (media.length > 0 || layer.length > 0 || supports.length > 0) { + if ( + typeof media !== "undefined" || + typeof layer !== "undefined" || + typeof supports !== "undefined" + ) { stmt.conditions.push({ layer, media, diff --git a/lib/parse-styles.js b/lib/parse-styles.js index e492c043..aa1a17cd 100644 --- a/lib/parse-styles.js +++ b/lib/parse-styles.js @@ -240,17 +240,21 @@ function isProcessableURL(uri) { } function serializeConditions(conditions) { - let layer = [] - let media = [] - let supports = [] + const layer = [] + const media = new Set() + const supports = new Set() for (const condition of conditions) { - layer = layer.concat(condition.layer) - media = Array.from(new Set(media.concat(condition.media))) - supports = Array.from(new Set(supports.concat(condition.supports))) + layer.push(condition.layer) + media.add(condition.media) + supports.add(condition.supports) } - return JSON.stringify({ layer, media, supports }) + return JSON.stringify({ + layer, + media: Array.from(media), + supports: Array.from(supports), + }) } module.exports = parseStyles diff --git a/test/fixtures/filter-all.expected.css b/test/fixtures/filter-all.expected.css index 1cbbdbe4..11e2abd7 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 aa4dcf69..2a90662b 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 0baad264..78a07a36 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-join.expected.css b/test/fixtures/media-join.expected.css index a9325004..9cc53dda 100644 --- a/test/fixtures/media-join.expected.css +++ b/test/fixtures/media-join.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.expected.css b/test/fixtures/simple.expected.css index 97d3d7a6..6a9c1ca7 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{}