tailwindcss 3.4.0 → 3.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/README.md +2 -3
  3. package/lib/cli/build/plugin.js +4 -11
  4. package/lib/cli.js +1 -5
  5. package/lib/corePluginList.js +1 -0
  6. package/lib/corePlugins.js +92 -25
  7. package/lib/css/preflight.css +4 -3
  8. package/lib/featureFlags.js +2 -6
  9. package/lib/lib/content.js +36 -3
  10. package/lib/lib/defaultExtractor.js +2 -2
  11. package/lib/lib/expandApplyAtRules.js +13 -0
  12. package/lib/lib/expandTailwindAtRules.js +20 -32
  13. package/lib/lib/generateRules.js +13 -2
  14. package/lib/lib/load-config.js +4 -0
  15. package/lib/lib/offsets.js +51 -2
  16. package/lib/lib/resolveDefaultsAtRules.js +3 -1
  17. package/lib/lib/setupContextUtils.js +23 -3
  18. package/lib/lib/sharedState.js +2 -10
  19. package/lib/plugin.js +0 -50
  20. package/lib/processTailwindFeatures.js +0 -2
  21. package/lib/util/dataTypes.js +12 -2
  22. package/lib/util/pseudoElements.js +3 -0
  23. package/package.json +5 -8
  24. package/peers/index.js +61 -61
  25. package/src/cli/build/plugin.js +4 -11
  26. package/src/cli.js +1 -5
  27. package/src/corePluginList.js +1 -1
  28. package/src/corePlugins.js +88 -27
  29. package/src/css/preflight.css +4 -3
  30. package/src/featureFlags.js +2 -6
  31. package/src/lib/content.js +42 -1
  32. package/src/lib/defaultExtractor.js +2 -2
  33. package/src/lib/expandApplyAtRules.js +17 -0
  34. package/src/lib/expandTailwindAtRules.js +23 -41
  35. package/src/lib/generateRules.js +12 -2
  36. package/src/lib/load-config.ts +5 -0
  37. package/src/lib/offsets.js +61 -2
  38. package/src/lib/resolveDefaultsAtRules.js +5 -1
  39. package/src/lib/setupContextUtils.js +28 -2
  40. package/src/lib/sharedState.js +0 -4
  41. package/src/plugin.js +0 -60
  42. package/src/processTailwindFeatures.js +0 -3
  43. package/src/util/dataTypes.js +13 -1
  44. package/src/util/pseudoElements.js +4 -0
  45. package/types/config.d.ts +7 -0
  46. package/types/generated/corePluginList.d.ts +1 -1
  47. package/lib/lib/detectNesting.js +0 -45
  48. package/lib/oxide/cli/build/deps.js +0 -89
  49. package/lib/oxide/cli/build/index.js +0 -53
  50. package/lib/oxide/cli/build/plugin.js +0 -375
  51. package/lib/oxide/cli/build/utils.js +0 -87
  52. package/lib/oxide/cli/build/watching.js +0 -179
  53. package/lib/oxide/cli/help/index.js +0 -72
  54. package/lib/oxide/cli/index.js +0 -214
  55. package/lib/oxide/cli/init/index.js +0 -52
  56. package/lib/oxide/cli.js +0 -5
  57. package/lib/oxide/postcss-plugin.js +0 -2
  58. package/scripts/swap-engines.js +0 -40
  59. package/src/lib/detectNesting.js +0 -47
  60. package/src/oxide/cli/build/deps.ts +0 -91
  61. package/src/oxide/cli/build/index.ts +0 -47
  62. package/src/oxide/cli/build/plugin.ts +0 -442
  63. package/src/oxide/cli/build/utils.ts +0 -74
  64. package/src/oxide/cli/build/watching.ts +0 -225
  65. package/src/oxide/cli/help/index.ts +0 -69
  66. package/src/oxide/cli/index.ts +0 -204
  67. package/src/oxide/cli/init/index.ts +0 -59
  68. package/src/oxide/cli.ts +0 -1
  69. package/src/oxide/postcss-plugin.ts +0 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ - Nothing yet!
