@weapp-tailwindcss/postcss 2.2.1-next.1 → 2.2.1-next.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.
@@ -2,4 +2,13 @@ export interface DynamicColorMixAlphaProtection {
2
2
  css: string;
3
3
  restore: (css: string) => string;
4
4
  }
5
- export declare function protectDynamicColorMixAlpha(css: string): DynamicColorMixAlphaProtection;
5
+ export interface DynamicColorMixAlphaProtectionOptions {
6
+ customPropertyValues?: ReadonlyMap<string, string> | undefined;
7
+ }
8
+ export interface ModernColorValueNormalization {
9
+ value: string;
10
+ changed: boolean;
11
+ hasUnsupported: boolean;
12
+ }
13
+ export declare function normalizeModernColorValue(value: string, customPropertyValues?: ReadonlyMap<string, string>): ModernColorValueNormalization;
14
+ export declare function protectDynamicColorMixAlpha(css: string, options?: DynamicColorMixAlphaProtectionOptions): DynamicColorMixAlphaProtection;
@@ -1,9 +1,13 @@
1
- import type { AtRule, Declaration, Rule } from 'postcss';
1
+ import type { AtRule, Root, Rule } from 'postcss';
2
+ import { Declaration } from 'postcss';
2
3
  export declare function isTailwindcssV4(options?: {
3
4
  majorVersion?: number;
4
5
  }): boolean;
5
6
  export declare function testIfRootHostForV4(node: Rule): boolean;
6
7
  export declare const cssVarsV4Nodes: Declaration[];
8
+ export declare function collectUsedTailwindcssV4Variables(root: Root): Set<string>;
9
+ export declare function usesTailwindcssV4ContentVariable(root: Root): boolean;
10
+ export declare function createUsedCssVarsV4Nodes(usedProps: ReadonlySet<string>): Declaration[];
7
11
  export declare function isTailwindcssV4ModernCheck(atRule: AtRule): boolean;
8
12
  export declare function isTailwindcssV4LinearGradientSupports(atRule: AtRule): boolean;
9
13
  export declare function isTailwindcssV4DisplayP3Supports(atRule: AtRule): boolean;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type DynamicColorMixAlphaProtection, protectDynamicColorMixAlpha } from './compat/color-mix';
1
+ export { type DynamicColorMixAlphaProtection, type DynamicColorMixAlphaProtectionOptions, type ModernColorValueNormalization, normalizeModernColorValue, protectDynamicColorMixAlpha, } from './compat/color-mix';
2
2
  export * from './handler';
3
3
  export { default as postcssHtmlTransform, type IOptions as PostcssHtmlTransformOptions } from './html-transform';
4
4
  export { createStylePipeline, type PipelineNodeContext, type PipelineNodeCursor, type PipelineStage, type ResolvedPipelineNode, type StyleProcessingPipeline, } from './pipeline';
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_html_transform = require("./html-transform-DuSRw6IH.js");
3
3
  require("./types.js");
4
+ let _csstools_css_color_parser = require("@csstools/css-color-parser");
5
+ let _csstools_css_parser_algorithms = require("@csstools/css-parser-algorithms");
6
+ let _csstools_css_tokenizer = require("@csstools/css-tokenizer");
4
7
  let postcss = require("postcss");
5
8
  postcss = require_html_transform.__toESM(postcss);
6
9
  let postcss_value_parser = require("postcss-value-parser");
