Skip to content

Commit 2b93b55

Browse files
authored
Merge pull request tailwindlabs#209 from tailwindcss/error-on-media-apply
[0.2] Be more strict about which classes can be `@apply`'d
2 parents 2fe2a38 + 447bc87 commit 2b93b55

File tree

3 files changed

+85
-51
lines changed

3 files changed

+85
-51
lines changed

__tests__/applyAtRule.test.js

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,39 +14,64 @@ test("it copies a class's declarations into itself", () => {
1414
})
1515
})
1616

17-
test("it doesn't copy a media query definition into itself", () => {
18-
const output = `.a {
19-
color: red;
20-
}
21-
22-
@media (min-width: 300px) {
23-
.a { color: blue; }
24-
}
25-
26-
.b {
27-
color: red;
28-
}`
29-
30-
return run(
31-
`.a {
32-
color: red;
33-
}
34-
35-
@media (min-width: 300px) {
36-
.a { color: blue; }
37-
}
38-
39-
.b {
40-
@apply .a;
41-
}`
42-
).then(result => {
43-
expect(result.css).toEqual(output)
44-
expect(result.warnings().length).toBe(0)
17+
test('it fails if the class does not exist', () => {
18+
return run('.b { @apply .a; }').catch(e => {
19+
expect(e).toMatchObject({ name: 'CssSyntaxError' })
4520
})
4621
})
4722

48-
test('it fails if the class does not exist', () => {
49-
run('.b { @apply .a; }').catch(error => {
50-
expect(error.reason).toEqual('No .a class found.')
23+
test('applying classes that are ever used in a media query is not supported', () => {
24+
const input = `
25+
.a {
26+
color: red;
27+
}
28+
29+
@media (min-width: 300px) {
30+
.a { color: blue; }
31+
}
32+
33+
.b {
34+
@apply .a;
35+
}
36+
`
37+
expect.assertions(1)
38+
return run(input).catch(e => {
39+
expect(e).toMatchObject({ name: 'CssSyntaxError' })
40+
})
41+
})
42+
43+
test('it does not match classes that include pseudo-selectors', () => {
44+
const input = `
45+
.a:hover {
46+
color: red;
47+
}
48+
49+
.b {
50+
@apply .a;
51+
}
52+
`
53+
expect.assertions(1)
54+
return run(input).catch(e => {
55+
expect(e).toMatchObject({ name: 'CssSyntaxError' })
56+
})
57+
})
58+
59+
test('it does not match classes that have multiple rules', () => {
60+
const input = `
61+
.a {
62+
color: red;
63+
}
64+
65+
.b {
66+
@apply .a;
67+
}
68+
69+
.a {
70+
color: blue;
71+
}
72+
`
73+
expect.assertions(1)
74+
return run(input).catch(e => {
75+
expect(e).toMatchObject({ name: 'CssSyntaxError' })
5176
})
5277
})

src/lib/substituteClassApplyAtRules.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import _ from 'lodash'
22
import postcss from 'postcss'
3-
import findMixin from '../util/findMixin'
43
import escapeClassName from '../util/escapeClassName'
54

65
function normalizeClassNames(classNames) {
@@ -9,6 +8,33 @@ function normalizeClassNames(classNames) {
98
})
109
}
1110

11+
function findMixin(css, mixin, onError) {
12+
const matches = []
13+
14+
css.walkRules(rule => {
15+
if (rule.selectors.includes(mixin)) {
16+
if (rule.parent.type !== 'root') {
17+
// prettier-ignore
18+
onError(`\`@apply\` cannot be used with ${mixin} because ${mixin} is nested inside of an at-rule (@${rule.parent.name}).`)
19+
}
20+
21+
matches.push(rule)
22+
}
23+
})
24+
25+
if (_.isEmpty(matches)) {
26+
// prettier-ignore
27+
onError(`\`@apply\` cannot be used with ${mixin} because ${mixin} either does not exist, or it's actual definition includes a pseudo-class like :hover, :active, etc.`)
28+
}
29+
30+
if (matches.length > 1) {
31+
// prettier-ignore
32+
onError(`\`@apply\` cannot be used with ${mixin} because ${mixin} is included in multiple rulesets.`)
33+
}
34+
35+
return _.flatten(matches.map(match => match.clone().nodes))
36+
}
37+
1238
export default function() {
1339
return function(css) {
1440
css.walkRules(rule => {
@@ -27,8 +53,8 @@ export default function() {
2753
})
2854

2955
const decls = _.flatMap(classes, mixin => {
30-
return findMixin(css, mixin, () => {
31-
throw atRule.error(`No ${mixin} class found.`)
56+
return findMixin(css, mixin, message => {
57+
throw atRule.error(message)
3258
})
3359
})
3460

src/util/findMixin.js

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)