11
+
12
+ ## [3.4.2] - 2024-03-27
13
+
14
+ ### Fixed
15
+
16
+ - Ensure max specificity of `0,0,1` for button and input Preflight rules ([#12735](https://github.com/tailwindlabs/tailwindcss/pull/12735))
17
+ - Improve glob handling for folders with `(`, `)`, `[` or `]` in the file path ([#12715](https://github.com/tailwindlabs/tailwindcss/pull/12715))
18
+ - Split `:has` rules when using `experimental.optimizeUniversalDefaults` ([#12736](https://github.com/tailwindlabs/tailwindcss/pull/12736))
19
+ - Sort arbitrary properties alphabetically across multiple class lists ([#12911](https://github.com/tailwindlabs/tailwindcss/pull/12911))
20
+ - Add `mix-blend-plus-darker` utility ([#12923](https://github.com/tailwindlabs/tailwindcss/pull/12923))
21
+ - Ensure dashes are allowed in variant modifiers ([#13303](https://github.com/tailwindlabs/tailwindcss/pull/13303))
22
+ - Fix crash showing completions in Intellisense when using a custom separator ([#13306](https://github.com/tailwindlabs/tailwindcss/pull/13306))
23
+ - Transpile `import.meta.url` in config files ([#13322](https://github.com/tailwindlabs/tailwindcss/pull/13322))
24
+ - Reset letter spacing for form elements ([#13150](https://github.com/tailwindlabs/tailwindcss/pull/13150))
25
+ - Fix missing `xx-large` and remove double `x-large` absolute size ([#13324](https://github.com/tailwindlabs/tailwindcss/pull/13324))
26
+ - Don't error when encountering nested CSS unless trying to `@apply` a class that uses nesting ([#13325](https://github.com/tailwindlabs/tailwindcss/pull/13325))
27
+ - Ensure that arbitrary properties respect `important` configuration ([#13353](https://github.com/tailwindlabs/tailwindcss/pull/13353))
28
+ - Change dark mode selector so `@apply` works correctly with pseudo elements ([#13379](https://github.com/tailwindlabs/tailwindcss/pull/13379))
29
+
30
+ ## [3.4.1] - 2024-01-05
31
+
32
+ ### Fixed
33
+
34
+ - Don't remove keyframe stops when using important utilities ([#12639](https://github.com/tailwindlabs/tailwindcss/pull/12639))
35
+ - Don't add spaces to gradients and grid track names when followed by `calc()` ([#12704](https://github.com/tailwindlabs/tailwindcss/pull/12704))
36
+ - Restore old behavior for `class` dark mode strategy ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
37
+ - Improve glob handling for folders with `(`, `)`, `[` or `]` in the file path ([#12715](https://github.com/tailwindlabs/tailwindcss/pull/12715))
38
+
39
+ ### Added
40
+
41
+ - Add new `selector` and `variant` strategies for dark mode ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
42
+
43
+ ### Changed
44
+
45
+ - Support `rtl` and `ltr` variants on same element as `dir` attribute ([#12717](https://github.com/tailwindlabs/tailwindcss/pull/12717))
46
+
10
47
  ## [3.4.0] - 2023-12-19
11
48
 
12
49
  ### Added
@@ -2333,7 +2370,9 @@ No release notes
2333
2370
 
2334
2371
  - Everything!
2335
2372
 
2336
- [unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.4.0...HEAD
2373
+ [unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.4.2...HEAD
2374
+ [3.4.2]: https://github.com/tailwindlabs/tailwindcss/compare/v3.4.1...v3.4.2
2375
+ [3.4.1]: https://github.com/tailwindlabs/tailwindcss/compare/v3.4.0...v3.4.1
2337
2376
  [3.4.0]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.7...v3.4.0
2338
2377
  [3.3.7]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.6...v3.3.7
2339
2378
  [3.3.6]: https://github.com/tailwindlabs/tailwindcss/compare/v3.3.5...v3.3.6
package/README.md CHANGED
@@ -12,15 +12,14 @@
12
12
  A utility-first CSS framework for rapidly building custom user interfaces.
13
13
  </p>
14
14
 
15
-
16
15
  <p align="center">
17
- <a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/actions/workflow/status/tailwindlabs/tailwindcss/ci-stable.yml?branch=master" alt="Build Status"></a>
16
+ <a href="https://github.com/tailwindlabs/tailwindcss/actions"><img src="https://img.shields.io/github/actions/workflow/status/tailwindlabs/tailwindcss/ci.yml?branch=master" alt="Build Status"></a>
18
17
  <a href="https://www.npmjs.com/package/tailwindcss"><img src="https://img.shields.io/npm/dt/tailwindcss.svg" alt="Total Downloads"></a>
19
18
  <a href="https://github.com/tailwindcss/tailwindcss/releases"><img src="https://img.shields.io/npm/v/tailwindcss.svg" alt="Latest Release"></a>
20
19
  <a href="https://github.com/tailwindcss/tailwindcss/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/tailwindcss.svg" alt="License"></a>
21
20
  </p>
22
21
 
23
- ------
22
+ ---
24
23
 
25
24
  ## Documentation
26
25
 
@@ -167,17 +167,10 @@ let state = {
167
167
  // TODO: When we make the postcss plugin async-capable this can become async
168
168
  let files = _fastglob.default.sync(this.contentPatterns.all);
169
169
  for (let file of files){
170
- if (false) {
171
- content.push({
172
- file,
173
- extension: _path.default.extname(file).slice(1)
174
- });
175
- } else {
176
- content.push({
177
- content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
178
- extension: _path.default.extname(file).slice(1)
179
- });
180
- }
170
+ content.push({
171
+ content: _fs.default.readFileSync(_path.default.resolve(file), "utf8"),
172
+ extension: _path.default.extname(file).slice(1)
173
+ });
181
174
  }
182
175
  // Resolve raw content in the tailwind config
183
176
  let rawContent = this.config.content.files.filter((file)=>{
package/lib/cli.js CHANGED
@@ -1,7 +1,3 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- if (false) {
4
- module.exports = require("./oxide/cli");
5
- } else {
6
- module.exports = require("./cli/index");
7
- }
3
+ module.exports = require("./cli/index");
@@ -185,6 +185,7 @@ const _default = [
185
185
  "transitionDuration",
186
186
  "transitionTimingFunction",
187
187
  "willChange",
188
+ "contain",
188
189
  "content",
189
190
  "forcedColorAdjust"
190
191
  ];
@@ -267,15 +267,15 @@ let variantPlugins = {
267
267
  }
268
268
  },
269
269
  directionVariants: ({ addVariant })=>{
270
- addVariant("ltr", ':is(:where([dir="ltr"]) &)');
271
- addVariant("rtl", ':is(:where([dir="rtl"]) &)');
270
+ addVariant("ltr", '&:where([dir="ltr"], [dir="ltr"] *)');
271
+ addVariant("rtl", '&:where([dir="rtl"], [dir="rtl"] *)');
272
272
  },
273
273
  reducedMotionVariants: ({ addVariant })=>{
274
274
  addVariant("motion-safe", "@media (prefers-reduced-motion: no-preference)");
275
275
  addVariant("motion-reduce", "@media (prefers-reduced-motion: reduce)");
276
276
  },
277
277
  darkVariants: ({ config , addVariant })=>{
278
- let [mode, className = ".dark"] = [].concat(config("darkMode", "media"));
278
+ let [mode, selector = ".dark"] = [].concat(config("darkMode", "media"));
279
279
  if (mode === false) {
280
280
  mode = "media";
281
281
  _log.default.warn("darkmode-false", [
@@ -284,10 +284,48 @@ let variantPlugins = {
284
284
  "https://tailwindcss.com/docs/upgrade-guide#remove-dark-mode-configuration"
285
285
  ]);
286
286
  }
287
- if (mode === "class") {
288
- addVariant("dark", `:is(:where(${className}) &)`);
287
+ if (mode === "variant") {
288
+ let formats;
289
+ if (Array.isArray(selector)) {
290
+ formats = selector;
291
+ } else if (typeof selector === "function") {
292
+ formats = selector;
293
+ } else if (typeof selector === "string") {
294
+ formats = [
295
+ selector
296
+ ];
297
+ }
298
+ // TODO: We could also add these warnings if the user passes a function that returns string | string[]
299
+ // But this is an advanced enough use case that it's probably not necessary
300
+ if (Array.isArray(formats)) {
301
+ for (let format of formats){
302
+ if (format === ".dark") {
303
+ mode = false;
304
+ _log.default.warn("darkmode-variant-without-selector", [
305
+ "When using `variant` for `darkMode`, you must provide a selector.",
306
+ 'Example: `darkMode: ["variant", ".your-selector &"]`'
307
+ ]);
308
+ } else if (!format.includes("&")) {
309
+ mode = false;
310
+ _log.default.warn("darkmode-variant-without-ampersand", [
311
+ "When using `variant` for `darkMode`, your selector must contain `&`.",
312
+ 'Example `darkMode: ["variant", ".your-selector &"]`'
313
+ ]);
314
+ }
315
+ }
316
+ }
317
+ selector = formats;
318
+ }
319
+ if (mode === "selector") {
320
+ // New preferred behavior
321
+ addVariant("dark", `&:where(${selector}, ${selector} *)`);
289
322
  } else if (mode === "media") {
290
323
  addVariant("dark", "@media (prefers-color-scheme: dark)");
324
+ } else if (mode === "variant") {
325
+ addVariant("dark", selector);
326
+ } else if (mode === "class") {
327
+ // Old behavior
328
+ addVariant("dark", `&:is(${selector} *)`);
291
329
  }
292
330
  },
293
331
  printVariant: ({ addVariant })=>{
@@ -1984,15 +2022,6 @@ let corePlugins = {
1984
2022
  matchUtilities({
1985
2023
  "space-x": (value)=>{
1986
2024
  value = value === "0" ? "0px" : value;
1987
- if (false) {
1988
- return {
1989
- "& > :not([hidden]) ~ :not([hidden])": {
1990
- "--tw-space-x-reverse": "0",
1991
- "margin-inline-end": `calc(${value} * var(--tw-space-x-reverse))`,
1992
- "margin-inline-start": `calc(${value} * calc(1 - var(--tw-space-x-reverse)))`
1993
- }
1994
- };
1995
- }
1996
2025
  return {
1997
2026
  "& > :not([hidden]) ~ :not([hidden])": {
1998
2027
  "--tw-space-x-reverse": "0",
@@ -2028,16 +2057,6 @@ let corePlugins = {
2028
2057
  matchUtilities({
2029
2058
  "divide-x": (value)=>{
2030
2059
  value = value === "0" ? "0px" : value;
2031
- if (false) {
2032
- return {
2033
- "& > :not([hidden]) ~ :not([hidden])": {
2034
- "@defaults border-width": {},
2035
- "--tw-divide-x-reverse": "0",
2036
- "border-inline-end-width": `calc(${value} * var(--tw-divide-x-reverse))`,
2037
- "border-inline-start-width": `calc(${value} * calc(1 - var(--tw-divide-x-reverse)))`
2038
- }
2039
- };
2040
- }
2041
2060
  return {
2042
2061
  "& > :not([hidden]) ~ :not([hidden])": {
2043
2062
  "@defaults border-width": {},
@@ -3654,6 +3673,9 @@ let corePlugins = {
3654
3673
  ".mix-blend-luminosity": {
3655
3674
  "mix-blend-mode": "luminosity"
3656
3675
  },
3676
+ ".mix-blend-plus-darker": {
3677
+ "mix-blend-mode": "plus-darker"
3678
+ },
3657
3679
  ".mix-blend-plus-lighter": {
3658
3680
  "mix-blend-mode": "plus-lighter"
3659
3681
  }
@@ -3667,7 +3689,7 @@ let corePlugins = {
3667
3689
  `var(--tw-shadow)`
3668
3690
  ].join(", ");
3669
3691
  return function({ matchUtilities , addDefaults , theme }) {
3670
- addDefaults(" box-shadow", {
3692
+ addDefaults("box-shadow", {
3671
3693
  "--tw-ring-offset-shadow": "0 0 #0000",
3672
3694
  "--tw-ring-shadow": "0 0 #0000",
3673
3695
  "--tw-shadow": "0 0 #0000",
@@ -4219,6 +4241,51 @@ let corePlugins = {
4219
4241
  ]
4220
4242
  ]
4221
4243
  ]),
4244
+ contain: ({ addDefaults , addUtilities })=>{
4245
+ let cssContainValue = "var(--tw-contain-size) var(--tw-contain-layout) var(--tw-contain-paint) var(--tw-contain-style)";
4246
+ addDefaults("contain", {
4247
+ "--tw-contain-size": " ",
4248
+ "--tw-contain-layout": " ",
4249
+ "--tw-contain-paint": " ",
4250
+ "--tw-contain-style": " "
4251
+ });
4252
+ addUtilities({
4253
+ ".contain-none": {
4254
+ contain: "none"
4255
+ },
4256
+ ".contain-content": {
4257
+ contain: "content"
4258
+ },
4259
+ ".contain-strict": {
4260
+ contain: "strict"
4261
+ },
4262
+ ".contain-size": {
4263
+ "@defaults contain": {},
4264
+ "--tw-contain-size": "size",
4265
+ contain: cssContainValue
4266
+ },
4267
+ ".contain-inline-size": {
4268
+ "@defaults contain": {},
4269
+ "--tw-contain-size": "inline-size",
4270
+ contain: cssContainValue
4271
+ },
4272
+ ".contain-layout": {
4273
+ "@defaults contain": {},
4274
+ "--tw-contain-layout": "layout",
4275
+ contain: cssContainValue
4276
+ },
4277
+ ".contain-paint": {
4278
+ "@defaults contain": {},
4279
+ "--tw-contain-paint": "paint",
4280
+ contain: cssContainValue
4281
+ },
4282
+ ".contain-style": {
4283
+ "@defaults contain": {},
4284
+ "--tw-contain-style": "style",
4285
+ contain: cssContainValue
4286
+ }
4287
+ });
4288
+ },
4222
4289
  content: (0, _createUtilityPlugin.default)("content", [
4223
4290
  [
4224
4291
  "content",
@@ -175,6 +175,7 @@ textarea {
175
175
  font-size: 100%; /* 1 */
176
176
  font-weight: inherit; /* 1 */
177
177
  line-height: inherit; /* 1 */
178
+ letter-spacing: inherit; /* 1 */
178
179
  color: inherit; /* 1 */
179
180
  margin: 0; /* 2 */
180
181
  padding: 0; /* 3 */
@@ -195,9 +196,9 @@ select {
195
196
  */
196
197
 
197
198
  button,
198
- [type='button'],
199
- [type='reset'],
200
- [type='submit'] {
199
+ input:where([type='button']),
200
+ input:where([type='reset']),
201
+ input:where([type='submit']) {
201
202
  -webkit-appearance: button; /* 1 */
202
203
  background-color: transparent; /* 2 */
203
204
  background-image: none; /* 2 */
@@ -29,12 +29,8 @@ function _interop_require_default(obj) {
29
29
  let defaults = {
30
30
  optimizeUniversalDefaults: false,
31
31
  generalizedModifiers: true,
32
- get disableColorOpacityUtilitiesByDefault () {
33
- return false;
34
- },
35
- get relativeContentPathsByDefault () {
36
- return false;
37
- }
32
+ disableColorOpacityUtilitiesByDefault: false,
33
+ relativeContentPathsByDefault: false
38
34
  };
39
35
  let featureFlags = {
40
36
  future: [
@@ -21,7 +21,6 @@ const _fs = /*#__PURE__*/ _interop_require_default(require("fs"));
21
21
  const _path = /*#__PURE__*/ _interop_require_default(require("path"));
22
22
  const _isglob = /*#__PURE__*/ _interop_require_default(require("is-glob"));
23
23
  const _fastglob = /*#__PURE__*/ _interop_require_default(require("fast-glob"));
24
- const _normalizepath = /*#__PURE__*/ _interop_require_default(require("normalize-path"));
25
24
  const _parseGlob = require("../util/parseGlob");
26
25
  const _sharedState = require("./sharedState");
27
26
  function _interop_require_default(obj) {
@@ -29,11 +28,42 @@ function _interop_require_default(obj) {
29
28
  default: obj
30
29
  };
31
30
  }
31
+ /*!
32
+ * Modified version of normalize-path, original license below
33
+ *
34
+ * normalize-path <https://github.com/jonschlinkert/normalize-path>
35
+ *
36
+ * Copyright (c) 2014-2018, Jon Schlinkert.
37
+ * Released under the MIT License.
38
+ */ function normalizePath(path) {
39
+ if (typeof path !== "string") {
40
+ throw new TypeError("expected path to be a string");
41
+ }
42
+ if (path === "\\" || path === "/") return "/";
43
+ var len = path.length;
44
+ if (len <= 1) return path;
45
+ // ensure that win32 namespaces has two leading slashes, so that the path is
46
+ // handled properly by the win32 version of path.parse() after being normalized
47
+ // https://msdn.microsoft.com/library/windows/desktop/aa365247(v=vs.85).aspx#namespaces
48
+ var prefix = "";
49
+ if (len > 4 && path[3] === "\\") {
50
+ var ch = path[2];
51
+ if ((ch === "?" || ch === ".") && path.slice(0, 2) === "\\\\") {
52
+ path = path.slice(2);
53
+ prefix = "//";
54
+ }
55
+ }
56
+ // Modified part: instead of purely splitting on `\\` and `/`, we split on
57
+ // `/` and `\\` that is _not_ followed by any of the following characters: ()[]
58
+ // This is to ensure that we keep the escaping of brackets and parentheses
59
+ let segs = path.split(/[/\\]+(?![\(\)\[\]])/);
60
+ return prefix + segs.join("/");
61
+ }
32
62
  function parseCandidateFiles(context, tailwindConfig) {
33
63
  let files = tailwindConfig.content.files;
34
64
  // Normalize the file globs
35
65
  files = files.filter((filePath)=>typeof filePath === "string");
36
- files = files.map(_normalizepath.default);
66
+ files = files.map(normalizePath);
37
67
  // Split into included and excluded globs
38
68
  let tasks = _fastglob.default.generateTasks(files);
39
69
  /** @type {ContentPath[]} */ let included = [];
@@ -60,6 +90,9 @@ function parseCandidateFiles(context, tailwindConfig) {
60
90
  * @param {boolean} ignore
61
91
  * @returns {ContentPath}
62
92
  */ function parseFilePath(filePath, ignore) {
93
+ // Escape special characters in the file path such as: ()[]
94
+ // But only if the special character isn't already escaped
95
+ filePath = filePath.replace(/(?<!\\)([\[\]\(\)])/g, "\\$1");
63
96
  let contentPath = {
64
97
  original: filePath,
65
98
  base: filePath,
@@ -81,7 +114,7 @@ function parseCandidateFiles(context, tailwindConfig) {
81
114
  // Afaik, this technically shouldn't be needed but there's probably
82
115
  // some internal, direct path matching with a normalized path in
83
116
  // a package which can't handle mixed directory separators
84
- let base = (0, _normalizepath.default)(contentPath.base);
117
+ let base = normalizePath(contentPath.base);
85
118
  // If the user's file path contains any special characters (like parens) for instance fast-glob
86
119
  // is like "OOOH SHINY" and treats them as such. So we have to escape the base path to fix this
87
120
  base = _fastglob.default.escapePath(base);
@@ -128,7 +128,7 @@ function* buildRegExps(context) {
128
128
  ]),
129
129
  // With variant modifier (e.g.: group-[..]/modifier)
130
130
  _regex.pattern([
131
- /([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/\w+/,
131
+ /([^\s"'`\[\\]+-)?\[[^\s"'`]+\]\/[\w_-]+/,
132
132
  separator
133
133
  ]),
134
134
  _regex.pattern([
@@ -144,7 +144,7 @@ function* buildRegExps(context) {
144
144
  _regex.any([
145
145
  // With variant modifier (e.g.: group-[..]/modifier)
146
146
  _regex.pattern([
147
- /([^\s"'`\[\\]+-)?\[[^\s`]+\]\/\w+/,
147
+ /([^\s"'`\[\\]+-)?\[[^\s`]+\]\/[\w_-]+/,
148
148
  separator
149
149
  ]),
150
150
  _regex.pattern([
@@ -380,6 +380,19 @@ function processApply(root, context, localCache) {
380
380
  throw apply.error(`The \`${applyCandidate}\` class does not exist. If \`${applyCandidate}\` is a custom class, make sure it is defined within a \`@layer\` directive.`);
381
381
  }
382
382
  let rules = applyClassCache.get(applyCandidate);
383
+ // Verify that we can apply the class
384
+ for (let [, rule] of rules){
385
+ if (rule.type === "atrule") {
386
+ continue;
387
+ }
388
+ rule.walkRules(()=>{
389
+ throw apply.error([
390
+ `The \`${applyCandidate}\` class cannot be used with \`@apply\` because \`@apply\` does not currently support nested CSS.`,
391
+ "Rewrite the selector without nesting or configure the `tailwindcss/nesting` plugin:",
392
+ "https://tailwindcss.com/docs/using-with-preprocessors#nesting"
393
+ ].join("\n"));
394
+ });
395
+ }
383
396
  candidates.push([
384
397
  applyCandidate,
385
398
  important,
@@ -154,37 +154,25 @@ function expandTailwindAtRules(context) {
154
154
  ]);
155
155
  let seen = new Set();
156
156
  env.DEBUG && console.time("Reading changed files");
157
- if (false) {
158
- // TODO: Pass through or implement `extractor`
159
- for (let candidate of require("@tailwindcss/oxide").parseCandidateStringsFromFiles(context.changedContent)){
160
- candidates.add(candidate);
161
- }
162
- // for (let { file, content, extension } of context.changedContent) {
163
- // let transformer = getTransformer(context.tailwindConfig, extension)
164
- // let extractor = getExtractor(context, extension)
165
- // getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
166
- // }
167
- } else {
168
- /** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */ let regexParserContent = [];
169
- for (let item of context.changedContent){
170
- let transformer = getTransformer(context.tailwindConfig, item.extension);
171
- let extractor = getExtractor(context, item.extension);
172
- regexParserContent.push([
173
- item,
174
- {
175
- transformer,
176
- extractor
177
- }
178
- ]);
179
- }
180
- const BATCH_SIZE = 500;
181
- for(let i = 0; i < regexParserContent.length; i += BATCH_SIZE){
182
- let batch = regexParserContent.slice(i, i + BATCH_SIZE);
183
- await Promise.all(batch.map(async ([{ file , content }, { transformer , extractor }])=>{
184
- content = file ? await _fs.default.promises.readFile(file, "utf8") : content;
185
- getClassCandidates(transformer(content), extractor, candidates, seen);
186
- }));
187
- }
157
+ /** @type {[item: {file?: string, content?: string}, meta: {transformer: any, extractor: any}][]} */ let regexParserContent = [];
158
+ for (let item of context.changedContent){
159
+ let transformer = getTransformer(context.tailwindConfig, item.extension);
160
+ let extractor = getExtractor(context, item.extension);
161
+ regexParserContent.push([
162
+ item,
163
+ {
164
+ transformer,
165
+ extractor
166
+ }
167
+ ]);
168
+ }
169
+ const BATCH_SIZE = 500;
170
+ for(let i = 0; i < regexParserContent.length; i += BATCH_SIZE){
171
+ let batch = regexParserContent.slice(i, i + BATCH_SIZE);
172
+ await Promise.all(batch.map(async ([{ file , content }, { transformer , extractor }])=>{
173
+ content = file ? await _fs.default.promises.readFile(file, "utf8") : content;
174
+ getClassCandidates(transformer(content), extractor, candidates, seen);
175
+ }));
188
176
  }
189
177
  env.DEBUG && console.timeEnd("Reading changed files");
190
178
  // ---
@@ -192,7 +180,7 @@ function expandTailwindAtRules(context) {
192
180
  let classCacheCount = context.classCache.size;
193
181
  env.DEBUG && console.time("Generate rules");
194
182
  env.DEBUG && console.time("Sorting candidates");
195
- let sortedCandidates = false ? candidates : new Set([
183
+ let sortedCandidates = new Set([
196
184
  ...candidates
197
185
  ].sort((a, z)=>{
198
186
  if (a === z) return 0;
@@ -162,6 +162,9 @@ function applyImportant(matches, classCandidate) {
162
162
  return matches;
163
163
  }
164
164
  let result = [];
165
+ function isInKeyframes(rule) {
166
+ return rule.parent && rule.parent.type === "atrule" && rule.parent.name === "keyframes";
167
+ }
165
168
  for (let [meta, rule] of matches){
166
169
  let container = _postcss.default.root({
167
170
  nodes: [
@@ -169,6 +172,11 @@ function applyImportant(matches, classCandidate) {
169
172
  ]
170
173
  });
171
174
  container.walkRules((r)=>{
175
+ // Declarations inside keyframes cannot be marked as important
176
+ // They will be ignored by the browser
177
+ if (isInKeyframes(r)) {
178
+ return;
179
+ }
172
180
  let ast = (0, _postcssselectorparser.default)().astSync(r.selector);
173
181
  // Remove extraneous selectors that do not include the base candidate
174
182
  ast.each((sel)=>(0, _formatVariantSelector.eliminateIrrelevantSelectors)(sel, classCandidate));
@@ -506,12 +514,15 @@ function extractArbitraryProperty(classCandidate, context) {
506
514
  if (!isParsableCssValue(property, normalized)) {
507
515
  return null;
508
516
  }
509
- let sort = context.offsets.arbitraryProperty();
517
+ let sort = context.offsets.arbitraryProperty(classCandidate);
510
518
  return [
511
519
  [
512
520
  {
513
521
  sort,
514
- layer: "utilities"
522
+ layer: "utilities",
523
+ options: {
524
+ respectImportant: true
525
+ }
515
526
  },
516
527
  ()=>({
517
528
  [(0, _nameClass.asClass)(classCandidate)]: {
@@ -31,6 +31,10 @@ function lazyJiti() {
31
31
  return jiti !== null && jiti !== void 0 ? jiti : jiti = (0, _jiti.default)(__filename, {
32
32
  interopDefault: true,
33
33
  transform: (opts)=>{
34
+ // Sucrase can't transform import.meta so we have to use Babel
35
+ if (opts.source.includes("import.meta")) {
36
+ return require("jiti/dist/babel.js")(opts);
37
+ }
34
38
  return (0, _sucrase.transform)(opts.source, {
35
39
  transforms: [
36
40
  "typescript",
@@ -66,15 +66,19 @@ class Offsets {
66
66
  variants: 0n,
67
67
  parallelIndex: 0n,
68
68
  index: this.offsets[layer]++,
69
+ propertyOffset: 0n,
70
+ property: "",
69
71
  options: []
70
72
  };
71
73
  }
72
74
  /**
75
+ * @param {string} name
73
76
  * @returns {RuleOffset}
74
- */ arbitraryProperty() {
77
+ */ arbitraryProperty(name) {
75
78
  return {
76
79
  ...this.create("utilities"),
77
- arbitrary: 1n
80
+ arbitrary: 1n,
81
+ property: name
78
82
  };
79
83
  }
80
84
  /**
@@ -214,6 +218,10 @@ class Offsets {
214
218
  if (a.arbitrary !== b.arbitrary) {
215
219
  return a.arbitrary - b.arbitrary;
216
220
  }
221
+ // Always sort arbitrary properties alphabetically
222
+ if (a.propertyOffset !== b.propertyOffset) {
223
+ return a.propertyOffset - b.propertyOffset;
224
+ }
217
225
  // Sort utilities, components, etc… in the order they were registered
218
226
  return a.index - b.index;
219
227
  }
@@ -267,8 +275,49 @@ class Offsets {
267
275
  * @template T
268
276
  * @param {[RuleOffset, T][]} list
269
277
  * @returns {[RuleOffset, T][]}
278
+ */ sortArbitraryProperties(list) {
279
+ // Collect all known arbitrary properties
280
+ let known = new Set();
281
+ for (let [offset] of list){
282
+ if (offset.arbitrary === 1n) {
283
+ known.add(offset.property);
284
+ }
285
+ }
286
+ // No arbitrary properties? Nothing to do.
287
+ if (known.size === 0) {
288
+ return list;
289
+ }
290
+ // Sort the properties alphabetically
291
+ let properties = Array.from(known).sort();
292
+ // Create a map from the property name to its offset
293
+ let offsets = new Map();
294
+ let offset = 1n;
295
+ for (let property of properties){
296
+ offsets.set(property, offset++);
297
+ }
298
+ // Apply the sorted offsets to the list
299
+ return list.map((item)=>{
300
+ let [offset, rule] = item;
301
+ var _offsets_get;
302
+ offset = {
303
+ ...offset,
304
+ propertyOffset: (_offsets_get = offsets.get(offset.property)) !== null && _offsets_get !== void 0 ? _offsets_get : 0n
305
+ };
306
+ return [
307
+ offset,
308
+ rule
309
+ ];
310
+ });
311
+ }
312
+ /**
313
+ * @template T
314
+ * @param {[RuleOffset, T][]} list
315
+ * @returns {[RuleOffset, T][]}
270
316
  */ sort(list) {
317
+ // Sort arbitrary variants so they're in alphabetical order
271
318
  list = this.remapArbitraryVariantOffsets(list);
319
+ // Sort arbitrary properties so they're in alphabetical order
320
+ list = this.sortArbitraryProperties(list);
272
321
  return list.sort(([a], [b])=>(0, _bigSign.default)(this.compare(a, b)));
273
322
  }
274
323
  }
@@ -111,7 +111,9 @@ function resolveDefaultsAtRules({ tailwindConfig }) {
111
111
  // we consider them separately because merging the declarations into
112
112
  // a single rule will cause browsers that do not understand the
113
113
  // vendor prefix to throw out the whole rule
114
- let selectorGroupName = selector.includes(":-") || selector.includes("::-") ? selector : "__DEFAULT__";
114
+ // Additionally if a selector contains `:has` we also consider
115
+ // it separately because FF only recently gained support for it
116
+ let selectorGroupName = selector.includes(":-") || selector.includes("::-") || selector.includes(":has") ? selector : "__DEFAULT__";
115
117
  var _selectorGroups_get;
116
118
  let selectors = (_selectorGroups_get = selectorGroups.get(selectorGroupName)) !== null && _selectorGroups_get !== void 0 ? _selectorGroups_get : new Set();
117
119
  selectorGroups.set(selectorGroupName, selectors);