@@ -24,9 +27,19 @@ postcss_rule_unit_converter = require_html_transform.__toESM(postcss_rule_unit_c
24
27
  let _weapp_core_escape = require("@weapp-core/escape");
25
28
  //#region src/compat/color-mix.ts
26
29
  const COLOR_MIX_NAME = "color-mix";
27
- const PLACEHOLDER_PREFIX = "__weapp_tw_dynamic_color_mix_";
30
+ const MODERN_COLOR_FUNCTION_NAMES = new Set([
31
+ "oklch",
32
+ "oklab",
33
+ "lch",
34
+ "lab"
35
+ ]);
36
+ const PLACEHOLDER_PREFIX = "__weapp_tw_color_mix_";
28
37
  const DYNAMIC_ALPHA_RE = /\b(?:var|env)\(|--[\w-]+\b/;
29
38
  const INTERNAL_TAILWIND_ALPHA_RE = /var\(\s*--tw-[^)]+-alpha\s*\)/;
39
+ const TRANSPARENT_COLOR_RE = /^transparent$/i;
40
+ const CURRENT_COLOR_RE = /^currentcolor$/i;
41
+ const CSS_WIDE_KEYWORD_RE = /^(?:inherit|initial|unset|revert|revert-layer)$/i;
42
+ const CUSTOM_PROPERTY_RE = /^--[\w-]+$/;
30
43
  function splitArguments(nodes) {
31
44
  const args = [];
32
45
  let current = [];
@@ -57,15 +70,147 @@ function splitStopSegments(nodes) {
57
70
  if (current.length > 0) segments.push(current);
58
71
  return segments;
59
72
  }
60
- function hasDynamicStopAlpha(nodes) {
61
- const segments = splitStopSegments(nodes);
62
- if (segments.length < 2) return false;
63
- const alpha = postcss_value_parser.default.stringify(segments[segments.length - 1]).trim();
64
- if (INTERNAL_TAILWIND_ALPHA_RE.test(alpha)) return false;
65
- return DYNAMIC_ALPHA_RE.test(alpha);
73
+ function trimNodes$1(nodes) {
74
+ let start = 0;
75
+ let end = nodes.length;
76
+ while (start < end && nodes[start]?.type === "space") start += 1;
77
+ while (end > start && nodes[end - 1]?.type === "space") end -= 1;
78
+ return nodes.slice(start, end);
79
+ }
80
+ function getParsedColorData(colorSource) {
81
+ try {
82
+ return (0, _csstools_css_color_parser.color)((0, _csstools_css_parser_algorithms.parseComponentValue)((0, _csstools_css_tokenizer.tokenize)({ css: colorSource })));
83
+ } catch {
84
+ return false;
85
+ }
86
+ }
87
+ function parseAlphaValue(alphaSource) {
88
+ const parsed = Number.parseFloat(alphaSource);
89
+ if (Number.isFinite(parsed)) return alphaSource.trim().endsWith("%") ? parsed / 100 : parsed;
90
+ }
91
+ function resolveVarColor(colorSource, customPropertyValues, depth = 0) {
92
+ if (depth > 5) return;
93
+ const parsed = (0, postcss_value_parser.default)(colorSource.trim());
94
+ const node = parsed.nodes.length === 1 ? parsed.nodes[0] : void 0;
95
+ if (node?.type !== "function" || node.value.toLowerCase() !== "var") return;
96
+ const args = splitArguments(node.nodes);
97
+ const propertyName = postcss_value_parser.default.stringify(trimNodes$1(args[0] ?? [])).trim();
98
+ if (!CUSTOM_PROPERTY_RE.test(propertyName)) return;
99
+ const resolved = customPropertyValues.get(propertyName);
100
+ if (!resolved) {
101
+ const fallback = args[1] ? postcss_value_parser.default.stringify(trimNodes$1(args[1])).trim() : void 0;
102
+ return fallback ? resolveColorData(fallback, customPropertyValues, depth + 1) : void 0;
103
+ }
104
+ return resolveColorData(resolved, customPropertyValues, depth + 1);
105
+ }
106
+ function resolveColorData(colorSource, customPropertyValues, depth = 0) {
107
+ if (typeof colorSource !== "string") return;
108
+ const trimmed = colorSource.trim();
109
+ if (TRANSPARENT_COLOR_RE.test(trimmed)) return getParsedColorData(trimmed) || void 0;
110
+ if (CURRENT_COLOR_RE.test(trimmed) || CSS_WIDE_KEYWORD_RE.test(trimmed)) return;
111
+ const resolvedVar = resolveVarColor(trimmed, customPropertyValues, depth);
112
+ if (resolvedVar) return resolvedVar;
113
+ return getParsedColorData(trimmed) || void 0;
114
+ }
115
+ function normalizeColorFunctionName(colorSource, alpha, customPropertyValues) {
116
+ const resolvedColor = resolveColorData(colorSource, customPropertyValues);
117
+ if (!resolvedColor) return;
118
+ resolvedColor.alpha = alpha;
119
+ return (0, _csstools_css_color_parser.serializeRGB)(resolvedColor).toString();
120
+ }
121
+ function normalizeStandaloneColorFunction(colorSource) {
122
+ const resolvedColor = getParsedColorData(colorSource);
123
+ return resolvedColor ? (0, _csstools_css_color_parser.serializeRGB)(resolvedColor).toString() : void 0;
124
+ }
125
+ function isDisplayP3ColorFunction(colorSource) {
126
+ return /^color\(\s*display-p3\b/i.test(colorSource.trim());
127
+ }
128
+ function hasUnsupportedModernColorFunction(value) {
129
+ const parsed = (0, postcss_value_parser.default)(value);
130
+ let hasUnsupported = false;
131
+ parsed.walk((node) => {
132
+ if (node.type !== "function") return;
133
+ const name = node.value.toLowerCase();
134
+ if (name === COLOR_MIX_NAME || MODERN_COLOR_FUNCTION_NAMES.has(name) || name === "color" && isDisplayP3ColorFunction(postcss_value_parser.default.stringify(node))) {
135
+ hasUnsupported = true;
136
+ return false;
137
+ }
138
+ });
139
+ return hasUnsupported;
140
+ }
141
+ function normalizeModernColorValue(value, customPropertyValues = /* @__PURE__ */ new Map()) {
142
+ if (!hasUnsupportedModernColorFunction(value)) return {
143
+ value,
144
+ changed: false,
145
+ hasUnsupported: false
146
+ };
147
+ const parsed = (0, postcss_value_parser.default)(value);
148
+ let changed = false;
149
+ parsed.walk((node) => {
150
+ if (node.type !== "function") return;
151
+ const name = node.value.toLowerCase();
152
+ const source = postcss_value_parser.default.stringify(node);
153
+ let normalized;
154
+ if (MODERN_COLOR_FUNCTION_NAMES.has(name) || name === "color" && isDisplayP3ColorFunction(source)) normalized = normalizeStandaloneColorFunction(source);
155
+ else if (name === COLOR_MIX_NAME) normalized = tryResolveColorMix(node, customPropertyValues)?.value;
156
+ if (!normalized) return;
157
+ const mutableNode = node;
158
+ mutableNode.type = "word";
159
+ mutableNode.value = normalized;
160
+ delete mutableNode.nodes;
161
+ changed = true;
162
+ });
163
+ const nextValue = changed ? parsed.toString() : value;
164
+ return {
165
+ value: nextValue,
166
+ changed,
167
+ hasUnsupported: hasUnsupportedModernColorFunction(nextValue)
168
+ };
66
169
  }
67
- function shouldProtectColorMix(node) {
68
- return splitArguments(node.nodes).slice(1).some(hasDynamicStopAlpha);
170
+ function createRgbaWithAlpha(colorSource, alphaSource, customPropertyValues) {
171
+ const resolvedColor = resolveColorData(colorSource, customPropertyValues);
172
+ if (!resolvedColor) return;
173
+ const [red, green, blue] = resolvedColor.channels.map((channel) => Math.round(Math.min(1, Math.max(0, channel)) * 255));
174
+ const alpha = alphaSource.trim();
175
+ return `rgba(${red}, ${green}, ${blue}, ${CUSTOM_PROPERTY_RE.test(alpha) ? `var(${alpha})` : alpha})`;
176
+ }
177
+ function tryResolveColorMix(node, customPropertyValues) {
178
+ const args = splitArguments(node.nodes);
179
+ if (args.length < 3) return;
180
+ const colorStopNodes = splitStopSegments(args[1] ?? []);
181
+ if (colorStopNodes.length < 2) return;
182
+ const colorNodes = trimNodes$1(colorStopNodes[0] ?? []);
183
+ const alphaNodes = trimNodes$1(colorStopNodes[1] ?? []);
184
+ const trailingNodes = trimNodes$1(args[2] ?? []);
185
+ if (!colorNodes.length || !alphaNodes.length || postcss_value_parser.default.stringify(trailingNodes).trim().toLowerCase() !== "transparent") return;
186
+ const colorSource = postcss_value_parser.default.stringify(colorNodes).trim();
187
+ const alphaSource = postcss_value_parser.default.stringify(alphaNodes).trim();
188
+ if (!colorSource || !alphaSource || INTERNAL_TAILWIND_ALPHA_RE.test(alphaSource)) return;
189
+ if (CURRENT_COLOR_RE.test(colorSource)) return {
190
+ value: colorSource,
191
+ deferred: false
192
+ };
193
+ if (DYNAMIC_ALPHA_RE.test(alphaSource)) {
194
+ const normalized = createRgbaWithAlpha(colorSource, alphaSource, customPropertyValues);
195
+ return normalized ? {
196
+ value: normalized,
197
+ deferred: true
198
+ } : {
199
+ value: colorSource,
200
+ deferred: true
201
+ };
202
+ }
203
+ const alpha = parseAlphaValue(alphaSource);
204
+ if (alpha === void 0) return;
205
+ const normalized = normalizeColorFunctionName(colorSource, alpha, customPropertyValues);
206
+ if (normalized) return {
207
+ value: normalized,
208
+ deferred: false
209
+ };
210
+ return {
211
+ value: colorSource,
212
+ deferred: false
213
+ };
69
214
  }
70
215
  function createPlaceholder(index) {
71
216
  return `${PLACEHOLDER_PREFIX}${index}__`;
@@ -76,40 +221,54 @@ function unwrapProtectedSupports(cssRoot) {
76
221
  atRule.replaceWith(atRule.nodes);
77
222
  });
78
223
  }
79
- function protectDynamicColorMixAlpha(css) {
224
+ function protectDynamicColorMixAlpha(css, options = {}) {
80
225
  if (!css.includes(COLOR_MIX_NAME)) return {
81
226
  css,
82
227
  restore: (value) => value
83
228
  };
84
229
  const replacements = /* @__PURE__ */ new Map();
85
230
  const root = postcss.default.parse(css);
231
+ const customPropertyValues = new Map(options.customPropertyValues);
232
+ let changed = false;
233
+ root.walkDecls((decl) => {
234
+ if (decl.prop.startsWith("--") && !decl.value.includes(COLOR_MIX_NAME)) customPropertyValues.set(decl.prop, decl.value.trim());
235
+ });
86
236
  root.walkDecls((decl) => {
87
237
  if (!decl.value.includes(COLOR_MIX_NAME)) return;
88
238
  const parsed = (0, postcss_value_parser.default)(decl.value);
89
239
  let mutated = false;
90
240
  parsed.walk((node) => {
91
241
  if (node.type !== "function" || node.value.toLowerCase() !== COLOR_MIX_NAME) return;
92
- if (!shouldProtectColorMix(node)) return;
93
- const placeholder = createPlaceholder(replacements.size);
94
- replacements.set(placeholder, postcss_value_parser.default.stringify(node));
95
- const mutableNode = node;
96
- mutableNode.type = "word";
97
- mutableNode.value = placeholder;
98
- delete mutableNode.nodes;
99
- mutated = true;
242
+ const resolved = tryResolveColorMix(node, customPropertyValues);
243
+ if (resolved) {
244
+ if (resolved.deferred) {
245
+ const placeholder = createPlaceholder(replacements.size);
246
+ replacements.set(placeholder, resolved.value);
247
+ const mutableNode = node;
248
+ mutableNode.type = "word";
249
+ mutableNode.value = placeholder;
250
+ delete mutableNode.nodes;
251
+ mutated = true;
252
+ return;
253
+ }
254
+ const mutableNode = node;
255
+ mutableNode.type = "word";
256
+ mutableNode.value = resolved.value;
257
+ delete mutableNode.nodes;
258
+ mutated = true;
259
+ }
100
260
  });
101
- if (mutated) decl.value = parsed.toString();
261
+ if (mutated) {
262
+ decl.value = parsed.toString();
263
+ changed = true;
264
+ }
102
265
  });
103
- if (replacements.size === 0) return {
104
- css,
105
- restore: (value) => value
106
- };
107
- unwrapProtectedSupports(root);
266
+ if (replacements.size > 0) unwrapProtectedSupports(root);
108
267
  return {
109
- css: root.toString(),
268
+ css: changed ? root.toString() : css,
110
269
  restore(value) {
111
270
  let restored = value;
112
- for (const [placeholder, original] of replacements) restored = restored.split(placeholder).join(original);
271
+ for (const [placeholder, replacement] of replacements) restored = restored.split(placeholder).join(replacement);
113
272
  return restored;
114
273
  }
115
274
  };
@@ -141,7 +300,7 @@ function isBaseCarrierSelector(selector) {
141
300
  function isBaseCarrierRule(rule) {
142
301
  return Array.isArray(rule.selectors) && rule.selectors.length > 0 && rule.selectors.every(isBaseCarrierSelector);
143
302
  }
144
- function hasClassSelector(rule) {
303
+ function hasClassSelector$1(rule) {
145
304
  return Array.isArray(rule.selectors) && rule.selectors.some((selector) => CLASS_SELECTOR_RE.test(selector));
146
305
  }
147
306
  function collectRequiredTwVars(value) {
@@ -171,7 +330,7 @@ function extractUniAppXBaseDefaults(result) {
171
330
  function injectUniAppXBaseDefaults(result, defaults) {
172
331
  if (defaults.size === 0) return;
173
332
  result.root.walkRules((rule) => {
174
- if (!hasClassSelector(rule)) return;
333
+ if (!hasClassSelector$1(rule)) return;
175
334
  const declaredProps = /* @__PURE__ */ new Set();
176
335
  const requiredProps = /* @__PURE__ */ new Set();
177
336
  rule.walkDecls((decl) => {
@@ -821,6 +980,8 @@ const nodes = [
821
980
  property("--tw-scroll-snap-strictness", "proximity", "*"),
822
981
  property("--tw-space-x-reverse", "0"),
823
982
  property("--tw-space-y-reverse", "0"),
983
+ property("--tw-scrollbar-thumb", "#0000", "<color>"),
984
+ property("--tw-scrollbar-track", "#0000", "<color>"),
824
985
  property("--tw-border-style", "solid"),
825
986
  property("--tw-divide-x-reverse", "0"),
826
987
  property("--tw-divide-y-reverse", "0"),
@@ -945,13 +1106,46 @@ const DISPLAY_P3_VALUE_RE = /color\(\s*display-p3\b/i;
945
1106
  const COLOR_GAMUT_P3_RE = /\(\s*color-gamut\s*:\s*p3\s*\)/i;
946
1107
  const RADIUS_VALUE_RE = /\b([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)\s*(r?px)\b/gi;
947
1108
  const SCIENTIFIC_NOTATION_RE = /e/i;
1109
+ const TW_VAR_FUNCTION_RE = /var\(\s*(--tw-[\w-]+)\b/g;
1110
+ const TW_CONTENT_VAR_RE = /var\(\s*--tw-content\b/;
948
1111
  function isTailwindcssV4(options) {
949
1112
  return options?.majorVersion === 4;
950
1113
  }
951
1114
  function testIfRootHostForV4(node) {
952
1115
  return node.type === "rule" && node.selector.includes(":root") && node.selector.includes(":host");
953
1116
  }
954
- const cssVarsV4Nodes = createCssVarNodes(nodes);
1117
+ createCssVarNodes(nodes);
1118
+ function collectUsedTailwindcssV4Variables(root) {
1119
+ const props = /* @__PURE__ */ new Set();
1120
+ root.walkDecls((decl) => {
1121
+ if (decl.prop.startsWith("--tw-")) props.add(decl.prop);
1122
+ TW_VAR_FUNCTION_RE.lastIndex = 0;
1123
+ let match = TW_VAR_FUNCTION_RE.exec(decl.value);
1124
+ while (match !== null) {
1125
+ const prop = match[1];
1126
+ if (prop) props.add(prop);
1127
+ match = TW_VAR_FUNCTION_RE.exec(decl.value);
1128
+ }
1129
+ });
1130
+ root.walkAtRules("property", (atRule) => {
1131
+ const prop = atRule.params.trim();
1132
+ if (prop.startsWith("--tw-")) props.add(prop);
1133
+ });
1134
+ return props;
1135
+ }
1136
+ function usesTailwindcssV4ContentVariable(root) {
1137
+ let used = false;
1138
+ root.walkDecls((decl) => {
1139
+ if (TW_CONTENT_VAR_RE.test(decl.value)) used = true;
1140
+ });
1141
+ return used;
1142
+ }
1143
+ function createUsedCssVarsV4Nodes(usedProps) {
1144
+ return nodes.filter((def) => usedProps.has(def.prop)).map((def) => new postcss.Declaration({
1145
+ prop: def.prop,
1146
+ value: def.value
1147
+ }));
1148
+ }
955
1149
  function isTailwindcssV4ModernCheck(atRule) {
956
1150
  return atRule.name === "supports" && [
957
1151
  MODERN_CHECK_WEBKIT_HYPHENS_RE,
@@ -2091,6 +2285,20 @@ function makePseudoVarRule() {
2091
2285
  }));
2092
2286
  return pseudoVarRule;
2093
2287
  }
2288
+ function isEmptyContentInitDeclaration(decl) {
2289
+ return decl.prop === "--tw-content" && (decl.value === "\"\"" || decl.value === "''");
2290
+ }
2291
+ function removeTailwindV4EmptyContentInit(node) {
2292
+ node.walkDecls((decl) => {
2293
+ if (isEmptyContentInitDeclaration(decl)) decl.remove();
2294
+ });
2295
+ }
2296
+ function hasClassSelector(node) {
2297
+ return node.selectors.some((selector) => selector.includes("."));
2298
+ }
2299
+ function isRootThemeScopeRule(node) {
2300
+ return node.selectors.length > 0 && node.selectors.every((selector) => selector === ":root" || selector === ":host" || DEFAULT_ROOT_SELECTORS.includes(selector));
2301
+ }
2094
2302
  function remakeCssVarSelector(selectors, options) {
2095
2303
  const { cssPreflightRange, cssSelectorReplacement } = options;
2096
2304
  if (cssPreflightRange === "all" && !selectors.includes(":not(not)")) selectors.push(":not(not)");
@@ -2112,6 +2320,7 @@ function resolveUniAppXVariableScopeSelectors(options) {
2112
2320
  function commonChunkPreflight(node, options) {
2113
2321
  const { ctx, cssInjectPreflight, injectAdditionalCssVarScope } = options;
2114
2322
  const uniAppXEnabled = isUniAppXEnabled(options);
2323
+ const isTailwindcss4 = isTailwindcssV4(options);
2115
2324
  const rootOption = options.cssSelectorReplacement?.root;
2116
2325
  const rootSelectors = rootOption === false || rootOption === void 0 ? [] : Array.isArray(rootOption) ? rootOption.filter(Boolean) : [rootOption];
2117
2326
  const hasHostSelector = node.selectors.some((selector) => selector.includes(":host"));
@@ -2121,31 +2330,33 @@ function commonChunkPreflight(node, options) {
2121
2330
  phase: "pre",
2122
2331
  reason: "append-host-selector"
2123
2332
  });
2333
+ if (isTailwindcss4 && !usesTailwindcssV4ContentVariable(node.root()) && (!hasClassSelector(node) || isRootThemeScopeRule(node))) removeTailwindV4EmptyContentInit(node);
2124
2334
  if (testIfVariablesScope(node) || uniAppXEnabled && node.selectors.includes("*") && hasTwVars(node, 2)) {
2125
2335
  ctx?.markVariablesScope(node);
2126
2336
  assignRuleSelectors(node, uniAppXEnabled ? resolveUniAppXVariableScopeSelectors(options) : remakeCssVarSelector(node.selectors, options), {
2127
2337
  phase: "pre",
2128
2338
  reason: "rewrite-variable-scope"
2129
2339
  });
2130
- if (!uniAppXEnabled) node.before(makePseudoVarRule());
2340
+ if (!uniAppXEnabled && !isTailwindcss4) node.before(makePseudoVarRule());
2131
2341
  if (typeof cssInjectPreflight === "function") node.append(...cssInjectPreflight());
2132
2342
  }
2133
- const isTailwindcss4 = isTailwindcssV4(options);
2134
2343
  if (injectAdditionalCssVarScope && (isTailwindcss4 ? testIfRootHostForV4(node) : testIfTwBackdrop(node))) {
2344
+ const nodes = isTailwindcss4 ? createUsedCssVarsV4Nodes(collectUsedTailwindcssV4Variables(node.root())) : cssVarsV3Nodes;
2345
+ if (nodes.length === 0) return;
2135
2346
  const syntheticRule = new postcss.Rule({
2136
2347
  selectors: uniAppXEnabled ? resolveUniAppXVariableScopeSelectors(options) : [
2137
2348
  "*",
2138
2349
  "::after",
2139
2350
  "::before"
2140
2351
  ],
2141
- nodes: isTailwindcss4 ? cssVarsV4Nodes : cssVarsV3Nodes
2352
+ nodes
2142
2353
  });
2143
2354
  if (!uniAppXEnabled) assignRuleSelectors(syntheticRule, remakeCssVarSelector(syntheticRule.selectors, options), {
2144
2355
  phase: "pre",
2145
2356
  reason: "rewrite-synthetic-variable-scope"
2146
2357
  });
2147
2358
  node.before(syntheticRule);
2148
- if (!uniAppXEnabled) node.before(makePseudoVarRule());
2359
+ if (!uniAppXEnabled && !isTailwindcss4) node.before(makePseudoVarRule());
2149
2360
  if (typeof cssInjectPreflight === "function") syntheticRule.append(...cssInjectPreflight());
2150
2361
  }
2151
2362
  }
@@ -2480,19 +2691,31 @@ function createStyleHandler(options) {
2480
2691
  const hasUserPlugins = Boolean(cachedOptions.postcssOptions?.plugins && (Array.isArray(cachedOptions.postcssOptions.plugins) ? cachedOptions.postcssOptions.plugins.length > 0 : typeof cachedOptions.postcssOptions.plugins === "object" && Object.keys(cachedOptions.postcssOptions.plugins).length > 0));
2481
2692
  const handler = ((rawSource, opt) => {
2482
2693
  const resolvedOptions = resolver.resolve(opt);
2694
+ const protectedColorMix = resolvedOptions.majorVersion === 4 ? protectDynamicColorMixAlpha(rawSource) : void 0;
2695
+ const source = protectedColorMix?.css ?? rawSource;
2483
2696
  let signal;
2484
2697
  if (!hasUserPlugins) try {
2485
- signal = probeFeatures(rawSource);
2698
+ signal = probeFeatures(source);
2486
2699
  } catch {
2487
2700
  signal = void 0;
2488
2701
  }
2489
- const cacheKey = `${getOptionsFingerprint(resolvedOptions)}|${signal ? signalToCacheKey(signal) : ""}|${simpleHash(rawSource)}`;
2702
+ const cacheKey = `${getOptionsFingerprint(resolvedOptions)}|${signal ? signalToCacheKey(signal) : ""}|${simpleHash(source)}`;
2490
2703
  const cachedResult = resultCache.get(cacheKey);
2491
2704
  if (cachedResult) return Promise.resolve(cachedResult);
2492
2705
  const processor = processorCache.getProcessor(resolvedOptions, signal);
2493
2706
  const processOptions = processorCache.getProcessOptions(resolvedOptions);
2494
- return processor.process(rawSource, processOptions).async().then((result) => {
2495
- const finalResult = applyUniAppXUvueCompatibility(applyUniAppXBaseCompatibility(result, resolvedOptions), resolvedOptions);
2707
+ return processor.process(source, processOptions).async().then((result) => {
2708
+ let finalResult = applyUniAppXUvueCompatibility(applyUniAppXBaseCompatibility(result, resolvedOptions), resolvedOptions);
2709
+ if (protectedColorMix) {
2710
+ const restoredCss = protectedColorMix.restore(finalResult.css);
2711
+ if (restoredCss !== finalResult.css) {
2712
+ const nextResult = finalResult.root.clone().toResult(finalResult.opts);
2713
+ nextResult.css = restoredCss;
2714
+ nextResult.root = postcss.default.parse(restoredCss, finalResult.opts);
2715
+ nextResult.messages.push(...finalResult.messages);
2716
+ finalResult = nextResult;
2717
+ }
2718
+ }
2496
2719
  resultCache.set(cacheKey, finalResult);
2497
2720
  return finalResult;
2498
2721
  });
@@ -2509,5 +2732,6 @@ exports.createInjectPreflight = createInjectPreflight;
2509
2732
  exports.createStyleHandler = createStyleHandler;
2510
2733
  exports.createStylePipeline = createStylePipeline;
2511
2734
  exports.internalCssSelectorReplacer = internalCssSelectorReplacer;
2735
+ exports.normalizeModernColorValue = normalizeModernColorValue;
2512
2736
  exports.postcssHtmlTransform = require_html_transform.postcssHtmlTransform;
2513
2737
  exports.protectDynamicColorMixAlpha = protectDynamicColorMixAlpha;
package/dist/index.mjs CHANGED
@@ -1,5 +1,8 @@
1
1
  import postcssHtmlTransform from "./html-transform.mjs";
2
2
  import "./types.mjs";
3
+ import { color, serializeRGB } from "@csstools/css-color-parser";
4
+ import { parseComponentValue } from "@csstools/css-parser-algorithms";
5
+ import { tokenize } from "@csstools/css-tokenizer";
3
6
  import postcss, { Declaration, Rule } from "postcss";
4
7
  import valueParser from "postcss-value-parser";
5
8
  import { defu, defuOverrideArray, regExpTest } from "@weapp-tailwindcss/shared";
@@ -14,9 +17,19 @@ import postcssUnitConverter, { presets } from "postcss-rule-unit-converter";
14
17
  import { MappingChars2String, escape } from "@weapp-core/escape";
15
18
  //#region src/compat/color-mix.ts
16
19
  const COLOR_MIX_NAME = "color-mix";
17
- const PLACEHOLDER_PREFIX = "__weapp_tw_dynamic_color_mix_";
20
+ const MODERN_COLOR_FUNCTION_NAMES = new Set([
21
+ "oklch",
22
+ "oklab",
23
+ "lch",
24
+ "lab"
25
+ ]);
26
+ const PLACEHOLDER_PREFIX = "__weapp_tw_color_mix_";
18
27
  const DYNAMIC_ALPHA_RE = /\b(?:var|env)\(|--[\w-]+\b/;
19
28
  const INTERNAL_TAILWIND_ALPHA_RE = /var\(\s*--tw-[^)]+-alpha\s*\)/;
29
+ const TRANSPARENT_COLOR_RE = /^transparent$/i;
30
+ const CURRENT_COLOR_RE = /^currentcolor$/i;
31
+ const CSS_WIDE_KEYWORD_RE = /^(?:inherit|initial|unset|revert|revert-layer)$/i;
32
+ const CUSTOM_PROPERTY_RE = /^--[\w-]+$/;
20
33
  function splitArguments(nodes) {
21
34
  const args = [];
22
35
  let current = [];
@@ -47,15 +60,147 @@ function splitStopSegments(nodes) {
47
60
  if (current.length > 0) segments.push(current);
48
61
  return segments;
49
62
  }
50
- function hasDynamicStopAlpha(nodes) {
51
- const segments = splitStopSegments(nodes);
52
- if (segments.length < 2) return false;
53
- const alpha = valueParser.stringify(segments[segments.length - 1]).trim();
54
- if (INTERNAL_TAILWIND_ALPHA_RE.test(alpha)) return false;
55
- return DYNAMIC_ALPHA_RE.test(alpha);
63
+ function trimNodes$1(nodes) {
64
+ let start = 0;
65
+ let end = nodes.length;
66
+ while (start < end && nodes[start]?.type === "space") start += 1;
67
+ while (end > start && nodes[end - 1]?.type === "space") end -= 1;
68
+ return nodes.slice(start, end);
69
+ }
70
+ function getParsedColorData(colorSource) {
71
+ try {
72
+ return color(parseComponentValue(tokenize({ css: colorSource })));
73
+ } catch {
74
+ return false;
75
+ }
76
+ }
77
+ function parseAlphaValue(alphaSource) {
78
+ const parsed = Number.parseFloat(alphaSource);
79
+ if (Number.isFinite(parsed)) return alphaSource.trim().endsWith("%") ? parsed / 100 : parsed;
80
+ }
81
+ function resolveVarColor(colorSource, customPropertyValues, depth = 0) {
82
+ if (depth > 5) return;
83
+ const parsed = valueParser(colorSource.trim());
84
+ const node = parsed.nodes.length === 1 ? parsed.nodes[0] : void 0;
85
+ if (node?.type !== "function" || node.value.toLowerCase() !== "var") return;
86
+ const args = splitArguments(node.nodes);
87
+ const propertyName = valueParser.stringify(trimNodes$1(args[0] ?? [])).trim();
88
+ if (!CUSTOM_PROPERTY_RE.test(propertyName)) return;
89
+ const resolved = customPropertyValues.get(propertyName);
90
+ if (!resolved) {
91
+ const fallback = args[1] ? valueParser.stringify(trimNodes$1(args[1])).trim() : void 0;
92
+ return fallback ? resolveColorData(fallback, customPropertyValues, depth + 1) : void 0;
93
+ }
94
+ return resolveColorData(resolved, customPropertyValues, depth + 1);
95
+ }
96
+ function resolveColorData(colorSource, customPropertyValues, depth = 0) {
97
+ if (typeof colorSource !== "string") return;
98
+ const trimmed = colorSource.trim();
99
+ if (TRANSPARENT_COLOR_RE.test(trimmed)) return getParsedColorData(trimmed) || void 0;
100
+ if (CURRENT_COLOR_RE.test(trimmed) || CSS_WIDE_KEYWORD_RE.test(trimmed)) return;
101
+ const resolvedVar = resolveVarColor(trimmed, customPropertyValues, depth);
102
+ if (resolvedVar) return resolvedVar;
103
+ return getParsedColorData(trimmed) || void 0;
104
+ }
105
+ function normalizeColorFunctionName(colorSource, alpha, customPropertyValues) {
106
+ const resolvedColor = resolveColorData(colorSource, customPropertyValues);
107
+ if (!resolvedColor) return;
108
+ resolvedColor.alpha = alpha;
109
+ return serializeRGB(resolvedColor).toString();
110
+ }
111
+ function normalizeStandaloneColorFunction(colorSource) {
112
+ const resolvedColor = getParsedColorData(colorSource);
113
+ return resolvedColor ? serializeRGB(resolvedColor).toString() : void 0;
114
+ }
115
+ function isDisplayP3ColorFunction(colorSource) {
116
+ return /^color\(\s*display-p3\b/i.test(colorSource.trim());
117
+ }
118
+ function hasUnsupportedModernColorFunction(value) {
119
+ const parsed = valueParser(value);
120
+ let hasUnsupported = false;
121
+ parsed.walk((node) => {
122
+ if (node.type !== "function") return;
123
+ const name = node.value.toLowerCase();
124
+ if (name === COLOR_MIX_NAME || MODERN_COLOR_FUNCTION_NAMES.has(name) || name === "color" && isDisplayP3ColorFunction(valueParser.stringify(node))) {
125
+ hasUnsupported = true;
126
+ return false;
127
+ }
128
+ });
129
+ return hasUnsupported;
130
+ }
131
+ function normalizeModernColorValue(value, customPropertyValues = /* @__PURE__ */ new Map()) {
132
+ if (!hasUnsupportedModernColorFunction(value)) return {
133
+ value,
134
+ changed: false,
135
+ hasUnsupported: false
136
+ };
137
+ const parsed = valueParser(value);
138
+ let changed = false;
139
+ parsed.walk((node) => {
140
+ if (node.type !== "function") return;
141
+ const name = node.value.toLowerCase();
142
+ const source = valueParser.stringify(node);
143
+ let normalized;
144
+ if (MODERN_COLOR_FUNCTION_NAMES.has(name) || name === "color" && isDisplayP3ColorFunction(source)) normalized = normalizeStandaloneColorFunction(source);
145
+ else if (name === COLOR_MIX_NAME) normalized = tryResolveColorMix(node, customPropertyValues)?.value;
146
+ if (!normalized) return;
147
+ const mutableNode = node;
148
+ mutableNode.type = "word";
149
+ mutableNode.value = normalized;
150
+ delete mutableNode.nodes;
151
+ changed = true;
152
+ });
153
+ const nextValue = changed ? parsed.toString() : value;
154
+ return {
155
+ value: nextValue,
156
+ changed,
157
+ hasUnsupported: hasUnsupportedModernColorFunction(nextValue)
158
+ };
56
159
  }
57
- function shouldProtectColorMix(node) {
58
- return splitArguments(node.nodes).slice(1).some(hasDynamicStopAlpha);
160
+ function createRgbaWithAlpha(colorSource, alphaSource, customPropertyValues) {
161
+ const resolvedColor = resolveColorData(colorSource, customPropertyValues);
162
+ if (!resolvedColor) return;
163
+ const [red, green, blue] = resolvedColor.channels.map((channel) => Math.round(Math.min(1, Math.max(0, channel)) * 255));
164
+ const alpha = alphaSource.trim();
165
+ return `rgba(${red}, ${green}, ${blue}, ${CUSTOM_PROPERTY_RE.test(alpha) ? `var(${alpha})` : alpha})`;
166
+ }
167
+ function tryResolveColorMix(node, customPropertyValues) {
168
+ const args = splitArguments(node.nodes);
169
+ if (args.length < 3) return;
170
+ const colorStopNodes = splitStopSegments(args[1] ?? []);
171
+ if (colorStopNodes.length < 2) return;
172
+ const colorNodes = trimNodes$1(colorStopNodes[0] ?? []);
173
+ const alphaNodes = trimNodes$1(colorStopNodes[1] ?? []);
174
+ const trailingNodes = trimNodes$1(args[2] ?? []);
175
+ if (!colorNodes.length || !alphaNodes.length || valueParser.stringify(trailingNodes).trim().toLowerCase() !== "transparent") return;
176
+ const colorSource = valueParser.stringify(colorNodes).trim();
177
+ const alphaSource = valueParser.stringify(alphaNodes).trim();
178
+ if (!colorSource || !alphaSource || INTERNAL_TAILWIND_ALPHA_RE.test(alphaSource)) return;
179
+ if (CURRENT_COLOR_RE.test(colorSource)) return {
180
+ value: colorSource,
181
+ deferred: false
182
+ };
183
+ if (DYNAMIC_ALPHA_RE.test(alphaSource)) {
184
+ const normalized = createRgbaWithAlpha(colorSource, alphaSource, customPropertyValues);
185
+ return normalized ? {
186
+ value: normalized,
187
+ deferred: true
188
+ } : {
189
+ value: colorSource,
190
+ deferred: true
191
+ };
192
+ }
193
+ const alpha = parseAlphaValue(alphaSource);
194
+ if (alpha === void 0) return;
195
+ const normalized = normalizeColorFunctionName(colorSource, alpha, customPropertyValues);
196
+ if (normalized) return {
197
+ value: normalized,
198
+ deferred: false
199
+ };
200
+ return {
201
+ value: colorSource,
202
+ deferred: false
203
+ };
59
204
  }
60
205
  function createPlaceholder(index) {
61
206
  return `${PLACEHOLDER_PREFIX}${index}__`;
@@ -66,40 +211,54 @@ function unwrapProtectedSupports(cssRoot) {
66
211
  atRule.replaceWith(atRule.nodes);
67
212
  });
68
213
  }
69
- function protectDynamicColorMixAlpha(css) {
214
+ function protectDynamicColorMixAlpha(css, options = {}) {
70
215
  if (!css.includes(COLOR_MIX_NAME)) return {
71
216
  css,
72
217
  restore: (value) => value
73
218
  };
74
219
  const replacements = /* @__PURE__ */ new Map();
75
220
  const root = postcss.parse(css);
221
+ const customPropertyValues = new Map(options.customPropertyValues);
222
+ let changed = false;
223
+ root.walkDecls((decl) => {
224
+ if (decl.prop.startsWith("--") && !decl.value.includes(COLOR_MIX_NAME)) customPropertyValues.set(decl.prop, decl.value.trim());
225
+ });
76
226
  root.walkDecls((decl) => {
77
227
  if (!decl.value.includes(COLOR_MIX_NAME)) return;
78
228
  const parsed = valueParser(decl.value);
79
229
  let mutated = false;
80
230
  parsed.walk((node) => {
81
231
  if (node.type !== "function" || node.value.toLowerCase() !== COLOR_MIX_NAME) return;
82
- if (!shouldProtectColorMix(node)) return;
83
- const placeholder = createPlaceholder(replacements.size);
84
- replacements.set(placeholder, valueParser.stringify(node));
85
- const mutableNode = node;
86
- mutableNode.type = "word";
87
- mutableNode.value = placeholder;
88
- delete mutableNode.nodes;
89
- mutated = true;
232
+ const resolved = tryResolveColorMix(node, customPropertyValues);
233
+ if (resolved) {
234
+ if (resolved.deferred) {
235
+ const placeholder = createPlaceholder(replacements.size);
236
+ replacements.set(placeholder, resolved.value);
237
+ const mutableNode = node;
238
+ mutableNode.type = "word";
239
+ mutableNode.value = placeholder;
240
+ delete mutableNode.nodes;
241
+ mutated = true;
242
+ return;
243
+ }
244
+ const mutableNode = node;
245
+ mutableNode.type = "word";
246
+ mutableNode.value = resolved.value;
247
+ delete mutableNode.nodes;
248
+ mutated = true;
249
+ }
90
250
  });
91
- if (mutated) decl.value = parsed.toString();
251
+ if (mutated) {
252
+ decl.value = parsed.toString();
253
+ changed = true;
254
+ }
92
255
  });
93
- if (replacements.size === 0) return {
94
- css,
95
- restore: (value) => value
96
- };
97
- unwrapProtectedSupports(root);
256
+ if (replacements.size > 0) unwrapProtectedSupports(root);
98
257
  return {
99
- css: root.toString(),
258
+ css: changed ? root.toString() : css,
100
259
  restore(value) {
101
260
  let restored = value;
102
- for (const [placeholder, original] of replacements) restored = restored.split(placeholder).join(original);
261
+ for (const [placeholder, replacement] of replacements) restored = restored.split(placeholder).join(replacement);
103
262
  return restored;
104
263
  }
105
264
  };
@@ -131,7 +290,7 @@ function isBaseCarrierSelector(selector) {
131
290
  function isBaseCarrierRule(rule) {
132
291
  return Array.isArray(rule.selectors) && rule.selectors.length > 0 && rule.selectors.every(isBaseCarrierSelector);
133
292
  }
134
- function hasClassSelector(rule) {
293
+ function hasClassSelector$1(rule) {
135
294
  return Array.isArray(rule.selectors) && rule.selectors.some((selector) => CLASS_SELECTOR_RE.test(selector));
136
295
  }
137
296
  function collectRequiredTwVars(value) {
@@ -161,7 +320,7 @@ function extractUniAppXBaseDefaults(result) {
161
320
  function injectUniAppXBaseDefaults(result, defaults) {
162
321
  if (defaults.size === 0) return;
163
322
  result.root.walkRules((rule) => {
164
- if (!hasClassSelector(rule)) return;
323
+ if (!hasClassSelector$1(rule)) return;
165
324
  const declaredProps = /* @__PURE__ */ new Set();
166
325
  const requiredProps = /* @__PURE__ */ new Set();
167
326
  rule.walkDecls((decl) => {
@@ -811,6 +970,8 @@ const nodes = [
811
970
  property("--tw-scroll-snap-strictness", "proximity", "*"),
812
971
  property("--tw-space-x-reverse", "0"),
813
972
  property("--tw-space-y-reverse", "0"),
973
+ property("--tw-scrollbar-thumb", "#0000", "<color>"),
974
+ property("--tw-scrollbar-track", "#0000", "<color>"),
814
975
  property("--tw-border-style", "solid"),
815
976
  property("--tw-divide-x-reverse", "0"),
816
977
  property("--tw-divide-y-reverse", "0"),
@@ -935,13 +1096,46 @@ const DISPLAY_P3_VALUE_RE = /color\(\s*display-p3\b/i;
935
1096
  const COLOR_GAMUT_P3_RE = /\(\s*color-gamut\s*:\s*p3\s*\)/i;
936
1097
  const RADIUS_VALUE_RE = /\b([+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:e[+-]?\d+)?)\s*(r?px)\b/gi;
937
1098
  const SCIENTIFIC_NOTATION_RE = /e/i;
1099
+ const TW_VAR_FUNCTION_RE = /var\(\s*(--tw-[\w-]+)\b/g;
1100
+ const TW_CONTENT_VAR_RE = /var\(\s*--tw-content\b/;
938
1101
  function isTailwindcssV4(options) {
939
1102
  return options?.majorVersion === 4;
940
1103
  }
941
1104
  function testIfRootHostForV4(node) {
942
1105
  return node.type === "rule" && node.selector.includes(":root") && node.selector.includes(":host");
943
1106
  }
944
- const cssVarsV4Nodes = createCssVarNodes(nodes);
1107
+ createCssVarNodes(nodes);
1108
+ function collectUsedTailwindcssV4Variables(root) {
1109
+ const props = /* @__PURE__ */ new Set();
1110
+ root.walkDecls((decl) => {
1111
+ if (decl.prop.startsWith("--tw-")) props.add(decl.prop);
1112
+ TW_VAR_FUNCTION_RE.lastIndex = 0;
1113
+ let match = TW_VAR_FUNCTION_RE.exec(decl.value);
1114
+ while (match !== null) {
1115
+ const prop = match[1];
1116
+ if (prop) props.add(prop);
1117
+ match = TW_VAR_FUNCTION_RE.exec(decl.value);
1118
+ }
1119
+ });
1120
+ root.walkAtRules("property", (atRule) => {
1121
+ const prop = atRule.params.trim();
1122
+ if (prop.startsWith("--tw-")) props.add(prop);
1123
+ });
1124
+ return props;
1125
+ }
1126
+ function usesTailwindcssV4ContentVariable(root) {
1127
+ let used = false;
1128
+ root.walkDecls((decl) => {
1129
+ if (TW_CONTENT_VAR_RE.test(decl.value)) used = true;
1130
+ });
1131
+ return used;
1132
+ }
1133
+ function createUsedCssVarsV4Nodes(usedProps) {
1134
+ return nodes.filter((def) => usedProps.has(def.prop)).map((def) => new Declaration({
1135
+ prop: def.prop,
1136
+ value: def.value
1137
+ }));
1138
+ }
945
1139
  function isTailwindcssV4ModernCheck(atRule) {
946
1140
  return atRule.name === "supports" && [
947
1141
  MODERN_CHECK_WEBKIT_HYPHENS_RE,
@@ -2081,6 +2275,20 @@ function makePseudoVarRule() {
2081
2275
  }));
2082
2276
  return pseudoVarRule;
2083
2277
  }
2278
+ function isEmptyContentInitDeclaration(decl) {
2279
+ return decl.prop === "--tw-content" && (decl.value === "\"\"" || decl.value === "''");
2280
+ }
2281
+ function removeTailwindV4EmptyContentInit(node) {
2282
+ node.walkDecls((decl) => {
2283
+ if (isEmptyContentInitDeclaration(decl)) decl.remove();
2284
+ });
2285
+ }
2286
+ function hasClassSelector(node) {
2287
+ return node.selectors.some((selector) => selector.includes("."));
2288
+ }
2289
+ function isRootThemeScopeRule(node) {
2290
+ return node.selectors.length > 0 && node.selectors.every((selector) => selector === ":root" || selector === ":host" || DEFAULT_ROOT_SELECTORS.includes(selector));
2291
+ }
2084
2292
  function remakeCssVarSelector(selectors, options) {
2085
2293
  const { cssPreflightRange, cssSelectorReplacement } = options;
2086
2294
  if (cssPreflightRange === "all" && !selectors.includes(":not(not)")) selectors.push(":not(not)");
@@ -2102,6 +2310,7 @@ function resolveUniAppXVariableScopeSelectors(options) {
2102
2310
  function commonChunkPreflight(node, options) {
2103
2311
  const { ctx, cssInjectPreflight, injectAdditionalCssVarScope } = options;
2104
2312
  const uniAppXEnabled = isUniAppXEnabled(options);
2313
+ const isTailwindcss4 = isTailwindcssV4(options);
2105
2314
  const rootOption = options.cssSelectorReplacement?.root;
2106
2315
  const rootSelectors = rootOption === false || rootOption === void 0 ? [] : Array.isArray(rootOption) ? rootOption.filter(Boolean) : [rootOption];
2107
2316
  const hasHostSelector = node.selectors.some((selector) => selector.includes(":host"));
@@ -2111,31 +2320,33 @@ function commonChunkPreflight(node, options) {
2111
2320
  phase: "pre",
2112
2321
  reason: "append-host-selector"
2113
2322
  });
2323
+ if (isTailwindcss4 && !usesTailwindcssV4ContentVariable(node.root()) && (!hasClassSelector(node) || isRootThemeScopeRule(node))) removeTailwindV4EmptyContentInit(node);
2114
2324
  if (testIfVariablesScope(node) || uniAppXEnabled && node.selectors.includes("*") && hasTwVars(node, 2)) {
2115
2325
  ctx?.markVariablesScope(node);
2116
2326
  assignRuleSelectors(node, uniAppXEnabled ? resolveUniAppXVariableScopeSelectors(options) : remakeCssVarSelector(node.selectors, options), {
2117
2327
  phase: "pre",
2118
2328
  reason: "rewrite-variable-scope"
2119
2329
  });
2120
- if (!uniAppXEnabled) node.before(makePseudoVarRule());
2330
+ if (!uniAppXEnabled && !isTailwindcss4) node.before(makePseudoVarRule());
2121
2331
  if (typeof cssInjectPreflight === "function") node.append(...cssInjectPreflight());
2122
2332
  }
2123
- const isTailwindcss4 = isTailwindcssV4(options);
2124
2333
  if (injectAdditionalCssVarScope && (isTailwindcss4 ? testIfRootHostForV4(node) : testIfTwBackdrop(node))) {
2334
+ const nodes = isTailwindcss4 ? createUsedCssVarsV4Nodes(collectUsedTailwindcssV4Variables(node.root())) : cssVarsV3Nodes;
2335
+ if (nodes.length === 0) return;
2125
2336
  const syntheticRule = new Rule({
2126
2337
  selectors: uniAppXEnabled ? resolveUniAppXVariableScopeSelectors(options) : [
2127
2338
  "*",
2128
2339
  "::after",
2129
2340
  "::before"
2130
2341
  ],
2131
- nodes: isTailwindcss4 ? cssVarsV4Nodes : cssVarsV3Nodes
2342
+ nodes
2132
2343
  });
2133
2344
  if (!uniAppXEnabled) assignRuleSelectors(syntheticRule, remakeCssVarSelector(syntheticRule.selectors, options), {
2134
2345
  phase: "pre",
2135
2346
  reason: "rewrite-synthetic-variable-scope"
2136
2347
  });
2137
2348
  node.before(syntheticRule);
2138
- if (!uniAppXEnabled) node.before(makePseudoVarRule());
2349
+ if (!uniAppXEnabled && !isTailwindcss4) node.before(makePseudoVarRule());
2139
2350
  if (typeof cssInjectPreflight === "function") syntheticRule.append(...cssInjectPreflight());
2140
2351
  }
2141
2352
  }
@@ -2470,19 +2681,31 @@ function createStyleHandler(options) {
2470
2681
  const hasUserPlugins = Boolean(cachedOptions.postcssOptions?.plugins && (Array.isArray(cachedOptions.postcssOptions.plugins) ? cachedOptions.postcssOptions.plugins.length > 0 : typeof cachedOptions.postcssOptions.plugins === "object" && Object.keys(cachedOptions.postcssOptions.plugins).length > 0));
2471
2682
  const handler = ((rawSource, opt) => {
2472
2683
  const resolvedOptions = resolver.resolve(opt);
2684
+ const protectedColorMix = resolvedOptions.majorVersion === 4 ? protectDynamicColorMixAlpha(rawSource) : void 0;
2685
+ const source = protectedColorMix?.css ?? rawSource;
2473
2686
  let signal;
2474
2687
  if (!hasUserPlugins) try {
2475
- signal = probeFeatures(rawSource);
2688
+ signal = probeFeatures(source);
2476
2689
  } catch {
2477
2690
  signal = void 0;
2478
2691
  }
2479
- const cacheKey = `${getOptionsFingerprint(resolvedOptions)}|${signal ? signalToCacheKey(signal) : ""}|${simpleHash(rawSource)}`;
2692
+ const cacheKey = `${getOptionsFingerprint(resolvedOptions)}|${signal ? signalToCacheKey(signal) : ""}|${simpleHash(source)}`;
2480
2693
  const cachedResult = resultCache.get(cacheKey);
2481
2694
  if (cachedResult) return Promise.resolve(cachedResult);
2482
2695
  const processor = processorCache.getProcessor(resolvedOptions, signal);
2483
2696
  const processOptions = processorCache.getProcessOptions(resolvedOptions);
2484
- return processor.process(rawSource, processOptions).async().then((result) => {
2485
- const finalResult = applyUniAppXUvueCompatibility(applyUniAppXBaseCompatibility(result, resolvedOptions), resolvedOptions);
2697
+ return processor.process(source, processOptions).async().then((result) => {
2698
+ let finalResult = applyUniAppXUvueCompatibility(applyUniAppXBaseCompatibility(result, resolvedOptions), resolvedOptions);
2699
+ if (protectedColorMix) {
2700
+ const restoredCss = protectedColorMix.restore(finalResult.css);
2701
+ if (restoredCss !== finalResult.css) {
2702
+ const nextResult = finalResult.root.clone().toResult(finalResult.opts);
2703
+ nextResult.css = restoredCss;
2704
+ nextResult.root = postcss.parse(restoredCss, finalResult.opts);
2705
+ nextResult.messages.push(...finalResult.messages);
2706
+ finalResult = nextResult;
2707
+ }
2708
+ }
2486
2709
  resultCache.set(cacheKey, finalResult);
2487
2710
  return finalResult;
2488
2711
  });
@@ -2494,4 +2717,4 @@ function createStyleHandler(options) {
2494
2717
  return handler;
2495
2718
  }
2496
2719
  //#endregion
2497
- export { createFallbackPlaceholderReplacer, createInjectPreflight, createStyleHandler, createStylePipeline, internalCssSelectorReplacer, postcssHtmlTransform, protectDynamicColorMixAlpha };
2720
+ export { createFallbackPlaceholderReplacer, createInjectPreflight, createStyleHandler, createStylePipeline, internalCssSelectorReplacer, normalizeModernColorValue, postcssHtmlTransform, protectDynamicColorMixAlpha };
package/dist/types.d.ts CHANGED
@@ -23,13 +23,13 @@ export type RequiredStyleHandlerOptions = {
23
23
  /**
24
24
  * @description 默认为 true,此时会对样式主文件,进行猜测
25
25
  */
26
- isMainChunk?: boolean;
27
- cssPreflight?: CssPreflightOptions;
28
- cssInjectPreflight?: InjectPreflight;
29
- escapeMap?: Record<string, string>;
26
+ isMainChunk?: boolean | undefined;
27
+ cssPreflight?: CssPreflightOptions | undefined;
28
+ cssInjectPreflight?: InjectPreflight | undefined;
29
+ escapeMap?: Record<string, string> | undefined;
30
30
  } & Pick<UserDefinedPostcssOptions, 'cssPreflightRange' | 'cssChildCombinatorReplaceValue' | 'injectAdditionalCssVarScope' | 'cssSelectorReplacement' | 'rem2rpx' | 'px2rpx' | 'unitsToPx'>;
31
31
  export interface InternalCssSelectorReplacerOptions {
32
- escapeMap?: Record<string, string>;
32
+ escapeMap?: Record<string, string> | undefined;
33
33
  }
34
34
  interface CssCalcOptions extends PostCssCalcOptions {
35
35
  includeCustomProperties?: (string | RegExp)[];
@@ -41,43 +41,43 @@ export interface UnitsToPxOptions extends Pick<UnitConverterOptions, 'disabled'
41
41
  transform?: GlobalUnitTransform | false;
42
42
  }
43
43
  export type IStyleHandlerOptions = {
44
- ctx?: PostcssContext;
45
- postcssOptions?: LoadedPostcssOptions;
46
- cssRemoveProperty?: boolean;
47
- cssRemoveHoverPseudoClass?: boolean;
48
- cssPresetEnv?: PresetEnvOptions;
49
- autoprefixer?: WeappAutoprefixerOptions;
50
- cssCalc?: boolean | CssCalcOptions | (string | RegExp)[];
44
+ ctx?: PostcssContext | undefined;
45
+ postcssOptions?: LoadedPostcssOptions | undefined;
46
+ cssRemoveProperty?: boolean | undefined;
47
+ cssRemoveHoverPseudoClass?: boolean | undefined;
48
+ cssPresetEnv?: PresetEnvOptions | undefined;
49
+ autoprefixer?: WeappAutoprefixerOptions | undefined;
50
+ cssCalc?: boolean | CssCalcOptions | (string | RegExp)[] | undefined;
51
51
  atRules?: {
52
- property?: boolean;
53
- supports?: boolean;
54
- media?: boolean;
55
- };
56
- uniAppX?: boolean;
57
- uniAppXCssTarget?: UniAppXCssTarget;
58
- uniAppXUnsupported?: UniAppXUnsupportedMode;
59
- majorVersion?: number;
52
+ property?: boolean | undefined;
53
+ supports?: boolean | undefined;
54
+ media?: boolean | undefined;
55
+ } | undefined;
56
+ uniAppX?: boolean | undefined;
57
+ uniAppXCssTarget?: UniAppXCssTarget | undefined;
58
+ uniAppXUnsupported?: UniAppXUnsupportedMode | undefined;
59
+ majorVersion?: number | undefined;
60
60
  } & RequiredStyleHandlerOptions;
61
61
  export interface UserDefinedPostcssOptions {
62
- cssPreflight?: CssPreflightOptions;
63
- cssPreflightRange?: 'all';
64
- cssChildCombinatorReplaceValue?: string | string[];
65
- cssPresetEnv?: PresetEnvOptions;
66
- autoprefixer?: WeappAutoprefixerOptions;
67
- injectAdditionalCssVarScope?: boolean;
62
+ cssPreflight?: CssPreflightOptions | undefined;
63
+ cssPreflightRange?: 'all' | undefined;
64
+ cssChildCombinatorReplaceValue?: string | string[] | undefined;
65
+ cssPresetEnv?: PresetEnvOptions | undefined;
66
+ autoprefixer?: WeappAutoprefixerOptions | undefined;
67
+ injectAdditionalCssVarScope?: boolean | undefined;
68
68
  cssSelectorReplacement?: {
69
- root?: string | string[] | false;
70
- universal?: string | string[] | false;
71
- };
72
- rem2rpx?: boolean | Rem2rpxOptions;
73
- px2rpx?: boolean | Px2rpxOptions;
74
- unitsToPx?: boolean | UnitsToPxOptions;
75
- postcssOptions?: LoadedPostcssOptions;
76
- cssRemoveHoverPseudoClass?: boolean;
77
- cssRemoveProperty?: boolean;
78
- uniAppX?: boolean;
79
- uniAppXCssTarget?: UniAppXCssTarget;
80
- uniAppXUnsupported?: UniAppXUnsupportedMode;
69
+ root?: string | string[] | false | undefined;
70
+ universal?: string | string[] | false | undefined;
71
+ } | undefined;
72
+ rem2rpx?: boolean | Rem2rpxOptions | undefined;
73
+ px2rpx?: boolean | Px2rpxOptions | undefined;
74
+ unitsToPx?: boolean | UnitsToPxOptions | undefined;
75
+ postcssOptions?: LoadedPostcssOptions | undefined;
76
+ cssRemoveHoverPseudoClass?: boolean | undefined;
77
+ cssRemoveProperty?: boolean | undefined;
78
+ uniAppX?: boolean | undefined;
79
+ uniAppXCssTarget?: UniAppXCssTarget | undefined;
80
+ uniAppXUnsupported?: UniAppXUnsupportedMode | undefined;
81
81
  }
82
82
  export type { CssCalcOptions, PresetEnvOptions, Px2rpxOptions, Rem2rpxOptions, WeappAutoprefixerOptions, };
83
83
  export interface StyleHandler {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@weapp-tailwindcss/postcss",
3
- "version": "2.2.1-next.1",
3
+ "version": "2.2.1-next.3",
4
4
  "description": "@weapp-tailwindcss/postcss",
5
5
  "author": "ice breaker <1324318532@qq.com>",
6
6
  "license": "MIT",
@@ -56,11 +56,14 @@
56
56
  "node": "^20.19.0 || >=22.12.0"
57
57
  },
58
58
  "dependencies": {
59
+ "@csstools/css-color-parser": "^4.1.1",
60
+ "@csstools/css-parser-algorithms": "^4.0.0",
61
+ "@csstools/css-tokenizer": "^4.0.0",
59
62
  "@weapp-core/escape": "~7.0.0",
60
63
  "@weapp-tailwindcss/postcss-calc": "^1.0.0",
61
64
  "autoprefixer": "^10.5.0",
62
65
  "lru-cache": "10.4.3",
63
- "postcss": "^8.5.14",
66
+ "postcss": "^8.5.15",
64
67
  "postcss-preset-env": "^10.6.1",
65
68
  "postcss-pxtrans": "^1.0.4",
66
69
  "postcss-rem-to-responsive-pixel": "^7.0.4",