From c2a8d1d3ea130312c5a299337a9020260d379a05 Mon Sep 17 00:00:00 2001 From: Bart Veneman <1536852+bartveneman@users.noreply.github.com> Date: Thu, 2 Nov 2023 09:19:54 +0100 Subject: [PATCH] Fix: do not crash on `@layer {}` or other atrules without prelude (#357) Reported on Twitter by @alexmacarthur https://twitter.com/amacarthur/status/1719715202092982649 --- src/atrules/atrules.test.js | 28 ++++++++++++ src/index.js | 91 +++++++++++++++++++------------------ 2 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/atrules/atrules.test.js b/src/atrules/atrules.test.js index a9e6f2a..972aa47 100644 --- a/src/atrules/atrules.test.js +++ b/src/atrules/atrules.test.js @@ -71,6 +71,13 @@ AtRules('finds @layer', () => { @layer components { @layer defaults, structures, themes, utilities; } + + /* Anonymous layer (no prelude/layer name) */ + @layer { + test { + color: green; + } + } ` const actual = analyze(fixture).atrules.layer const expected = { @@ -241,6 +248,9 @@ AtRules('finds @imports', () => { @import url('../example.css') layer; @import url('remedy.css') layer(reset.remedy); + + /* @import without prelude */ + @import; ` const actual = analyze(fixture).atrules.import const expected = { @@ -265,6 +275,9 @@ AtRules('finds @charsets', () => { const fixture = ` @charset "UTF-8"; @charset "UTF-16"; + + /* No prelude */ + @charset; ` const actual = analyze(fixture).atrules.charset const expected = { @@ -289,6 +302,9 @@ AtRules('finds @supports', () => { @media (min-width: 0) { @supports (-webkit-appearance: none) {} } + + /* No prelude */ + @supports {} ` const actual = analyze(fixture).atrules.supports @@ -353,6 +369,9 @@ AtRules('finds @media', () => { @supports (-webkit-appearance: none) { @media (min-width: 0) {} } + + /* No prelude */ + @media {} ` const actual = analyze(fixture).atrules.media @@ -427,6 +446,9 @@ AtRules('analyzes @keyframes', () => { @keyframes one {} @keyframes TWO {} + /* No prelude */ + @keyframes {} + /* Prefixes */ @-webkit-keyframes animation {} @-moz-keyframes animation {} @@ -523,6 +545,9 @@ AtRules('analyzes container queries', () => { /* only applies when an inline-size container is available */ h2 { font-size: calc(1.2em + 1cqi); } } + + /* No prelude */ + @container {} ` const result = analyze(fixture) const actual = result.atrules.container @@ -590,6 +615,9 @@ AtRules('analyzes @property', () => { } } } + + /* No prelude */ + @property {} ` const actual = analyze(fixture).atrules.property const expected = { diff --git a/src/index.js b/src/index.js index d992623..2ac2d5c 100644 --- a/src/index.js +++ b/src/index.js @@ -178,53 +178,56 @@ export function analyze(css, options = {}) { break } - if (atRuleName === 'media') { - let prelude = stringifyNode(node.prelude) - medias.push(prelude, node.prelude.loc) - if (isMediaBrowserhack(node.prelude)) { - mediaBrowserhacks.push(prelude, node.prelude.loc) + // All the AtRules in here MUST have a prelude, we we can count their names + if (node.prelude !== null) { + if (atRuleName === 'media') { + let prelude = stringifyNode(node.prelude) + medias.push(prelude, node.prelude.loc) + if (isMediaBrowserhack(node.prelude)) { + mediaBrowserhacks.push(prelude, node.prelude.loc) + } + break } - break - } - if (atRuleName === 'supports') { - let prelude = stringifyNode(node.prelude) - supports.push(prelude, node.prelude.loc) - if (isSupportsBrowserhack(node.prelude)) { - supportsBrowserhacks.push(prelude, node.prelude.loc) + if (atRuleName === 'supports') { + let prelude = stringifyNode(node.prelude) + supports.push(prelude, node.prelude.loc) + if (isSupportsBrowserhack(node.prelude)) { + supportsBrowserhacks.push(prelude, node.prelude.loc) + } + break } - break - } - if (endsWith('keyframes', atRuleName)) { - let name = '@' + atRuleName + ' ' + stringifyNode(node.prelude) - if (hasVendorPrefix(atRuleName)) { - prefixedKeyframes.push(name, node.prelude.loc) + if (endsWith('keyframes', atRuleName)) { + let name = '@' + atRuleName + ' ' + stringifyNode(node.prelude) + if (hasVendorPrefix(atRuleName)) { + prefixedKeyframes.push(name, node.prelude.loc) + } + keyframes.push(name, node.prelude.loc) + break + } + if (atRuleName === 'import') { + imports.push(stringifyNode(node.prelude), node.prelude.loc) + break + } + if (atRuleName === 'charset') { + charsets.push(stringifyNode(node.prelude), node.prelude.loc) + break + } + if (atRuleName === 'container') { + containers.push(stringifyNode(node.prelude), node.prelude.loc) + break + } + if (atRuleName === 'layer') { + let prelude = stringifyNode(node.prelude) + prelude + .split(',') + .forEach(name => layers.push(name.trim(), node.prelude.loc)) + break + } + if (atRuleName === 'property') { + let prelude = stringifyNode(node.prelude) + registeredProperties.push(prelude, node.prelude.loc) + break } - keyframes.push(name, node.prelude.loc) - break - } - if (atRuleName === 'import') { - imports.push(stringifyNode(node.prelude), node.prelude.loc) - break - } - if (atRuleName === 'charset') { - charsets.push(stringifyNode(node.prelude), node.prelude.loc) - break - } - if (atRuleName === 'container') { - containers.push(stringifyNode(node.prelude), node.prelude.loc) - break - } - if (atRuleName === 'layer') { - let prelude = stringifyNode(node.prelude) - prelude - .split(',') - .forEach(name => layers.push(name.trim(), node.prelude.loc)) - break - } - if (atRuleName === 'property') { - let prelude = stringifyNode(node.prelude) - registeredProperties.push(prelude, node.prelude.loc) - break } break }