tailwindcss 3.2.4 → 3.2.5

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 (100) hide show
  1. package/CHANGELOG.md +44 -1
  2. package/README.md +1 -1
  3. package/lib/cli/build/index.js +5 -1
  4. package/lib/cli/build/plugin.js +39 -34
  5. package/lib/cli/index.js +231 -10
  6. package/lib/cli/init/index.js +2 -2
  7. package/lib/cli.js +4 -226
  8. package/lib/corePlugins.js +45 -27
  9. package/lib/featureFlags.js +8 -8
  10. package/lib/index.js +4 -46
  11. package/lib/lib/collapseAdjacentRules.js +2 -2
  12. package/lib/lib/collapseDuplicateDeclarations.js +2 -2
  13. package/lib/lib/content.js +16 -16
  14. package/lib/lib/defaultExtractor.js +10 -5
  15. package/lib/lib/detectNesting.js +7 -1
  16. package/lib/lib/evaluateTailwindFunctions.js +4 -4
  17. package/lib/lib/expandApplyAtRules.js +2 -2
  18. package/lib/lib/expandTailwindAtRules.js +35 -9
  19. package/lib/lib/findAtConfigPath.js +3 -3
  20. package/lib/lib/generateRules.js +105 -50
  21. package/lib/lib/offsets.js +88 -1
  22. package/lib/lib/remap-bitfield.js +87 -0
  23. package/lib/lib/resolveDefaultsAtRules.js +4 -4
  24. package/lib/lib/setupContextUtils.js +121 -80
  25. package/lib/lib/setupTrackingContext.js +25 -4
  26. package/lib/lib/sharedState.js +19 -1
  27. package/lib/oxide/cli/build/deps.js +81 -0
  28. package/lib/oxide/cli/build/index.js +47 -0
  29. package/lib/oxide/cli/build/plugin.js +364 -0
  30. package/lib/oxide/cli/build/utils.js +77 -0
  31. package/lib/oxide/cli/build/watching.js +177 -0
  32. package/lib/oxide/cli/help/index.js +70 -0
  33. package/lib/oxide/cli/index.js +220 -0
  34. package/lib/oxide/cli/init/index.js +35 -0
  35. package/lib/oxide/cli.js +5 -0
  36. package/lib/oxide/postcss-plugin.js +2 -0
  37. package/lib/plugin.js +98 -0
  38. package/lib/postcss-plugins/nesting/plugin.js +2 -2
  39. package/lib/util/cloneNodes.js +2 -2
  40. package/lib/util/color.js +20 -6
  41. package/lib/util/createUtilityPlugin.js +2 -2
  42. package/lib/util/dataTypes.js +26 -2
  43. package/lib/util/defaults.js +4 -4
  44. package/lib/util/escapeClassName.js +3 -3
  45. package/lib/util/formatVariantSelector.js +171 -105
  46. package/lib/util/getAllConfigs.js +2 -2
  47. package/lib/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +2 -2
  48. package/lib/util/negateValue.js +2 -2
  49. package/lib/util/normalizeConfig.js +22 -22
  50. package/lib/util/pluginUtils.js +38 -40
  51. package/lib/util/prefixSelector.js +22 -8
  52. package/lib/util/resolveConfig.js +8 -10
  53. package/oxide-node-api-shim/index.js +21 -0
  54. package/oxide-node-api-shim/package.json +5 -0
  55. package/package.json +32 -19
  56. package/peers/index.js +61 -42
  57. package/resolveConfig.d.ts +11 -2
  58. package/scripts/swap-engines.js +40 -0
  59. package/src/cli/build/index.js +6 -2
  60. package/src/cli/build/plugin.js +14 -9
  61. package/src/cli/index.js +234 -3
  62. package/src/cli.js +4 -220
  63. package/src/corePlugins.js +31 -3
  64. package/src/index.js +4 -46
  65. package/src/lib/content.js +12 -17
  66. package/src/lib/defaultExtractor.js +9 -3
  67. package/src/lib/detectNesting.js +9 -1
  68. package/src/lib/expandTailwindAtRules.js +37 -6
  69. package/src/lib/generateRules.js +90 -28
  70. package/src/lib/offsets.js +104 -1
  71. package/src/lib/remap-bitfield.js +82 -0
  72. package/src/lib/setupContextUtils.js +97 -55
  73. package/src/lib/setupTrackingContext.js +31 -6
  74. package/src/lib/sharedState.js +17 -0
  75. package/src/oxide/cli/build/deps.ts +91 -0
  76. package/src/oxide/cli/build/index.ts +47 -0
  77. package/src/oxide/cli/build/plugin.ts +436 -0
  78. package/src/oxide/cli/build/utils.ts +74 -0
  79. package/src/oxide/cli/build/watching.ts +225 -0
  80. package/src/oxide/cli/help/index.ts +69 -0
  81. package/src/oxide/cli/index.ts +212 -0
  82. package/src/oxide/cli/init/index.ts +32 -0
  83. package/src/oxide/cli.ts +1 -0
  84. package/src/oxide/postcss-plugin.ts +1 -0
  85. package/src/plugin.js +107 -0
  86. package/src/util/color.js +17 -2
  87. package/src/util/dataTypes.js +29 -4
  88. package/src/util/formatVariantSelector.js +215 -122
  89. package/src/util/{isValidArbitraryValue.js → isSyntacticallyValidPropertyValue.js} +1 -1
  90. package/src/util/negateValue.js +1 -1
  91. package/src/util/pluginUtils.js +22 -19
  92. package/src/util/prefixSelector.js +28 -10
  93. package/src/util/resolveConfig.js +0 -2
  94. package/stubs/defaultConfig.stub.js +149 -165
  95. package/types/config.d.ts +7 -2
  96. package/types/generated/default-theme.d.ts +77 -77
  97. package/lib/cli/shared.js +0 -12
  98. package/scripts/install-integrations.js +0 -27
  99. package/scripts/rebuildFixtures.js +0 -68
  100. package/src/cli/shared.js +0 -5
