diff --git a/CHANGELOG.md b/CHANGELOG.md index 840d7f7af79f..0390f3674b08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allows fallback values in plugin API helpers ([#8762](https://github.com/tailwindlabs/tailwindcss/pull/8762)) - Fix usage of postcss.config.js in standalone CLI ([#8769](https://github.com/tailwindlabs/tailwindcss/pull/8769)) +- Fix usage of special-character prefixes ([#8772](https://github.com/tailwindlabs/tailwindcss/pull/8772)) ## [3.1.4] - 2022-06-21 diff --git a/src/lib/defaultExtractor.js b/src/lib/defaultExtractor.js index 1d631a48e1a5..3aec2e5629dd 100644 --- a/src/lib/defaultExtractor.js +++ b/src/lib/defaultExtractor.js @@ -1,4 +1,4 @@ -import { flagEnabled } from '../featureFlags.js' +import { flagEnabled } from '../featureFlags' import * as regex from './regex' export function defaultExtractor(context) { @@ -22,6 +22,10 @@ export function defaultExtractor(context) { function* buildRegExps(context) { let separator = context.tailwindConfig.separator let variantGroupingEnabled = flagEnabled(context.tailwindConfig, 'variantGrouping') + let prefix = + context.tailwindConfig.prefix !== '' + ? regex.optional(regex.pattern([/-?/, regex.escape(context.tailwindConfig.prefix)])) + : '' let utility = regex.any([ // Arbitrary properties @@ -88,6 +92,8 @@ function* buildRegExps(context) { // Important (optional) /!?/, + prefix, + variantGroupingEnabled ? regex.any([ // Or any of those things but grouped separated by commas diff --git a/tests/prefix.test.js b/tests/prefix.test.js index c4fd48019887..47d0d672ebd9 100644 --- a/tests/prefix.test.js +++ b/tests/prefix.test.js @@ -400,3 +400,120 @@ it('supports prefixed utilities using arbitrary values', async () => { } `) }) + +it('supports non-word prefixes (1)', async () => { + let config = { + prefix: '@', + content: [ + { + raw: html` +
+
+
+
+
+
+ + +
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .my-utility { + color: orange; + } + } + .foo { + @apply @text-white; + @apply [background-color:red]; + } + ` + + const result = await run(input, config) + + expect(result.css).toMatchFormattedCss(css` + .\@bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + .\@underline { + text-decoration-line: underline; + } + .my-utility { + color: orange; + } + .foo { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + background-color: red; + } + .hover\:before\:\@content-\[\'Hovering\'\]:hover::before { + --tw-content: 'Hovering'; + content: var(--tw-content); + } + `) +}) + +it('supports non-word prefixes (2)', async () => { + let config = { + prefix: '@]$', + content: [ + { + raw: html` +
+
+
+
+
+
+ + +
+ `, + }, + ], + corePlugins: { preflight: false }, + } + + let input = css` + @tailwind utilities; + @layer utilities { + .my-utility { + color: orange; + } + } + .foo { + @apply @]$text-white; + @apply [background-color:red]; + } + ` + + const result = await run(input, config) + + // TODO: The class `.hover\:before\:\@\]\$content-\[\'Hovering\'\]:hover::before` is not generated + // This happens because of the parenthesis/brace/bracket clipping performed on candidates + + expect(result.css).toMatchFormattedCss(css` + .\@\]\$bg-black { + --tw-bg-opacity: 1; + background-color: rgb(0 0 0 / var(--tw-bg-opacity)); + } + .\@\]\$underline { + text-decoration-line: underline; + } + .my-utility { + color: orange; + } + .foo { + --tw-text-opacity: 1; + color: rgb(255 255 255 / var(--tw-text-opacity)); + background-color: red; + } + `) +})