Skip to content

Commit c2a8d1d

Browse files
authored
Fix: do not crash on @layer {} or other atrules without prelude (#357)
Reported on Twitter by @alexmacarthur https://twitter.com/amacarthur/status/1719715202092982649
1 parent 417665f commit c2a8d1d

File tree

2 files changed

+75
-44
lines changed

2 files changed

+75
-44
lines changed

src/atrules/atrules.test.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ AtRules('finds @layer', () => {
7171
@layer components {
7272
@layer defaults, structures, themes, utilities;
7373
}
74+
75+
/* Anonymous layer (no prelude/layer name) */
76+
@layer {
77+
test {
78+
color: green;
79+
}
80+
}
7481
`
7582
const actual = analyze(fixture).atrules.layer
7683
const expected = {
@@ -241,6 +248,9 @@ AtRules('finds @imports', () => {
241248
@import url('../example.css') layer;
242249
243250
@import url('remedy.css') layer(reset.remedy);
251+
252+
/* @import without prelude */
253+
@import;
244254
`
245255
const actual = analyze(fixture).atrules.import
246256
const expected = {
@@ -265,6 +275,9 @@ AtRules('finds @charsets', () => {
265275
const fixture = `
266276
@charset "UTF-8";
267277
@charset "UTF-16";
278+
279+
/* No prelude */
280+
@charset;
268281
`
269282
const actual = analyze(fixture).atrules.charset
270283
const expected = {
@@ -289,6 +302,9 @@ AtRules('finds @supports', () => {
289302
@media (min-width: 0) {
290303
@supports (-webkit-appearance: none) {}
291304
}
305+
306+
/* No prelude */
307+
@supports {}
292308
`
293309
const actual = analyze(fixture).atrules.supports
294310

@@ -353,6 +369,9 @@ AtRules('finds @media', () => {
353369
@supports (-webkit-appearance: none) {
354370
@media (min-width: 0) {}
355371
}
372+
373+
/* No prelude */
374+
@media {}
356375
`
357376
const actual = analyze(fixture).atrules.media
358377

@@ -427,6 +446,9 @@ AtRules('analyzes @keyframes', () => {
427446
@keyframes one {}
428447
@keyframes TWO {}
429448
449+
/* No prelude */
450+
@keyframes {}
451+
430452
/* Prefixes */
431453
@-webkit-keyframes animation {}
432454
@-moz-keyframes animation {}
@@ -523,6 +545,9 @@ AtRules('analyzes container queries', () => {
523545
/* only applies when an inline-size container is available */
524546
h2 { font-size: calc(1.2em + 1cqi); }
525547
}
548+
549+
/* No prelude */
550+
@container {}
526551
`
527552
const result = analyze(fixture)
528553
const actual = result.atrules.container
@@ -590,6 +615,9 @@ AtRules('analyzes @property', () => {
590615
}
591616
}
592617
}
618+
619+
/* No prelude */
620+
@property {}
593621
`
594622
const actual = analyze(fixture).atrules.property
595623
const expected = {

src/index.js

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -178,53 +178,56 @@ export function analyze(css, options = {}) {
178178
break
179179
}
180180

181-
if (atRuleName === 'media') {
182-
let prelude = stringifyNode(node.prelude)
183-
medias.push(prelude, node.prelude.loc)
184-
if (isMediaBrowserhack(node.prelude)) {
185-
mediaBrowserhacks.push(prelude, node.prelude.loc)
181+
// All the AtRules in here MUST have a prelude, we we can count their names
182+
if (node.prelude !== null) {
183+
if (atRuleName === 'media') {
184+
let prelude = stringifyNode(node.prelude)
185+
medias.push(prelude, node.prelude.loc)
186+
if (isMediaBrowserhack(node.prelude)) {
187+
mediaBrowserhacks.push(prelude, node.prelude.loc)
188+
}
189+
break
186190
}
187-
break
188-
}
189-
if (atRuleName === 'supports') {
190-
let prelude = stringifyNode(node.prelude)
191-
supports.push(prelude, node.prelude.loc)
192-
if (isSupportsBrowserhack(node.prelude)) {
193-
supportsBrowserhacks.push(prelude, node.prelude.loc)
191+
if (atRuleName === 'supports') {
192+
let prelude = stringifyNode(node.prelude)
193+
supports.push(prelude, node.prelude.loc)
194+
if (isSupportsBrowserhack(node.prelude)) {
195+
supportsBrowserhacks.push(prelude, node.prelude.loc)
196+
}
197+
break
194198
}
195-
break
196-
}
197-
if (endsWith('keyframes', atRuleName)) {
198-
let name = '@' + atRuleName + ' ' + stringifyNode(node.prelude)
199-
if (hasVendorPrefix(atRuleName)) {
200-
prefixedKeyframes.push(name, node.prelude.loc)
199+
if (endsWith('keyframes', atRuleName)) {
200+
let name = '@' + atRuleName + ' ' + stringifyNode(node.prelude)
201+
if (hasVendorPrefix(atRuleName)) {
202+
prefixedKeyframes.push(name, node.prelude.loc)
203+
}
204+
keyframes.push(name, node.prelude.loc)
205+
break
206+
}
207+
if (atRuleName === 'import') {
208+
imports.push(stringifyNode(node.prelude), node.prelude.loc)
209+
break
210+
}
211+
if (atRuleName === 'charset') {
212+
charsets.push(stringifyNode(node.prelude), node.prelude.loc)
213+
break
214+
}
215+
if (atRuleName === 'container') {
216+
containers.push(stringifyNode(node.prelude), node.prelude.loc)
217+
break
218+
}
219+
if (atRuleName === 'layer') {
220+
let prelude = stringifyNode(node.prelude)
221+
prelude
222+
.split(',')
223+
.forEach(name => layers.push(name.trim(), node.prelude.loc))
224+
break
225+
}
226+
if (atRuleName === 'property') {
227+
let prelude = stringifyNode(node.prelude)
228+
registeredProperties.push(prelude, node.prelude.loc)
229+
break
201230
}
202-
keyframes.push(name, node.prelude.loc)
203-
break
204-
}
205-
if (atRuleName === 'import') {
206-
imports.push(stringifyNode(node.prelude), node.prelude.loc)
207-
break
208-
}
209-
if (atRuleName === 'charset') {
210-
charsets.push(stringifyNode(node.prelude), node.prelude.loc)
211-
break
212-
}
213-
if (atRuleName === 'container') {
214-
containers.push(stringifyNode(node.prelude), node.prelude.loc)
215-
break
216-
}
217-
if (atRuleName === 'layer') {
218-
let prelude = stringifyNode(node.prelude)
219-
prelude
220-
.split(',')
221-
.forEach(name => layers.push(name.trim(), node.prelude.loc))
222-
break
223-
}
224-
if (atRuleName === 'property') {
225-
let prelude = stringifyNode(node.prelude)
226-
registeredProperties.push(prelude, node.prelude.loc)
227-
break
228231
}
229232
break
230233
}

0 commit comments

Comments
 (0)