tailwindcss 3.1.0 → 3.1.3

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.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  - Nothing yet!
11
11
 
12
+ ## [3.1.3] - 2022-06-14
13
+
14
+ ### Fixed
15
+
16
+ - Fix extraction of multi-word utilities with arbitrary values and quotes ([#8604](https://github.com/tailwindlabs/tailwindcss/pull/8604))
17
+ - Fix casing of import of `corePluginList` type definition ([#8587](https://github.com/tailwindlabs/tailwindcss/pull/8587))
18
+ - Ignore PostCSS nodes returned by `addVariant` ([#8608](https://github.com/tailwindlabs/tailwindcss/pull/8608))
19
+ - Fix missing spaces around arithmetic operators ([#8615](https://github.com/tailwindlabs/tailwindcss/pull/8615))
20
+ - Detect alpha value in CSS `theme()` function when using quotes ([#8625](https://github.com/tailwindlabs/tailwindcss/pull/8625))
21
+ - Fix "Maximum call stack size exceeded" bug ([#8636](https://github.com/tailwindlabs/tailwindcss/pull/8636))
22
+ - Allow functions returning parallel variants to mutate the container ([#8622](https://github.com/tailwindlabs/tailwindcss/pull/8622))
23
+ - Remove text opacity CSS variables from `::marker` ([#8622](https://github.com/tailwindlabs/tailwindcss/pull/8622))
24
+
25
+ ## [3.1.2] - 2022-06-10
26
+
27
+ ### Fixed
28
+
29
+ - Ensure `\` is a valid arbitrary variant token ([#8576](https://github.com/tailwindlabs/tailwindcss/pull/8576))
30
+ - Enable `postcss-import` in the CLI by default in watch mode ([#8574](https://github.com/tailwindlabs/tailwindcss/pull/8574), [#8580](https://github.com/tailwindlabs/tailwindcss/pull/8580))
31
+
32
+ ## [3.1.1] - 2022-06-09
33
+
34
+ ### Fixed
35
+
36
+ - Fix candidate extractor regression ([#8558](https://github.com/tailwindlabs/tailwindcss/pull/8558))
37
+ - Split `::backdrop` into separate defaults group ([#8567](https://github.com/tailwindlabs/tailwindcss/pull/8567))
38
+ - Fix postcss plugin type ([#8564](https://github.com/tailwindlabs/tailwindcss/pull/8564))
39
+ - Fix class detection in markdown code fences and slim templates ([#8569](https://github.com/tailwindlabs/tailwindcss/pull/8569))
40
+
12
41
  ## [3.1.0] - 2022-06-08
13
42
 
14
43
  ### Fixed
@@ -1952,7 +1981,10 @@ No release notes
1952
1981
 
1953
1982
  - Everything!
1954
1983
 
1955
- [unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.1.0...HEAD
1984
+ [unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v3.1.3...HEAD
1985
+ [3.1.3]: https://github.com/tailwindlabs/tailwindcss/compare/v3.1.2...v3.1.3
1986
+ [3.1.2]: https://github.com/tailwindlabs/tailwindcss/compare/v3.1.1...v3.1.2
1987
+ [3.1.1]: https://github.com/tailwindlabs/tailwindcss/compare/v3.1.0...v3.1.1
1956
1988
  [3.1.0]: https://github.com/tailwindlabs/tailwindcss/compare/v3.0.24...v3.1.0
1957
1989
  [3.0.24]: https://github.com/tailwindlabs/tailwindcss/compare/v3.0.23...v3.0.24
1958
1990
  [3.0.23]: https://github.com/tailwindlabs/tailwindcss/compare/v3.0.22...v3.0.23
package/lib/cli.js CHANGED
@@ -428,6 +428,43 @@ async function build() {
428
428
  config1.options
429
429
  ];
430
430
  }
431
+ function loadBuiltinPostcssPlugins() {
432
+ let postcss = loadPostcss();
433
+ let IMPORT_COMMENT = "__TAILWIND_RESTORE_IMPORT__: ";
434
+ return [
435
+ [
436
+ (root)=>{
437
+ root.walkAtRules("import", (rule)=>{
438
+ if (rule.params.slice(1).startsWith("tailwindcss/")) {
439
+ rule.after(postcss.comment({
440
+ text: IMPORT_COMMENT + rule.params
441
+ }));
442
+ rule.remove();
443
+ }
444
+ });
445
+ },
446
+ (()=>{
447
+ try {
448
+ return require("postcss-import");
449
+ } catch {}
450
+ return (0, _indexJs).lazyPostcssImport();
451
+ })(),
452
+ (root)=>{
453
+ root.walkComments((rule)=>{
454
+ if (rule.text.startsWith(IMPORT_COMMENT)) {
455
+ rule.after(postcss.atRule({
456
+ name: "import",
457
+ params: rule.text.replace(IMPORT_COMMENT, "")
458
+ }));
459
+ rule.remove();
460
+ }
461
+ });
462
+ },
463
+ ],
464
+ [],
465
+ {},
466
+ ];
467
+ }
431
468
  function resolveConfig() {
432
469
  let config = configPath ? require(configPath) : {};
433
470
  if (args["--purge"]) {
@@ -503,40 +540,7 @@ async function build() {
503
540
  };
504
541
  };
505
542
  tailwindPlugin.postcss = true;
506
- let IMPORT_COMMENT = "__TAILWIND_RESTORE_IMPORT__: ";
507
- let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins() : [
508
- [
509
- (root)=>{
510
- root.walkAtRules("import", (rule)=>{
511
- if (rule.params.slice(1).startsWith("tailwindcss/")) {
512
- rule.after(postcss.comment({
513
- text: IMPORT_COMMENT + rule.params
514
- }));
515
- rule.remove();
516
- }
517
- });
518
- },
519
- (()=>{
520
- try {
521
- return require("postcss-import");
522
- } catch {}
523
- return (0, _indexJs).lazyPostcssImport();
524
- })(),
525
- (root)=>{
526
- root.walkComments((rule)=>{
527
- if (rule.text.startsWith(IMPORT_COMMENT)) {
528
- rule.after(postcss.atRule({
529
- name: "import",
530
- params: rule.text.replace(IMPORT_COMMENT, "")
531
- }));
532
- rule.remove();
533
- }
534
- });
535
- },
536
- ],
537
- [],
538
- {},
539
- ];
543
+ let [beforePlugins, afterPlugins, postcssOptions] = includePostCss ? await loadPostCssPlugins() : loadBuiltinPostcssPlugins();
540
544
  let plugins = [
541
545
  ...beforePlugins,
542
546
  tailwindPlugin,
@@ -623,10 +627,7 @@ async function build() {
623
627
  env.DEBUG && console.timeEnd("Module dependencies");
624
628
  return resolveConfig();
625
629
  }
626
- let [beforePlugins, afterPlugins] = includePostCss ? await loadPostCssPlugins() : [
627
- [],
628
- []
629
- ];
630
+ let [beforePlugins, afterPlugins] = includePostCss ? await loadPostCssPlugins() : loadBuiltinPostcssPlugins();
630
631
  let plugins = [
631
632
  ...beforePlugins,
632
633
  "__TAILWIND_PLUGIN_POSITION__",
@@ -19,6 +19,7 @@ var _packageJson = require("../package.json");
19
19
  var _log = _interopRequireDefault(require("./util/log"));
20
20
  var _normalizeScreens = require("./util/normalizeScreens");
21
21
  var _parseBoxShadowValue = require("./util/parseBoxShadowValue");
22
+ var _removeAlphaVariables = require("./util/removeAlphaVariables");
22
23
  var _featureFlags = require("./featureFlags");
23
24
  function _interopRequireDefault(obj) {
24
25
  return obj && obj.__esModule ? obj : {
@@ -69,8 +70,18 @@ let variantPlugins = {
69
70
  addVariant("first-letter", "&::first-letter");
70
71
  addVariant("first-line", "&::first-line");
71
72
  addVariant("marker", [
72
- "& *::marker",
73
- "&::marker"
73
+ ({ container })=>{
74
+ (0, _removeAlphaVariables).removeAlphaVariables(container, [
75
+ "--tw-text-opacity"
76
+ ]);
77
+ return "& *::marker";
78
+ },
79
+ ({ container })=>{
80
+ (0, _removeAlphaVariables).removeAlphaVariables(container, [
81
+ "--tw-text-opacity"
82
+ ]);
83
+ return "&::marker";
84
+ },
74
85
  ]);
75
86
  addVariant("selection", [
76
87
  "& *::selection",
@@ -140,22 +151,11 @@ let variantPlugins = {
140
151
  [
141
152
  "visited",
142
153
  ({ container })=>{
143
- let toRemove = [
154
+ (0, _removeAlphaVariables).removeAlphaVariables(container, [
144
155
  "--tw-text-opacity",
145
156
  "--tw-border-opacity",
146
- "--tw-bg-opacity"
147
- ];
148
- container.walkDecls((decl)=>{
149
- if (toRemove.includes(decl.prop)) {
150
- decl.remove();
151
- return;
152
- }
153
- for (const varName of toRemove){
154
- if (decl.value.includes(`/ var(${varName})`)) {
155
- decl.value = decl.value.replace(`/ var(${varName})`, "");
156
- }
157
- }
158
- });
157
+ "--tw-bg-opacity",
158
+ ]);
159
159
  return "&:visited";
160
160
  },
161
161
  ],
@@ -52,7 +52,10 @@ function defaultExtractor(context) {
52
52
  /** @type {(string|string)[]} */ let results = [];
53
53
  for (let pattern of patterns){
54
54
  var ref;
55
- results.push(...(ref = content.match(pattern)) !== null && ref !== void 0 ? ref : []);
55
+ results = [
56
+ ...results,
57
+ ...(ref = content.match(pattern)) !== null && ref !== void 0 ? ref : []
58
+ ];
56
59
  }
57
60
  return results.filter((v)=>v !== undefined).map(clipAtBalancedParens);
58
61
  };
@@ -62,7 +65,7 @@ function* buildRegExps(context) {
62
65
  let variantGroupingEnabled = (0, _featureFlagsJs).flagEnabled(context.tailwindConfig, "variantGrouping");
63
66
  let utility = regex.any([
64
67
  // Arbitrary properties
65
- /\[[^\s:'"]+:[^\s\]]+\]/,
68
+ /\[[^\s:'"`]+:[^\s\]]+\]/,
66
69
  // Utilities
67
70
  regex.pattern([
68
71
  // Utility Name / Group Name
@@ -71,22 +74,22 @@ function* buildRegExps(context) {
71
74
  regex.optional(regex.any([
72
75
  regex.pattern([
73
76
  // Arbitrary values
74
- /-\[[^\s:]+\]/,
77
+ /-(?:\w+-)*\[[^\s:]+\]/,
75
78
  // Not immediately followed by an `{[(`
76
79
  /(?![{([]])/,
77
80
  // optionally followed by an opacity modifier
78
- /(?:\/[^\s'"\\$]*)?/,
81
+ /(?:\/[^\s'"`\\><$]*)?/,
79
82
  ]),
80
83
  regex.pattern([
81
84
  // Arbitrary values
82
- /-\[[^\s]+\]/,
85
+ /-(?:\w+-)*\[[^\s]+\]/,
83
86
  // Not immediately followed by an `{[(`
84
87
  /(?![{([]])/,
85
88
  // optionally followed by an opacity modifier
86
- /(?:\/[^\s'"\\$]*)?/,
89
+ /(?:\/[^\s'"`\\$]*)?/,
87
90
  ]),
88
91
  // Normal values w/o quotes — may include an opacity modifier
89
- /[-\/][^\s'"\\$={]*/,
92
+ /[-\/][^\s'"`\\$={><]*/,
90
93
  ])),
91
94
  ]),
92
95
  ]);
@@ -95,11 +98,11 @@ function* buildRegExps(context) {
95
98
  "((?=((",
96
99
  regex.any([
97
100
  regex.pattern([
98
- /([^\s"'\[\\]+-)?\[[^\s"'\\]+\]/,
101
+ /([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/,
99
102
  separator
100
103
  ]),
101
104
  regex.pattern([
102
- /[^\s"'\[\\]+/,
105
+ /[^\s"'`\[\\]+/,
103
106
  separator
104
107
  ]),
105
108
  ], true),
@@ -121,8 +124,8 @@ function* buildRegExps(context) {
121
124
  utility,
122
125
  ]) : utility,
123
126
  ]);
124
- // 5. Inner matches
125
- // yield /[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g
127
+ // 5. Inner matches
128
+ yield /[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g;
126
129
  }
127
130
  // We want to capture any "special" characters
128
131
  // AND the characters immediately following them (if there is one)
@@ -42,7 +42,7 @@ function listKeys(obj) {
42
42
  return list(Object.keys(obj));
43
43
  }
44
44
  function validatePath(config, path, defaultValue, themeOpts = {}) {
45
- const pathString = Array.isArray(path) ? pathToString(path) : path.replace(/^['"]+/g, "").replace(/['"]+$/g, "");
45
+ const pathString = Array.isArray(path) ? pathToString(path) : path.replace(/^['"]+|['"]+$/g, "");
46
46
  const pathSegments = Array.isArray(path) ? path : (0, _toPath).toPath(pathString);
47
47
  const value = (0, _dlv).default(config.theme, pathSegments, defaultValue);
48
48
  if (value === undefined) {
@@ -140,7 +140,10 @@ let nodeTypePropertyMap = {
140
140
  function _default({ tailwindConfig: config }) {
141
141
  let functions = {
142
142
  theme: (node, path, ...defaultValue)=>{
143
- let matches = path.match(/^([^\/\s]+)(?:\s*\/\s*([^\/\s]+))$/);
143
+ // Strip quotes from beginning and end of string
144
+ // This allows the alpha value to be present inside of quotes
145
+ path = path.replace(/^['"]+|['"]+$/g, "");
146
+ let matches = path.match(/^([^\s]+)(?![^\[]*\])(?:\s*\/\s*([^\/\s]+))$/);
144
147
  let alpha = undefined;
145
148
  if (matches) {
146
149
  path = matches[1];
@@ -205,13 +205,16 @@ function applyVariant(variant, matches, context) {
205
205
  rule1.clone()
206
206
  ]
207
207
  });
208
- for (let [variantSort, variantFunction] of variantFunctionTuples){
209
- let clone = container.clone();
208
+ for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples){
209
+ let clone = containerFromArray !== null && containerFromArray !== void 0 ? containerFromArray : container.clone();
210
210
  let collectedFormats = [];
211
- let originals = new Map();
212
211
  function prepareBackup() {
213
- if (originals.size > 0) return; // Already prepared, chicken out
214
- clone.walkRules((rule)=>originals.set(rule, rule.selector));
212
+ // Already prepared, chicken out
213
+ if (clone.raws.neededBackup) {
214
+ return;
215
+ }
216
+ clone.raws.neededBackup = true;
217
+ clone.walkRules((rule)=>rule.raws.originalSelector = rule.selector);
215
218
  }
216
219
  function modifySelectors(modifierFunction) {
217
220
  prepareBackup();
@@ -264,7 +267,10 @@ function applyVariant(variant, matches, context) {
264
267
  // then this might be the place too look at. One potential solution to this problem is
265
268
  // reserving additional X places for these 'unknown' variants in between.
266
269
  variantSort | BigInt(idx << ruleWithVariant.length),
267
- variantFunction,
270
+ variantFunction,
271
+ // If the clone has been modified we have to pass that back
272
+ // though so each rule can use the modified container
273
+ clone.clone(),
268
274
  ]);
269
275
  }
270
276
  continue;
@@ -275,13 +281,15 @@ function applyVariant(variant, matches, context) {
275
281
  if (ruleWithVariant === null) {
276
282
  continue;
277
283
  }
278
- // We filled the `originals`, therefore we assume that somebody touched
284
+ // We had to backup selectors, therefore we assume that somebody touched
279
285
  // `container` or `modifySelectors`. Let's see if they did, so that we
280
286
  // can restore the selectors, and collect the format strings.
281
- if (originals.size > 0) {
287
+ if (clone.raws.neededBackup) {
288
+ delete clone.raws.neededBackup;
282
289
  clone.walkRules((rule)=>{
283
- if (!originals.has(rule)) return;
284
- let before = originals.get(rule);
290
+ let before = rule.raws.originalSelector;
291
+ if (!before) return;
292
+ delete rule.raws.originalSelector;
285
293
  if (before === rule.selector) return; // No mutation happened
286
294
  let modified = rule.selector;
287
295
  // Rebuild the base selector, this is what plugin authors would do
@@ -23,52 +23,66 @@ function resolveDefaultsAtRules({ tailwindConfig }) {
23
23
  variableNodeMap.get(variable).add(rule.parent);
24
24
  rule.remove();
25
25
  });
26
- for (let universal of universals){
27
- /** @type {Map<string, Set<string>>} */ let selectorGroups = new Map();
28
- var ref;
29
- let rules = (ref = variableNodeMap.get(universal.params)) !== null && ref !== void 0 ? ref : [];
30
- for (let rule of rules){
31
- for (let selector of extractElementSelector(rule.selector)){
32
- // If selector contains a vendor prefix after a pseudo element or class,
33
- // we consider them separately because merging the declarations into
34
- // a single rule will cause browsers that do not understand the
35
- // vendor prefix to throw out the whole rule
36
- let selectorGroupName = selector.includes(":-") || selector.includes("::-") ? selector : "__DEFAULT__";
37
- var ref1;
38
- let selectors = (ref1 = selectorGroups.get(selectorGroupName)) !== null && ref1 !== void 0 ? ref1 : new Set();
39
- selectorGroups.set(selectorGroupName, selectors);
40
- selectors.add(selector);
26
+ if ((0, _featureFlags).flagEnabled(tailwindConfig, "optimizeUniversalDefaults")) {
27
+ for (let universal of universals){
28
+ /** @type {Map<string, Set<string>>} */ let selectorGroups = new Map();
29
+ var ref;
30
+ let rules = (ref = variableNodeMap.get(universal.params)) !== null && ref !== void 0 ? ref : [];
31
+ for (let rule of rules){
32
+ for (let selector of extractElementSelector(rule.selector)){
33
+ // If selector contains a vendor prefix after a pseudo element or class,
34
+ // we consider them separately because merging the declarations into
35
+ // a single rule will cause browsers that do not understand the
36
+ // vendor prefix to throw out the whole rule
37
+ let selectorGroupName = selector.includes(":-") || selector.includes("::-") ? selector : "__DEFAULT__";
38
+ var ref1;
39
+ let selectors = (ref1 = selectorGroups.get(selectorGroupName)) !== null && ref1 !== void 0 ? ref1 : new Set();
40
+ selectorGroups.set(selectorGroupName, selectors);
41
+ selectors.add(selector);
42
+ }
41
43
  }
42
- }
43
- if ((0, _featureFlags).flagEnabled(tailwindConfig, "optimizeUniversalDefaults")) {
44
- if (selectorGroups.size === 0) {
45
- universal.remove();
46
- continue;
47
- }
48
- for (let [, selectors] of selectorGroups){
49
- let universalRule = _postcss.default.rule({
50
- source: universal.source
51
- });
52
- universalRule.selectors = [
53
- ...selectors
54
- ];
55
- universalRule.append(universal.nodes.map((node)=>node.clone()));
56
- universal.before(universalRule);
44
+ if ((0, _featureFlags).flagEnabled(tailwindConfig, "optimizeUniversalDefaults")) {
45
+ if (selectorGroups.size === 0) {
46
+ universal.remove();
47
+ continue;
48
+ }
49
+ for (let [, selectors] of selectorGroups){
50
+ let universalRule = _postcss.default.rule({
51
+ source: universal.source
52
+ });
53
+ universalRule.selectors = [
54
+ ...selectors
55
+ ];
56
+ universalRule.append(universal.nodes.map((node)=>node.clone()));
57
+ universal.before(universalRule);
58
+ }
57
59
  }
58
- } else {
59
- let universalRule = _postcss.default.rule({
60
- source: universal.source
61
- });
62
- universalRule.selectors = [
60
+ universal.remove();
61
+ }
62
+ } else if (universals.size) {
63
+ let universalRule = _postcss.default.rule({
64
+ selectors: [
63
65
  "*",
64
66
  "::before",
65
- "::after",
66
- "::backdrop"
67
- ];
67
+ "::after"
68
+ ]
69
+ });
70
+ for (let universal of universals){
68
71
  universalRule.append(universal.nodes);
69
- universal.before(universalRule);
72
+ if (!universalRule.parent) {
73
+ universal.before(universalRule);
74
+ }
75
+ if (!universalRule.source) {
76
+ universalRule.source = universal.source;
77
+ }
78
+ universal.remove();
70
79
  }
71
- universal.remove();
80
+ let backdropRule = universalRule.clone({
81
+ selectors: [
82
+ "::backdrop"
83
+ ]
84
+ });
85
+ universalRule.after(backdropRule);
72
86
  }
73
87
  };
74
88
  }
@@ -478,10 +478,11 @@ function buildPluginApi(tailwindConfig, context, { variantList , variantMap , of
478
478
  throw new Error(`Your custom variant \`${variantName}\` has an invalid format string. Make sure it's an at-rule or contains a \`&\` placeholder.`);
479
479
  }
480
480
  if (Array.isArray(result)) {
481
- return result.map((variant)=>parseVariant(variant));
481
+ return result.filter((variant)=>typeof variant === "string").map((variant)=>parseVariant(variant));
482
482
  }
483
483
  // result may be undefined with legacy variants that use APIs like `modifySelectors`
484
- return result && parseVariant(result)(api);
484
+ // result may also be a postcss node if someone was returning the result from `modifySelectors`
485
+ return result && typeof result === "string" && parseVariant(result)(api);
485
486
  };
486
487
  }
487
488
  if (!isValidVariantFormatString(variantFunction)) {
@@ -46,9 +46,9 @@ function normalize(value, isRoot = true) {
46
46
  if (isRoot) {
47
47
  value = value.trim();
48
48
  }
49
- // Add spaces around operators inside calc() that do not follow an operator
49
+ // Add spaces around operators inside math functions like calc() that do not follow an operator
50
50
  // or '('.
51
- value = value.replace(/calc\(.+\)/g, (match)=>{
51
+ value = value.replace(/(calc|min|max|clamp)\(.+\)/g, (match)=>{
52
52
  return match.replace(/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, "$1 $2 ");
53
53
  });
54
54
  // Add spaces around some operators not inside calc() that do not follow an operator
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ exports.removeAlphaVariables = removeAlphaVariables;
6
+ function removeAlphaVariables(container, toRemove) {
7
+ container.walkDecls((decl)=>{
8
+ if (toRemove.includes(decl.prop)) {
9
+ decl.remove();
10
+ return;
11
+ }
12
+ for (let varName of toRemove){
13
+ if (decl.value.includes(`/ var(${varName})`)) {
14
+ decl.value = decl.value.replace(`/ var(${varName})`, "");
15
+ }
16
+ }
17
+ });
18
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tailwindcss",
3
- "version": "3.1.0",
3
+ "version": "3.1.3",
4
4
  "description": "A utility-first CSS framework for rapidly building custom user interfaces.",
5
5
  "license": "MIT",
6
6
  "main": "lib/index.js",
@@ -52,8 +52,8 @@
52
52
  "eslint": "^8.16.0",
53
53
  "eslint-config-prettier": "^8.5.0",
54
54
  "eslint-plugin-prettier": "^4.0.0",
55
- "jest": "^28.1.0",
56
- "jest-diff": "^28.1.0",
55
+ "jest": "^28.1.1",
56
+ "jest-diff": "^28.1.1",
57
57
  "prettier": "^2.6.2",
58
58
  "prettier-plugin-tailwindcss": "^0.1.11",
59
59
  "rimraf": "^3.0.0",
@@ -63,7 +63,7 @@
63
63
  "postcss": "^8.0.9"
64
64
  },
65
65
  "dependencies": {
66
- "arg": "^5.0.1",
66
+ "arg": "^5.0.2",
67
67
  "chokidar": "^3.5.3",
68
68
  "color-name": "^1.1.4",
69
69
  "detective": "^5.2.1",
package/src/cli.js CHANGED
@@ -484,6 +484,45 @@ async function build() {
484
484
  return [beforePlugins, afterPlugins, config.options]
485
485
  }
486
486
 
487
+ function loadBuiltinPostcssPlugins() {
488
+ let postcss = loadPostcss()
489
+ let IMPORT_COMMENT = '__TAILWIND_RESTORE_IMPORT__: '
490
+ return [
491
+ [
492
+ (root) => {
493
+ root.walkAtRules('import', (rule) => {
494
+ if (rule.params.slice(1).startsWith('tailwindcss/')) {
495
+ rule.after(postcss.comment({ text: IMPORT_COMMENT + rule.params }))
496
+ rule.remove()
497
+ }
498
+ })
499
+ },
500
+ (() => {
501
+ try {
502
+ return require('postcss-import')
503
+ } catch {}
504
+
505
+ return lazyPostcssImport()
506
+ })(),
507
+ (root) => {
508
+ root.walkComments((rule) => {
509
+ if (rule.text.startsWith(IMPORT_COMMENT)) {
510
+ rule.after(
511
+ postcss.atRule({
512
+ name: 'import',
513
+ params: rule.text.replace(IMPORT_COMMENT, ''),
514
+ })
515
+ )
516
+ rule.remove()
517
+ }
518
+ })
519
+ },
520
+ ],
521
+ [],
522
+ {},
523
+ ]
524
+ }
525
+
487
526
  function resolveConfig() {
488
527
  let config = configPath ? require(configPath) : {}
489
528
 
@@ -568,44 +607,9 @@ async function build() {
568
607
 
569
608
  tailwindPlugin.postcss = true
570
609
 
571
- let IMPORT_COMMENT = '__TAILWIND_RESTORE_IMPORT__: '
572
-
573
610
  let [beforePlugins, afterPlugins, postcssOptions] = includePostCss
574
611
  ? await loadPostCssPlugins()
575
- : [
576
- [
577
- (root) => {
578
- root.walkAtRules('import', (rule) => {
579
- if (rule.params.slice(1).startsWith('tailwindcss/')) {
580
- rule.after(postcss.comment({ text: IMPORT_COMMENT + rule.params }))
581
- rule.remove()
582
- }
583
- })
584
- },
585
- (() => {
586
- try {
587
- return require('postcss-import')
588
- } catch {}
589
-
590
- return lazyPostcssImport()
591
- })(),
592
- (root) => {
593
- root.walkComments((rule) => {
594
- if (rule.text.startsWith(IMPORT_COMMENT)) {
595
- rule.after(
596
- postcss.atRule({
597
- name: 'import',
598
- params: rule.text.replace(IMPORT_COMMENT, ''),
599
- })
600
- )
601
- rule.remove()
602
- }
603
- })
604
- },
605
- ],
606
- [],
607
- {},
608
- ]
612
+ : loadBuiltinPostcssPlugins()
609
613
 
610
614
  let plugins = [
611
615
  ...beforePlugins,
@@ -705,7 +709,9 @@ async function build() {
705
709
  return resolveConfig()
706
710
  }
707
711
 
708
- let [beforePlugins, afterPlugins] = includePostCss ? await loadPostCssPlugins() : [[], []]
712
+ let [beforePlugins, afterPlugins] = includePostCss
713
+ ? await loadPostCssPlugins()
714
+ : loadBuiltinPostcssPlugins()
709
715
 
710
716
  let plugins = [
711
717
  ...beforePlugins,
@@ -14,6 +14,7 @@ import { version as tailwindVersion } from '../package.json'
14
14
  import log from './util/log'
15
15
  import { normalizeScreens } from './util/normalizeScreens'
16
16
  import { formatBoxShadowValue, parseBoxShadowValue } from './util/parseBoxShadowValue'
17
+ import { removeAlphaVariables } from './util/removeAlphaVariables'
17
18
  import { flagEnabled } from './featureFlags'
18
19
 
19
20
  export let variantPlugins = {
@@ -21,7 +22,19 @@ export let variantPlugins = {
21
22
  addVariant('first-letter', '&::first-letter')
22
23
  addVariant('first-line', '&::first-line')
23
24
 
24
- addVariant('marker', ['& *::marker', '&::marker'])
25
+ addVariant('marker', [
26
+ ({ container }) => {
27
+ removeAlphaVariables(container, ['--tw-text-opacity'])
28
+
29
+ return '& *::marker'
30
+ },
31
+ ({ container }) => {
32
+ removeAlphaVariables(container, ['--tw-text-opacity'])
33
+
34
+ return '&::marker'
35
+ },
36
+ ])
37
+
25
38
  addVariant('selection', ['& *::selection', '&::selection'])
26
39
 
27
40
  addVariant('file', '&::file-selector-button')
@@ -77,21 +90,11 @@ export let variantPlugins = {
77
90
  [
78
91
  'visited',
79
92
  ({ container }) => {
80
- let toRemove = ['--tw-text-opacity', '--tw-border-opacity', '--tw-bg-opacity']
81
-
82
- container.walkDecls((decl) => {
83
- if (toRemove.includes(decl.prop)) {
84
- decl.remove()
85
-
86
- return
87
- }
88
-
89
- for (const varName of toRemove) {
90
- if (decl.value.includes(`/ var(${varName})`)) {
91
- decl.value = decl.value.replace(`/ var(${varName})`, '')
92
- }
93
- }
94
- })
93
+ removeAlphaVariables(container, [
94
+ '--tw-text-opacity',
95
+ '--tw-border-opacity',
96
+ '--tw-bg-opacity',
97
+ ])
95
98
 
96
99
  return '&:visited'
97
100
  },
@@ -12,7 +12,7 @@ export function defaultExtractor(context) {
12
12
  let results = []
13
13
 
14
14
  for (let pattern of patterns) {
15
- results.push(...(content.match(pattern) ?? []))
15
+ results = [...results, ...(content.match(pattern) ?? [])]
16
16
  }
17
17
 
18
18
  return results.filter((v) => v !== undefined).map(clipAtBalancedParens)
@@ -25,7 +25,7 @@ function* buildRegExps(context) {
25
25
 
26
26
  let utility = regex.any([
27
27
  // Arbitrary properties
28
- /\[[^\s:'"]+:[^\s\]]+\]/,
28
+ /\[[^\s:'"`]+:[^\s\]]+\]/,
29
29
 
30
30
  // Utilities
31
31
  regex.pattern([
@@ -37,28 +37,28 @@ function* buildRegExps(context) {
37
37
  regex.any([
38
38
  regex.pattern([
39
39
  // Arbitrary values
40
- /-\[[^\s:]+\]/,
40
+ /-(?:\w+-)*\[[^\s:]+\]/,
41
41
 
42
42
  // Not immediately followed by an `{[(`
43
43
  /(?![{([]])/,
44
44
 
45
45
  // optionally followed by an opacity modifier
46
- /(?:\/[^\s'"\\$]*)?/,
46
+ /(?:\/[^\s'"`\\><$]*)?/,
47
47
  ]),
48
48
 
49
49
  regex.pattern([
50
50
  // Arbitrary values
51
- /-\[[^\s]+\]/,
51
+ /-(?:\w+-)*\[[^\s]+\]/,
52
52
 
53
53
  // Not immediately followed by an `{[(`
54
54
  /(?![{([]])/,
55
55
 
56
56
  // optionally followed by an opacity modifier
57
- /(?:\/[^\s'"\\$]*)?/,
57
+ /(?:\/[^\s'"`\\$]*)?/,
58
58
  ]),
59
59
 
60
60
  // Normal values w/o quotes — may include an opacity modifier
61
- /[-\/][^\s'"\\$={]*/,
61
+ /[-\/][^\s'"`\\$={><]*/,
62
62
  ])
63
63
  ),
64
64
  ]),
@@ -69,8 +69,8 @@ function* buildRegExps(context) {
69
69
  '((?=((',
70
70
  regex.any(
71
71
  [
72
- regex.pattern([/([^\s"'\[\\]+-)?\[[^\s"'\\]+\]/, separator]),
73
- regex.pattern([/[^\s"'\[\\]+/, separator]),
72
+ regex.pattern([/([^\s"'`\[\\]+-)?\[[^\s"'`]+\]/, separator]),
73
+ regex.pattern([/[^\s"'`\[\\]+/, separator]),
74
74
  ],
75
75
  true
76
76
  ),
@@ -91,7 +91,7 @@ function* buildRegExps(context) {
91
91
  ])
92
92
 
93
93
  // 5. Inner matches
94
- // yield /[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g
94
+ yield /[^<>"'`\s.(){}[\]#=%$]*[^<>"'`\s.(){}[\]#=%:$]/g
95
95
  }
96
96
 
97
97
  // We want to capture any "special" characters
@@ -40,9 +40,7 @@ function listKeys(obj) {
40
40
  }
41
41
 
42
42
  function validatePath(config, path, defaultValue, themeOpts = {}) {
43
- const pathString = Array.isArray(path)
44
- ? pathToString(path)
45
- : path.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
43
+ const pathString = Array.isArray(path) ? pathToString(path) : path.replace(/^['"]+|['"]+$/g, '')
46
44
  const pathSegments = Array.isArray(path) ? path : toPath(pathString)
47
45
  const value = dlv(config.theme, pathSegments, defaultValue)
48
46
 
@@ -162,7 +160,11 @@ let nodeTypePropertyMap = {
162
160
  export default function ({ tailwindConfig: config }) {
163
161
  let functions = {
164
162
  theme: (node, path, ...defaultValue) => {
165
- let matches = path.match(/^([^\/\s]+)(?:\s*\/\s*([^\/\s]+))$/)
163
+ // Strip quotes from beginning and end of string
164
+ // This allows the alpha value to be present inside of quotes
165
+ path = path.replace(/^['"]+|['"]+$/g, '')
166
+
167
+ let matches = path.match(/^([^\s]+)(?![^\[]*\])(?:\s*\/\s*([^\/\s]+))$/)
166
168
  let alpha = undefined
167
169
 
168
170
  if (matches) {
@@ -163,15 +163,17 @@ function applyVariant(variant, matches, context) {
163
163
 
164
164
  let container = postcss.root({ nodes: [rule.clone()] })
165
165
 
166
- for (let [variantSort, variantFunction] of variantFunctionTuples) {
167
- let clone = container.clone()
166
+ for (let [variantSort, variantFunction, containerFromArray] of variantFunctionTuples) {
167
+ let clone = containerFromArray ?? container.clone()
168
168
  let collectedFormats = []
169
169
 
170
- let originals = new Map()
171
-
172
170
  function prepareBackup() {
173
- if (originals.size > 0) return // Already prepared, chicken out
174
- clone.walkRules((rule) => originals.set(rule, rule.selector))
171
+ // Already prepared, chicken out
172
+ if (clone.raws.neededBackup) {
173
+ return
174
+ }
175
+ clone.raws.neededBackup = true
176
+ clone.walkRules((rule) => (rule.raws.originalSelector = rule.selector))
175
177
  }
176
178
 
177
179
  function modifySelectors(modifierFunction) {
@@ -231,6 +233,10 @@ function applyVariant(variant, matches, context) {
231
233
  // reserving additional X places for these 'unknown' variants in between.
232
234
  variantSort | BigInt(idx << ruleWithVariant.length),
233
235
  variantFunction,
236
+
237
+ // If the clone has been modified we have to pass that back
238
+ // though so each rule can use the modified container
239
+ clone.clone(),
234
240
  ])
235
241
  }
236
242
  continue
@@ -244,13 +250,15 @@ function applyVariant(variant, matches, context) {
244
250
  continue
245
251
  }
246
252
 
247
- // We filled the `originals`, therefore we assume that somebody touched
253
+ // We had to backup selectors, therefore we assume that somebody touched
248
254
  // `container` or `modifySelectors`. Let's see if they did, so that we
249
255
  // can restore the selectors, and collect the format strings.
250
- if (originals.size > 0) {
256
+ if (clone.raws.neededBackup) {
257
+ delete clone.raws.neededBackup
251
258
  clone.walkRules((rule) => {
252
- if (!originals.has(rule)) return
253
- let before = originals.get(rule)
259
+ let before = rule.raws.originalSelector
260
+ if (!before) return
261
+ delete rule.raws.originalSelector
254
262
  if (before === rule.selector) return // No mutation happened
255
263
 
256
264
  let modified = rule.selector
@@ -91,56 +91,73 @@ export default function resolveDefaultsAtRules({ tailwindConfig }) {
91
91
  rule.remove()
92
92
  })
93
93
 
94
- for (let universal of universals) {
95
- /** @type {Map<string, Set<string>>} */
96
- let selectorGroups = new Map()
94
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
95
+ for (let universal of universals) {
96
+ /** @type {Map<string, Set<string>>} */
97
+ let selectorGroups = new Map()
98
+
99
+ let rules = variableNodeMap.get(universal.params) ?? []
100
+
101
+ for (let rule of rules) {
102
+ for (let selector of extractElementSelector(rule.selector)) {
103
+ // If selector contains a vendor prefix after a pseudo element or class,
104
+ // we consider them separately because merging the declarations into
105
+ // a single rule will cause browsers that do not understand the
106
+ // vendor prefix to throw out the whole rule
107
+ let selectorGroupName =
108
+ selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
109
+
110
+ let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
111
+ selectorGroups.set(selectorGroupName, selectors)
112
+
113
+ selectors.add(selector)
114
+ }
115
+ }
97
116
 
98
- let rules = variableNodeMap.get(universal.params) ?? []
117
+ if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
118
+ if (selectorGroups.size === 0) {
119
+ universal.remove()
120
+ continue
121
+ }
99
122
 
100
- for (let rule of rules) {
101
- for (let selector of extractElementSelector(rule.selector)) {
102
- // If selector contains a vendor prefix after a pseudo element or class,
103
- // we consider them separately because merging the declarations into
104
- // a single rule will cause browsers that do not understand the
105
- // vendor prefix to throw out the whole rule
106
- let selectorGroupName =
107
- selector.includes(':-') || selector.includes('::-') ? selector : '__DEFAULT__'
123
+ for (let [, selectors] of selectorGroups) {
124
+ let universalRule = postcss.rule({
125
+ source: universal.source,
126
+ })
108
127
 
109
- let selectors = selectorGroups.get(selectorGroupName) ?? new Set()
110
- selectorGroups.set(selectorGroupName, selectors)
128
+ universalRule.selectors = [...selectors]
111
129
 
112
- selectors.add(selector)
130
+ universalRule.append(universal.nodes.map((node) => node.clone()))
131
+ universal.before(universalRule)
132
+ }
113
133
  }
114
- }
115
134
 
116
- if (flagEnabled(tailwindConfig, 'optimizeUniversalDefaults')) {
117
- if (selectorGroups.size === 0) {
118
- universal.remove()
119
- continue
120
- }
121
-
122
- for (let [, selectors] of selectorGroups) {
123
- let universalRule = postcss.rule({
124
- source: universal.source,
125
- })
135
+ universal.remove()
136
+ }
137
+ } else if (universals.size) {
138
+ let universalRule = postcss.rule({
139
+ selectors: ['*', '::before', '::after'],
140
+ })
126
141
 
127
- universalRule.selectors = [...selectors]
142
+ for (let universal of universals) {
143
+ universalRule.append(universal.nodes)
128
144
 
129
- universalRule.append(universal.nodes.map((node) => node.clone()))
145
+ if (!universalRule.parent) {
130
146
  universal.before(universalRule)
131
147
  }
132
- } else {
133
- let universalRule = postcss.rule({
134
- source: universal.source,
135
- })
136
148
 
137
- universalRule.selectors = ['*', '::before', '::after', '::backdrop']
149
+ if (!universalRule.source) {
150
+ universalRule.source = universal.source
151
+ }
138
152
 
139
- universalRule.append(universal.nodes)
140
- universal.before(universalRule)
153
+ universal.remove()
141
154
  }
142
155
 
143
- universal.remove()
156
+ let backdropRule = universalRule.clone({
157
+ selectors: ['::backdrop'],
158
+ })
159
+
160
+ universalRule.after(backdropRule)
144
161
  }
145
162
  }
146
163
  }
@@ -465,11 +465,14 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs
465
465
  }
466
466
 
467
467
  if (Array.isArray(result)) {
468
- return result.map((variant) => parseVariant(variant))
468
+ return result
469
+ .filter((variant) => typeof variant === 'string')
470
+ .map((variant) => parseVariant(variant))
469
471
  }
470
472
 
471
473
  // result may be undefined with legacy variants that use APIs like `modifySelectors`
472
- return result && parseVariant(result)(api)
474
+ // result may also be a postcss node if someone was returning the result from `modifySelectors`
475
+ return result && typeof result === 'string' && parseVariant(result)(api)
473
476
  }
474
477
  }
475
478
 
@@ -40,9 +40,9 @@ export function normalize(value, isRoot = true) {
40
40
  value = value.trim()
41
41
  }
42
42
 
43
- // Add spaces around operators inside calc() that do not follow an operator
43
+ // Add spaces around operators inside math functions like calc() that do not follow an operator
44
44
  // or '('.
45
- value = value.replace(/calc\(.+\)/g, (match) => {
45
+ value = value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
46
46
  return match.replace(
47
47
  /(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
48
48
  '$1 $2 '
@@ -0,0 +1,24 @@
1
+ /**
2
+ * This function removes any uses of CSS variables used as an alpha channel
3
+ *
4
+ * This is required for selectors like `:visited` which do not allow
5
+ * changes in opacity or external control using CSS variables.
6
+ *
7
+ * @param {import('postcss').Container} container
8
+ * @param {string[]} toRemove
9
+ */
10
+ export function removeAlphaVariables(container, toRemove) {
11
+ container.walkDecls((decl) => {
12
+ if (toRemove.includes(decl.prop)) {
13
+ decl.remove()
14
+
15
+ return
16
+ }
17
+
18
+ for (let varName of toRemove) {
19
+ if (decl.value.includes(`/ var(${varName})`)) {
20
+ decl.value = decl.value.replace(`/ var(${varName})`, '')
21
+ }
22
+ }
23
+ })
24
+ }
package/types/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { CorePluginList } from './generated/CorePluginList'
1
+ import type { CorePluginList } from './generated/corePluginList'
2
2
  import type { DefaultColors } from './generated/colors'
3
3
 
4
4
  // Helpers
package/types/index.d.ts CHANGED
@@ -1 +1,7 @@
1
- export type { Config } from './config.d'
1
+ import { PluginCreator } from 'postcss'
2
+ import type { Config } from './config.d'
3
+
4
+ declare const plugin: PluginCreator<string | Config | { config: string | Config }>
5
+
6
+ export { Config }
7
+ export default plugin