@@ -6,12 +6,14 @@ Object.defineProperty(exports, "default", {
6
6
  enumerable: true,
7
7
  get: ()=>expandTailwindAtRules
8
8
  });
9
+ const _fs = /*#__PURE__*/ _interopRequireDefault(require("fs"));
9
10
  const _quickLru = /*#__PURE__*/ _interopRequireDefault(require("quick-lru"));
10
11
  const _sharedState = /*#__PURE__*/ _interopRequireWildcard(require("./sharedState"));
11
12
  const _generateRules = require("./generateRules");
12
13
  const _log = /*#__PURE__*/ _interopRequireDefault(require("../util/log"));
13
14
  const _cloneNodes = /*#__PURE__*/ _interopRequireDefault(require("../util/cloneNodes"));
14
15
  const _defaultExtractor = require("./defaultExtractor");
16
+ const _oxide = /*#__PURE__*/ _interopRequireDefault(require("@tailwindcss/oxide"));
15
17
  function _interopRequireDefault(obj) {
16
18
  return obj && obj.__esModule ? obj : {
17
19
  default: obj
@@ -142,24 +144,48 @@ function expandTailwindAtRules(context) {
142
144
  if (Object.values(layerNodes).every((n)=>n === null)) {
143
145
  return root;
144
146
  }
147
+ var _context_candidates;
145
148
  // ---
146
149
  // Find potential rules in changed files
147
150
  let candidates = new Set([
151
+ ...(_context_candidates = context.candidates) !== null && _context_candidates !== void 0 ? _context_candidates : [],
148
152
  _sharedState.NOT_ON_DEMAND
149
153
  ]);
150
154
  let seen = new Set();
151
155
  env.DEBUG && console.time("Reading changed files");
152
- for (let { content , extension } of context.changedContent){
153
- let transformer = getTransformer(context.tailwindConfig, extension);
154
- let extractor = getExtractor(context, extension);
155
- getClassCandidates(transformer(content), extractor, candidates, seen);
156
+ if (env.OXIDE) {
157
+ // TODO: Pass through or implement `extractor`
158
+ for (let candidate of _oxide.default.parseCandidateStringsFromFiles(context.changedContent)){
159
+ candidates.add(candidate);
160
+ }
161
+ // for (let { file, content, extension } of context.changedContent) {
162
+ // let transformer = getTransformer(context.tailwindConfig, extension)
163
+ // let extractor = getExtractor(context, extension)
164
+ // getClassCandidatesOxide(file, transformer(content), extractor, candidates, seen)
165
+ // }
166
+ } else {
167
+ for (let { file , content , extension } of context.changedContent){
168
+ let transformer = getTransformer(context.tailwindConfig, extension);
169
+ let extractor = getExtractor(context, extension);
170
+ content = file ? _fs.default.readFileSync(file, "utf8") : content;
171
+ getClassCandidates(transformer(content), extractor, candidates, seen);
172
+ }
156
173
  }
157
174
  env.DEBUG && console.timeEnd("Reading changed files");
158
175
  // ---
159
176
  // Generate the actual CSS
160
177
  let classCacheCount = context.classCache.size;
161
178
  env.DEBUG && console.time("Generate rules");
162
- (0, _generateRules.generateRules)(candidates, context);
179
+ env.DEBUG && console.time("Sorting candidates");
180
+ let sortedCandidates = env.OXIDE ? candidates : new Set([
181
+ ...candidates
182
+ ].sort((a, z)=>{
183
+ if (a === z) return 0;
184
+ if (a < z) return -1;
185
+ return 1;
186
+ }));
187
+ env.DEBUG && console.timeEnd("Sorting candidates");
188
+ (0, _generateRules.generateRules)(sortedCandidates, context);
163
189
  env.DEBUG && console.timeEnd("Generate rules");
164
190
  // We only ever add to the classCache, so if it didn't grow, there is nothing new.
165
191
  env.DEBUG && console.time("Build stylesheet");
@@ -199,8 +225,8 @@ function expandTailwindAtRules(context) {
199
225
  }
200
226
  // We do post-filtering to not alter the emitted order of the variants
201
227
  const variantNodes = Array.from(screenNodes).filter((node)=>{
202
- var ref;
203
- const parentLayer = (ref = node.raws.tailwind) === null || ref === void 0 ? void 0 : ref.parentLayer;
228
+ var _node_raws_tailwind;
229
+ const parentLayer = (_node_raws_tailwind = node.raws.tailwind) === null || _node_raws_tailwind === void 0 ? void 0 : _node_raws_tailwind.parentLayer;
204
230
  if (parentLayer === "components") {
205
231
  return layerNodes.components !== null;
206
232
  }
@@ -221,8 +247,8 @@ function expandTailwindAtRules(context) {
221
247
  }
222
248
  // If we've got a utility layer and no utilities are generated there's likely something wrong
223
249
  const hasUtilityVariants = variantNodes.some((node)=>{
224
- var ref;
225
- return ((ref = node.raws.tailwind) === null || ref === void 0 ? void 0 : ref.parentLayer) === "utilities";
250
+ var _node_raws_tailwind;
251
+ return ((_node_raws_tailwind = node.raws.tailwind) === null || _node_raws_tailwind === void 0 ? void 0 : _node_raws_tailwind.parentLayer) === "utilities";
226
252
  });
227
253
  if (layerNodes.utilities && utilityNodes.size === 0 && !hasUtilityVariants) {
228
254
  _log.default.warn("content-problems", [
@@ -17,9 +17,9 @@ function findAtConfigPath(root, result) {
17
17
  let configPath = null;
18
18
  let relativeTo = null;
19
19
  root.walkAtRules("config", (rule)=>{
20
- var ref;
21
- var _file, ref1;
22
- relativeTo = (ref1 = (_file = (ref = rule.source) === null || ref === void 0 ? void 0 : ref.input.file) !== null && _file !== void 0 ? _file : result.opts.from) !== null && ref1 !== void 0 ? ref1 : null;
20
+ var _rule_source;
21
+ var _rule_source_input_file, _ref;
22
+ relativeTo = (_ref = (_rule_source_input_file = (_rule_source = rule.source) === null || _rule_source === void 0 ? void 0 : _rule_source.input.file) !== null && _rule_source_input_file !== void 0 ? _rule_source_input_file : result.opts.from) !== null && _ref !== void 0 ? _ref : null;
23
23
  if (relativeTo === null) {
24
24
  throw rule.error("The `@config` directive cannot be used without setting `from` in your PostCSS config.");
25
25
  }
@@ -25,7 +25,7 @@ const _formatVariantSelector = require("../util/formatVariantSelector");
25
25
  const _nameClass = require("../util/nameClass");
26
26
  const _dataTypes = require("../util/dataTypes");
27
27
  const _setupContextUtils = require("./setupContextUtils");
28
- const _isValidArbitraryValue = /*#__PURE__*/ _interopRequireDefault(require("../util/isValidArbitraryValue"));
28
+ const _isSyntacticallyValidPropertyValue = /*#__PURE__*/ _interopRequireDefault(require("../util/isSyntacticallyValidPropertyValue"));
29
29
  const _splitAtTopLevelOnlyJs = require("../util/splitAtTopLevelOnly.js");
30
30
  const _featureFlags = require("../featureFlags");
31
31
  function _interopRequireDefault(obj) {
@@ -198,10 +198,16 @@ function applyVariant(variant, matches, context) {
198
198
  };
199
199
  // Retrieve "modifier"
200
200
  {
201
- let match = /(.*)\/(.*)$/g.exec(variant);
202
- if (match) {
203
- variant = match[1];
204
- args.modifier = match[2];
201
+ let [baseVariant, ...modifiers] = (0, _splitAtTopLevelOnlyJs.splitAtTopLevelOnly)(variant, "/");
202
+ // This is a hack to support variants with `/` in them, like `ar-1/10/20:text-red-500`
203
+ // In this case 1/10 is a value but /20 is a modifier
204
+ if (modifiers.length > 1) {
205
+ baseVariant = baseVariant + "/" + modifiers.slice(0, -1).join("/");
206
+ modifiers = modifiers.slice(-1);
207
+ }
208
+ if (modifiers.length && !context.variantMap.has(variant)) {
209
+ variant = baseVariant;
210
+ args.modifier = modifiers[0];
205
211
  if (!(0, _featureFlags.flagEnabled)(context.tailwindConfig, "generalizedModifiers")) {
206
212
  return [];
207
213
  }
@@ -216,9 +222,9 @@ function applyVariant(variant, matches, context) {
216
222
  // But we don't want:
217
223
  // @-[200px] (`-` is incorrect)
218
224
  // group[:hover] (`-` is missing)
219
- let match1 = /(.)(-?)\[(.*)\]/g.exec(variant);
220
- if (match1) {
221
- let [, char, seperator, value] = match1;
225
+ let match = /(.)(-?)\[(.*)\]/g.exec(variant);
226
+ if (match) {
227
+ let [, char, seperator, value] = match;
222
228
  // @-[200px] case
223
229
  if (char === "@" && seperator === "-") return [];
224
230
  // group[:hover] case
@@ -243,6 +249,7 @@ function applyVariant(variant, matches, context) {
243
249
  ]);
244
250
  }
245
251
  if (context.variantMap.has(variant)) {
252
+ let isArbitraryVariant = isArbitraryValue(variant);
246
253
  let variantFunctionTuples = context.variantMap.get(variant).slice();
247
254
  let result = [];
248
255
  for (let [meta, rule] of matches){
@@ -299,7 +306,10 @@ function applyVariant(variant, matches, context) {
299
306
  clone.append(wrapper);
300
307
  },
301
308
  format (selectorFormat) {
302
- collectedFormats.push(selectorFormat);
309
+ collectedFormats.push({
310
+ format: selectorFormat,
311
+ isArbitraryVariant
312
+ });
303
313
  },
304
314
  args
305
315
  });
@@ -322,7 +332,10 @@ function applyVariant(variant, matches, context) {
322
332
  continue;
323
333
  }
324
334
  if (typeof ruleWithVariant === "string") {
325
- collectedFormats.push(ruleWithVariant);
335
+ collectedFormats.push({
336
+ format: ruleWithVariant,
337
+ isArbitraryVariant
338
+ });
326
339
  }
327
340
  if (ruleWithVariant === null) {
328
341
  continue;
@@ -358,7 +371,10 @@ function applyVariant(variant, matches, context) {
358
371
  // modified (by plugin): .foo .foo\\:markdown > p
359
372
  // rebuiltBase (internal): .foo\\:markdown > p
360
373
  // format: .foo &
361
- collectedFormats.push(modified.replace(rebuiltBase, "&"));
374
+ collectedFormats.push({
375
+ format: modified.replace(rebuiltBase, "&"),
376
+ isArbitraryVariant
377
+ });
362
378
  rule.selector = before;
363
379
  });
364
380
  }
@@ -370,13 +386,12 @@ function applyVariant(variant, matches, context) {
370
386
  ...clone.nodes[0].raws.tailwind,
371
387
  parentLayer: meta.layer
372
388
  };
373
- var _collectedFormats;
389
+ var _meta_collectedFormats;
374
390
  let withOffset = [
375
391
  {
376
392
  ...meta,
377
393
  sort: context.offsets.applyVariantOffset(meta.sort, variantSort, Object.assign(args, context.variantOptions.get(variant))),
378
- collectedFormats: ((_collectedFormats = meta.collectedFormats) !== null && _collectedFormats !== void 0 ? _collectedFormats : []).concat(collectedFormats),
379
- isArbitraryVariant: isArbitraryValue(variant)
394
+ collectedFormats: ((_meta_collectedFormats = meta.collectedFormats) !== null && _meta_collectedFormats !== void 0 ? _meta_collectedFormats : []).concat(collectedFormats)
380
395
  },
381
396
  clone.nodes[0]
382
397
  ];
@@ -434,7 +449,7 @@ function isValidPropName(name) {
434
449
  function isParsableNode(node) {
435
450
  let isParsable = true;
436
451
  node.walkDecls((decl)=>{
437
- if (!isParsableCssValue(decl.name, decl.value)) {
452
+ if (!isParsableCssValue(decl.prop, decl.value)) {
438
453
  isParsable = false;
439
454
  return false;
440
455
  }
@@ -456,15 +471,15 @@ function isParsableCssValue(property, value) {
456
471
  }
457
472
  }
458
473
  function extractArbitraryProperty(classCandidate, context) {
459
- var ref;
460
- let [, property, value] = (ref = classCandidate.match(/^\[([a-zA-Z0-9-_]+):(\S+)\]$/)) !== null && ref !== void 0 ? ref : [];
474
+ var _classCandidate_match;
475
+ let [, property, value] = (_classCandidate_match = classCandidate.match(/^\[([a-zA-Z0-9-_]+):(\S+)\]$/)) !== null && _classCandidate_match !== void 0 ? _classCandidate_match : [];
461
476
  if (value === undefined) {
462
477
  return null;
463
478
  }
464
479
  if (!isValidPropName(property)) {
465
480
  return null;
466
481
  }
467
- if (!(0, _isValidArbitraryValue.default)(value)) {
482
+ if (!(0, _isSyntacticallyValidPropertyValue.default)(value)) {
468
483
  return null;
469
484
  }
470
485
  let normalized = (0, _dataTypes.normalize)(value);
@@ -535,12 +550,12 @@ function splitWithSeparator(input, separator) {
535
550
  }
536
551
  function* recordCandidates(matches, classCandidate) {
537
552
  for (const match of matches){
538
- var ref;
539
- var ref1;
553
+ var _match__options;
554
+ var _match__options_preserveSource;
540
555
  match[1].raws.tailwind = {
541
556
  ...match[1].raws.tailwind,
542
557
  classCandidate,
543
- preserveSource: (ref1 = (ref = match[0].options) === null || ref === void 0 ? void 0 : ref.preserveSource) !== null && ref1 !== void 0 ? ref1 : false
558
+ preserveSource: (_match__options_preserveSource = (_match__options = match[0].options) === null || _match__options === void 0 ? void 0 : _match__options.preserveSource) !== null && _match__options_preserveSource !== void 0 ? _match__options_preserveSource : false
544
559
  };
545
560
  yield match;
546
561
  }
@@ -614,9 +629,9 @@ function* resolveMatches(candidate, context, original = candidate) {
614
629
  }
615
630
  }
616
631
  if (matchesPerPlugin.length > 0) {
617
- var ref;
618
- var ref1, _options;
619
- let matchingTypes = Array.from((0, _pluginUtils.getMatchingTypes)((ref1 = (ref = sort.options) === null || ref === void 0 ? void 0 : ref.types) !== null && ref1 !== void 0 ? ref1 : [], modifier, (_options = sort.options) !== null && _options !== void 0 ? _options : {}, context.tailwindConfig)).map(([_, type])=>type);
632
+ var _sort_options;
633
+ var _sort_options_types, _sort_options1;
634
+ let matchingTypes = Array.from((0, _pluginUtils.getMatchingTypes)((_sort_options_types = (_sort_options = sort.options) === null || _sort_options === void 0 ? void 0 : _sort_options.types) !== null && _sort_options_types !== void 0 ? _sort_options_types : [], modifier, (_sort_options1 = sort.options) !== null && _sort_options1 !== void 0 ? _sort_options1 : {}, context.tailwindConfig)).map(([_, type])=>type);
620
635
  if (matchingTypes.length > 0) {
621
636
  typesByMatches.set(matchesPerPlugin, matchingTypes);
622
637
  }
@@ -656,18 +671,18 @@ function* resolveMatches(candidate, context, original = candidate) {
656
671
  });
657
672
  });
658
673
  }
659
- var ref2;
674
+ var _findFallback;
660
675
  // Try to find a fallback plugin, because we already know that multiple plugins matched for
661
676
  // the given arbitrary value.
662
- let fallback = (ref2 = findFallback(withoutAny)) !== null && ref2 !== void 0 ? ref2 : findFallback(withAny);
677
+ let fallback = (_findFallback = findFallback(withoutAny)) !== null && _findFallback !== void 0 ? _findFallback : findFallback(withAny);
663
678
  if (fallback) {
664
679
  matches = [
665
680
  fallback
666
681
  ];
667
682
  } else {
668
- var ref3;
683
+ var _typesByMatches_get;
669
684
  let typesPerPlugin = matches.map((match)=>new Set([
670
- ...(ref3 = typesByMatches.get(match)) !== null && ref3 !== void 0 ? ref3 : []
685
+ ...(_typesByMatches_get = typesByMatches.get(match)) !== null && _typesByMatches_get !== void 0 ? _typesByMatches_get : []
671
686
  ]));
672
687
  // Remove duplicates, so that we can detect proper unique types for each plugin.
673
688
  for (let pluginTypes of typesPerPlugin){
@@ -718,31 +733,71 @@ function* resolveMatches(candidate, context, original = candidate) {
718
733
  candidate
719
734
  };
720
735
  // Apply final format selector
721
- if (match[0].collectedFormats) {
722
- let finalFormat = (0, _formatVariantSelector.formatVariantSelector)("&", ...match[0].collectedFormats);
723
- let container = _postcss.default.root({
724
- nodes: [
725
- match[1].clone()
726
- ]
727
- });
728
- container.walkRules((rule)=>{
729
- var ref;
730
- if (inKeyframes(rule)) return;
731
- var ref1;
732
- rule.selector = (0, _formatVariantSelector.finalizeSelector)(finalFormat, {
733
- selector: rule.selector,
734
- candidate: original,
735
- base: candidate.split(new RegExp(`\\${(ref1 = context === null || context === void 0 ? void 0 : (ref = context.tailwindConfig) === null || ref === void 0 ? void 0 : ref.separator) !== null && ref1 !== void 0 ? ref1 : ":"}(?![^[]*\\])`)).pop(),
736
- isArbitraryVariant: match[0].isArbitraryVariant,
737
- context
738
- });
739
- });
740
- match[1] = container.nodes[0];
736
+ match = applyFinalFormat(match, {
737
+ context,
738
+ candidate,
739
+ original
740
+ });
741
+ // Skip rules with invalid selectors
742
+ // This will cause the candidate to be added to the "not class"
743
+ // cache skipping it entirely for future builds
744
+ if (match === null) {
745
+ continue;
741
746
  }
742
747
  yield match;
743
748
  }
744
749
  }
745
750
  }
751
+ function applyFinalFormat(match, { context , candidate , original }) {
752
+ if (!match[0].collectedFormats) {
753
+ return match;
754
+ }
755
+ let isValid = true;
756
+ let finalFormat;
757
+ try {
758
+ finalFormat = (0, _formatVariantSelector.formatVariantSelector)(match[0].collectedFormats, {
759
+ context,
760
+ candidate
761
+ });
762
+ } catch {
763
+ // The format selector we produced is invalid
764
+ // This could be because:
765
+ // - A bug exists
766
+ // - A plugin introduced an invalid variant selector (ex: `addVariant('foo', '&;foo')`)
767
+ // - The user used an invalid arbitrary variant (ex: `[&;foo]:underline`)
768
+ // Either way the build will fail because of this
769
+ // We would rather that the build pass "silently" given that this could
770
+ // happen because of picking up invalid things when scanning content
771
+ // So we'll throw out the candidate instead
772
+ return null;
773
+ }
774
+ let container = _postcss.default.root({
775
+ nodes: [
776
+ match[1].clone()
777
+ ]
778
+ });
779
+ container.walkRules((rule)=>{
780
+ if (inKeyframes(rule)) {
781
+ return;
782
+ }
783
+ try {
784
+ rule.selector = (0, _formatVariantSelector.finalizeSelector)(rule.selector, finalFormat, {
785
+ candidate: original,
786
+ context
787
+ });
788
+ } catch {
789
+ // If this selector is invalid we also want to skip it
790
+ // But it's likely that being invalid here means there's a bug in a plugin rather than too loosely matching content
791
+ isValid = false;
792
+ return false;
793
+ }
794
+ });
795
+ if (!isValid) {
796
+ return null;
797
+ }
798
+ match[1] = container.nodes[0];
799
+ return match;
800
+ }
746
801
  function inKeyframes(rule) {
747
802
  return rule.parent && rule.parent.type === "atrule" && rule.parent.name === "keyframes";
748
803
  }
@@ -787,8 +842,8 @@ function generateRules(candidates, context) {
787
842
  continue;
788
843
  }
789
844
  context.classCache.set(candidate, matches);
790
- var ref;
791
- let rules = (ref = context.candidateRuleCache.get(candidate)) !== null && ref !== void 0 ? ref : new Set();
845
+ var _context_candidateRuleCache_get;
846
+ let rules = (_context_candidateRuleCache_get = context.candidateRuleCache.get(candidate)) !== null && _context_candidateRuleCache_get !== void 0 ? _context_candidateRuleCache_get : new Set();
792
847
  context.candidateRuleCache.set(candidate, rules);
793
848
  for (const match of matches){
794
849
  let [{ sort , options }, rule] = match;
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "Offsets", {
8
8
  get: ()=>Offsets
9
9
  });
10
10
  const _bigSign = /*#__PURE__*/ _interopRequireDefault(require("../util/bigSign"));
11
+ const _remapBitfieldJs = require("./remap-bitfield.js");
11
12
  function _interopRequireDefault(obj) {
12
13
  return obj && obj.__esModule ? obj : {
13
14
  default: obj
@@ -96,6 +97,7 @@ class Offsets {
96
97
  * @param {VariantOption} options
97
98
  * @returns {RuleOffset}
98
99
  */ applyVariantOffset(rule, variant, options) {
100
+ options.variant = variant.variants;
99
101
  return {
100
102
  ...rule,
101
103
  layer: "variants",
@@ -153,7 +155,7 @@ class Offsets {
153
155
  this.reservedVariantBits += BigInt(fnCount);
154
156
  return {
155
157
  ...this.create("variants"),
156
- variants: 1n << this.reservedVariantBits
158
+ variants: this.variantOffsets.get(variant)
157
159
  };
158
160
  }
159
161
  /**
@@ -165,11 +167,29 @@ class Offsets {
165
167
  if (a.layer !== b.layer) {
166
168
  return this.layerPositions[a.layer] - this.layerPositions[b.layer];
167
169
  }
170
+ // When sorting the `variants` layer, we need to sort based on the parent layer as well within
171
+ // this variants layer.
172
+ if (a.parentLayer !== b.parentLayer) {
173
+ return this.layerPositions[a.parentLayer] - this.layerPositions[b.parentLayer];
174
+ }
168
175
  // Sort based on the sorting function
169
176
  for (let aOptions of a.options){
170
177
  for (let bOptions of b.options){
171
178
  if (aOptions.id !== bOptions.id) continue;
172
179
  if (!aOptions.sort || !bOptions.sort) continue;
180
+ var _max;
181
+ let maxFnVariant = (_max = max([
182
+ aOptions.variant,
183
+ bOptions.variant
184
+ ])) !== null && _max !== void 0 ? _max : 0n;
185
+ // Create a mask of 0s from bits 1..N where N represents the mask of the Nth bit
186
+ let mask = ~(maxFnVariant | maxFnVariant - 1n);
187
+ let aVariantsAfterFn = a.variants & mask;
188
+ let bVariantsAfterFn = b.variants & mask;
189
+ // If the variants the same, we _can_ sort them
190
+ if (aVariantsAfterFn !== bVariantsAfterFn) {
191
+ continue;
192
+ }
173
193
  let result = aOptions.sort({
174
194
  value: aOptions.value,
175
195
  modifier: aOptions.modifier
@@ -196,10 +216,57 @@ class Offsets {
196
216
  return a.index - b.index;
197
217
  }
198
218
  /**
219
+ * Arbitrary variants are recorded in the order they're encountered.
220
+ * This means that the order is not stable between environments and sets of content files.
221
+ *
222
+ * In order to make the order stable, we need to remap the arbitrary variant offsets to
223
+ * be in alphabetical order starting from the offset of the first arbitrary variant.
224
+ */ recalculateVariantOffsets() {
225
+ // Sort the variants by their name
226
+ let variants = Array.from(this.variantOffsets.entries()).filter(([v])=>v.startsWith("[")).sort(([a], [z])=>fastCompare(a, z));
227
+ // Sort the list of offsets
228
+ // This is not necessarily a discrete range of numbers which is why
229
+ // we're using sort instead of creating a range from min/max
230
+ let newOffsets = variants.map(([, offset])=>offset).sort((a, z)=>(0, _bigSign.default)(a - z));
231
+ // Create a map from the old offsets to the new offsets in the new sort order
232
+ /** @type {[bigint, bigint][]} */ let mapping = variants.map(([, oldOffset], i)=>[
233
+ oldOffset,
234
+ newOffsets[i]
235
+ ]);
236
+ // Remove any variants that will not move letting us skip
237
+ // remapping if everything happens to be in order
238
+ return mapping.filter(([a, z])=>a !== z);
239
+ }
240
+ /**
241
+ * @template T
242
+ * @param {[RuleOffset, T][]} list
243
+ * @returns {[RuleOffset, T][]}
244
+ */ remapArbitraryVariantOffsets(list) {
245
+ let mapping = this.recalculateVariantOffsets();
246
+ // No arbitrary variants? Nothing to do.
247
+ // Everyhing already in order? Nothing to do.
248
+ if (mapping.length === 0) {
249
+ return list;
250
+ }
251
+ // Remap every variant offset in the list
252
+ return list.map((item)=>{
253
+ let [offset, rule] = item;
254
+ offset = {
255
+ ...offset,
256
+ variants: (0, _remapBitfieldJs.remapBitfield)(offset.variants, mapping)
257
+ };
258
+ return [
259
+ offset,
260
+ rule
261
+ ];
262
+ });
263
+ }
264
+ /**
199
265
  * @template T
200
266
  * @param {[RuleOffset, T][]} list
201
267
  * @returns {[RuleOffset, T][]}
202
268
  */ sort(list) {
269
+ list = this.remapArbitraryVariantOffsets(list);
203
270
  return list.sort(([a], [b])=>(0, _bigSign.default)(this.compare(a, b)));
204
271
  }
205
272
  }
@@ -215,3 +282,23 @@ class Offsets {
215
282
  }
216
283
  return max;
217
284
  }
285
+ /**
286
+ * A fast ASCII order string comparison function.
287
+ *
288
+ * Using `.sort()` without a custom compare function is faster
289
+ * But you can only use that if you're sorting an array of
290
+ * only strings. If you're sorting strings inside objects
291
+ * or arrays, you need must use a custom compare function.
292
+ *
293
+ * @param {string} a
294
+ * @param {string} b
295
+ */ function fastCompare(a, b) {
296
+ let aLen = a.length;
297
+ let bLen = b.length;
298
+ let minLen = aLen < bLen ? aLen : bLen;
299
+ for(let i = 0; i < minLen; i++){
300
+ let cmp = a.charCodeAt(i) - b.charCodeAt(i);
301
+ if (cmp !== 0) return cmp;
302
+ }
303
+ return aLen - bLen;
304
+ }
@@ -0,0 +1,87 @@
1
+ // @ts-check
2
+ /**
3
+ * We must remap all the old bits to new bits for each set variant
4
+ * Only arbitrary variants are considered as those are the only
5
+ * ones that need to be re-sorted at this time
6
+ *
7
+ * An iterated process that removes and sets individual bits simultaneously
8
+ * will not work because we may have a new bit that is also a later old bit
9
+ * This means that we would be removing a previously set bit which we don't
10
+ * want to do
11
+ *
12
+ * For example (assume `bN` = `1<<N`)
13
+ * Given the "total" mapping `[[b1, b3], [b2, b4], [b3, b1], [b4, b2]]`
14
+ * The mapping is "total" because:
15
+ * 1. Every input and output is accounted for
16
+ * 2. All combinations are unique
17
+ * 3. No one input maps to multiple outputs and vice versa
18
+ * And, given an offset with all bits set:
19
+ * V = b1 | b2 | b3 | b4
20
+ *
21
+ * Let's explore the issue with removing and setting bits simultaneously:
22
+ * V & ~b1 | b3 = b2 | b3 | b4
23
+ * V & ~b2 | b4 = b3 | b4
24
+ * V & ~b3 | b1 = b1 | b4
25
+ * V & ~b4 | b2 = b1 | b2
26
+ *
27
+ * As you can see, we end up with the wrong result.
28
+ * This is because we're removing a bit that was previously set.
29
+ * And, thus the final result is missing b3 and b4.
30
+ *
31
+ * Now, let's explore the issue with removing the bits first:
32
+ * V & ~b1 = b2 | b3 | b4
33
+ * V & ~b2 = b3 | b4
34
+ * V & ~b3 = b4
35
+ * V & ~b4 = 0
36
+ *
37
+ * And then setting the bits:
38
+ * V | b3 = b3
39
+ * V | b4 = b3 | b4
40
+ * V | b1 = b1 | b3 | b4
41
+ * V | b2 = b1 | b2 | b3 | b4
42
+ *
43
+ * We get the correct result because we're not removing any bits that were
44
+ * previously set thus properly remapping the bits to the new order
45
+ *
46
+ * To collect this into a single operation that can be done simultaneously
47
+ * we must first create a mask for the old bits that are set and a mask for
48
+ * the new bits that are set. Then we can remove the old bits and set the new
49
+ * bits simultaneously in a "single" operation like so:
50
+ * OldMask = b1 | b2 | b3 | b4
51
+ * NewMask = b3 | b4 | b1 | b2
52
+ *
53
+ * So this:
54
+ * V & ~oldMask | newMask
55
+ *
56
+ * Expands to this:
57
+ * V & ~b1 & ~b2 & ~b3 & ~b4 | b3 | b4 | b1 | b2
58
+ *
59
+ * Which becomes this:
60
+ * b1 | b2 | b3 | b4
61
+ *
62
+ * Which is the correct result!
63
+ *
64
+ * @param {bigint} num
65
+ * @param {[bigint, bigint][]} mapping
66
+ */ "use strict";
67
+ Object.defineProperty(exports, "__esModule", {
68
+ value: true
69
+ });
70
+ Object.defineProperty(exports, "remapBitfield", {
71
+ enumerable: true,
72
+ get: ()=>remapBitfield
73
+ });
74
+ function remapBitfield(num, mapping) {
75
+ // Create masks for the old and new bits that are set
76
+ let oldMask = 0n;
77
+ let newMask = 0n;
78
+ for (let [oldBit, newBit] of mapping){
79
+ if (num & oldBit) {
80
+ oldMask = oldMask | oldBit;
81
+ newMask = newMask | newBit;
82
+ }
83
+ }
84
+ // Remove all old bits
85
+ // Set all new bits
86
+ return num & ~oldMask | newMask;
87
+ }
@@ -99,8 +99,8 @@ function resolveDefaultsAtRules({ tailwindConfig }) {
99
99
  if ((0, _featureFlags.flagEnabled)(tailwindConfig, "optimizeUniversalDefaults")) {
100
100
  for (let universal of universals){
101
101
  /** @type {Map<string, Set<string>>} */ let selectorGroups = new Map();
102
- var ref;
103
- let rules = (ref = variableNodeMap.get(universal.params)) !== null && ref !== void 0 ? ref : [];
102
+ var _variableNodeMap_get;
103
+ let rules = (_variableNodeMap_get = variableNodeMap.get(universal.params)) !== null && _variableNodeMap_get !== void 0 ? _variableNodeMap_get : [];
104
104
  for (let rule of rules){
105
105
  for (let selector of extractElementSelector(rule.selector)){
106
106
  // If selector contains a vendor prefix after a pseudo element or class,
@@ -108,8 +108,8 @@ function resolveDefaultsAtRules({ tailwindConfig }) {
108
108
  // a single rule will cause browsers that do not understand the
109
109
  // vendor prefix to throw out the whole rule
110
110
  let selectorGroupName = selector.includes(":-") || selector.includes("::-") ? selector : "__DEFAULT__";
111
- var ref1;
112
- let selectors = (ref1 = selectorGroups.get(selectorGroupName)) !== null && ref1 !== void 0 ? ref1 : new Set();
111
+ var _selectorGroups_get;
112
+ let selectors = (_selectorGroups_get = selectorGroups.get(selectorGroupName)) !== null && _selectorGroups_get !== void 0 ? _selectorGroups_get : new Set();
113
113
  selectorGroups.set(selectorGroupName, selectors);
114
114
  selectors.add(selector);
115
115
  }