Skip to content

Commit 5191ec1

Browse files
Fix usage of special-character prefixes (#8772)
* Fix import * Support arbitrary prefixes * Add test * Update changelog
1 parent c8c4852 commit 5191ec1

File tree

3 files changed

+125
-1
lines changed

3 files changed

+125
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Allows fallback values in plugin API helpers ([#8762](https://github.com/tailwindlabs/tailwindcss/pull/8762))
1313
- Fix usage of postcss.config.js in standalone CLI ([#8769](https://github.com/tailwindlabs/tailwindcss/pull/8769))
14+
- Fix usage of special-character prefixes ([#8772](https://github.com/tailwindlabs/tailwindcss/pull/8772))
1415

1516
## [3.1.4] - 2022-06-21
1617

src/lib/defaultExtractor.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { flagEnabled } from '../featureFlags.js'
1+
import { flagEnabled } from '../featureFlags'
22
import * as regex from './regex'
33

44
export function defaultExtractor(context) {
@@ -22,6 +22,10 @@ export function defaultExtractor(context) {
2222
function* buildRegExps(context) {
2323
let separator = context.tailwindConfig.separator
2424
let variantGroupingEnabled = flagEnabled(context.tailwindConfig, 'variantGrouping')
25+
let prefix =
26+
context.tailwindConfig.prefix !== ''
27+
? regex.optional(regex.pattern([/-?/, regex.escape(context.tailwindConfig.prefix)]))
28+
: ''
2529

2630
let utility = regex.any([
2731
// Arbitrary properties
@@ -88,6 +92,8 @@ function* buildRegExps(context) {
8892
// Important (optional)
8993
/!?/,
9094

95+
prefix,
96+
9197
variantGroupingEnabled
9298
? regex.any([
9399
// Or any of those things but grouped separated by commas

tests/prefix.test.js

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,3 +400,120 @@ it('supports prefixed utilities using arbitrary values', async () => {
400400
}
401401
`)
402402
})
403+
404+
it('supports non-word prefixes (1)', async () => {
405+
let config = {
406+
prefix: '@',
407+
content: [
408+
{
409+
raw: html`
410+
<div class="@underline"></div>
411+
<div class="@bg-black"></div>
412+
<div class="@[color:red]"></div>
413+
<div class="hover:before:@content-['Hovering']"></div>
414+
<div class="my-utility"></div>
415+
<div class="foo"></div>
416+
417+
<!-- these won't be detected -->
418+
<div class="overline"></div>
419+
`,
420+
},
421+
],
422+
corePlugins: { preflight: false },
423+
}
424+
425+
let input = css`
426+
@tailwind utilities;
427+
@layer utilities {
428+
.my-utility {
429+
color: orange;
430+
}
431+
}
432+
.foo {
433+
@apply @text-white;
434+
@apply [background-color:red];
435+
}
436+
`
437+
438+
const result = await run(input, config)
439+
440+
expect(result.css).toMatchFormattedCss(css`
441+
.\@bg-black {
442+
--tw-bg-opacity: 1;
443+
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
444+
}
445+
.\@underline {
446+
text-decoration-line: underline;
447+
}
448+
.my-utility {
449+
color: orange;
450+
}
451+
.foo {
452+
--tw-text-opacity: 1;
453+
color: rgb(255 255 255 / var(--tw-text-opacity));
454+
background-color: red;
455+
}
456+
.hover\:before\:\@content-\[\'Hovering\'\]:hover::before {
457+
--tw-content: 'Hovering';
458+
content: var(--tw-content);
459+
}
460+
`)
461+
})
462+
463+
it('supports non-word prefixes (2)', async () => {
464+
let config = {
465+
prefix: '@]$',
466+
content: [
467+
{
468+
raw: html`
469+
<div class="@]$underline"></div>
470+
<div class="@]$bg-black"></div>
471+
<div class="@]$[color:red]"></div>
472+
<div class="hover:before:@]$content-['Hovering']"></div>
473+
<div class="my-utility"></div>
474+
<div class="foo"></div>
475+
476+
<!-- these won't be detected -->
477+
<div class="overline"></div>
478+
`,
479+
},
480+
],
481+
corePlugins: { preflight: false },
482+
}
483+
484+
let input = css`
485+
@tailwind utilities;
486+
@layer utilities {
487+
.my-utility {
488+
color: orange;
489+
}
490+
}
491+
.foo {
492+
@apply @]$text-white;
493+
@apply [background-color:red];
494+
}
495+
`
496+
497+
const result = await run(input, config)
498+
499+
// TODO: The class `.hover\:before\:\@\]\$content-\[\'Hovering\'\]:hover::before` is not generated
500+
// This happens because of the parenthesis/brace/bracket clipping performed on candidates
501+
502+
expect(result.css).toMatchFormattedCss(css`
503+
.\@\]\$bg-black {
504+
--tw-bg-opacity: 1;
505+
background-color: rgb(0 0 0 / var(--tw-bg-opacity));
506+
}
507+
.\@\]\$underline {
508+
text-decoration-line: underline;
509+
}
510+
.my-utility {
511+
color: orange;
512+
}
513+
.foo {
514+
--tw-text-opacity: 1;
515+
color: rgb(255 255 255 / var(--tw-text-opacity));
516+
background-color: red;
517+
}
518+
`)
519+
})

0 commit comments

Comments
 (0)