@plumeria/unplugin 18.4.0 → 18.4.1

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 (3) hide show
  1. package/dist/core.js +94 -172
  2. package/dist/core.mjs +93 -171
  3. package/package.json +2 -2
package/dist/core.js CHANGED
@@ -71,81 +71,6 @@ const isStaticArgValue = (node) => node.type === 'StringLiteral' ||
71
71
  (node.type === 'TemplateLiteral' && node.expressions.length === 0) ||
72
72
  utils_1.t.isIdentifier(node) ||
73
73
  utils_1.t.isMemberExpression(node);
74
- const namedParamsOf = (params, defaults) => {
75
- if (params.length !== 1)
76
- return undefined;
77
- const first = params[0];
78
- const pattern = (first?.pat ?? first);
79
- if (pattern?.type !== 'ObjectPattern')
80
- return undefined;
81
- const named = [];
82
- for (const prop of pattern.properties ?? []) {
83
- if (prop.type === 'AssignmentPatternProperty' && utils_1.t.isIdentifier(prop.key)) {
84
- named.push({ key: prop.key.value, local: prop.key.value });
85
- if (prop.value)
86
- defaults[prop.key.value] = prop.value;
87
- }
88
- else if (prop.type === 'KeyValuePatternProperty' &&
89
- utils_1.t.isIdentifier(prop.key) &&
90
- utils_1.t.isIdentifier(prop.value)) {
91
- named.push({ key: prop.key.value, local: prop.value.value });
92
- }
93
- else if (prop.type === 'KeyValuePatternProperty' &&
94
- utils_1.t.isIdentifier(prop.key) &&
95
- prop.value?.type === 'AssignmentPattern' &&
96
- utils_1.t.isIdentifier(prop.value.left)) {
97
- const pattern = prop.value;
98
- named.push({ key: String(prop.key.value), local: pattern.left.value });
99
- defaults[pattern.left.value] = pattern.right;
100
- }
101
- else {
102
- return undefined;
103
- }
104
- }
105
- return named.length > 0 ? named : undefined;
106
- };
107
- const styleFunctionsOf = (objExpr) => {
108
- const styleFunctions = {};
109
- objExpr.properties.forEach((prop) => {
110
- if (prop.type !== 'KeyValueProperty' || prop.key.type !== 'Identifier')
111
- return;
112
- const func = prop.value;
113
- if (func.type !== 'ArrowFunctionExpression' &&
114
- func.type !== 'FunctionExpression')
115
- return;
116
- const defaults = {};
117
- const params = func.params.map((p) => {
118
- const pattern = typeof p === 'object' && p !== null && 'pat' in p ? p.pat : p;
119
- if (utils_1.t.isIdentifier(pattern))
120
- return pattern.value;
121
- if (pattern?.type === 'AssignmentPattern' &&
122
- utils_1.t.isIdentifier(pattern.left)) {
123
- defaults[pattern.left.value] = pattern.right;
124
- return pattern.left.value;
125
- }
126
- return 'arg';
127
- });
128
- let actualBody = func.body;
129
- if (actualBody?.type === 'ParenthesisExpression')
130
- actualBody = actualBody.expression;
131
- if (actualBody?.type === 'BlockStatement') {
132
- const first = actualBody.stmts?.[0];
133
- if (first?.type === 'ReturnStatement')
134
- actualBody = first.argument;
135
- if (actualBody?.type === 'ParenthesisExpression')
136
- actualBody = actualBody.expression;
137
- }
138
- if (actualBody && actualBody.type === 'ObjectExpression') {
139
- styleFunctions[prop.key.value] = {
140
- params,
141
- named: namedParamsOf(func.params, defaults),
142
- defaults,
143
- body: actualBody,
144
- };
145
- }
146
- });
147
- return styleFunctions;
148
- };
149
74
  const foldDynamicVars = (vars) => {
150
75
  const grouped = new Map();
151
76
  [...vars]
@@ -167,59 +92,6 @@ const foldDynamicVars = (vars) => {
167
92
  return `"${cssVar}": ${value}`;
168
93
  });
169
94
  };
170
- const isUnitlessProp = (prop) => zss_engine_1.exceptionCamelCase.includes(prop) || prop.startsWith('--');
171
- const collectVarProps = (style, cssVar, props) => {
172
- const reference = `var(${cssVar})`;
173
- for (const [prop, value] of Object.entries(style)) {
174
- if (typeof value === 'string' && value.includes(reference))
175
- props.push(prop);
176
- else if (value !== null && typeof value === 'object')
177
- collectVarProps(value, cssVar, props);
178
- }
179
- };
180
- const retargetVar = (style, cssVar, next, unitless) => {
181
- const reference = `var(${cssVar})`;
182
- for (const [prop, value] of Object.entries(style)) {
183
- if (typeof value === 'string' && value.includes(reference)) {
184
- if (isUnitlessProp(prop) === unitless)
185
- style[prop] = value
186
- .split(reference)
187
- .join(`var(${next})`);
188
- }
189
- else if (value !== null && typeof value === 'object') {
190
- retargetVar(value, cssVar, next, unitless);
191
- }
192
- }
193
- };
194
- const splitVarByUnit = (style, cssVar) => {
195
- const props = [];
196
- collectVarProps(style, cssVar, props);
197
- if (props.length === 0)
198
- return [];
199
- const lead = props[0];
200
- const split = props.find((prop) => isUnitlessProp(prop) !== isUnitlessProp(lead));
201
- if (!split)
202
- return [{ cssVar, prop: lead }];
203
- const splitVar = `${cssVar}-${(0, zss_engine_1.camelToKebabCase)(split).replace(/^-+/, '')}`;
204
- retargetVar(style, cssVar, splitVar, isUnitlessProp(split));
205
- return [
206
- { cssVar, prop: lead },
207
- { cssVar: splitVar, prop: split },
208
- ];
209
- };
210
- const applyVarFallback = (style, cssVar, literal) => {
211
- const reference = `var(${cssVar})`;
212
- for (const [prop, value] of Object.entries(style)) {
213
- if (typeof value === 'string' && value.includes(reference)) {
214
- style[prop] = value
215
- .split(reference)
216
- .join(`var(${cssVar}, ${(0, zss_engine_1.applyCssValue)(literal, (0, zss_engine_1.camelToKebabCase)(prop))})`);
217
- }
218
- else if (value !== null && typeof value === 'object') {
219
- applyVarFallback(value, cssVar, literal);
220
- }
221
- }
222
- };
223
95
  const unpluginFactory = (options = {}, unpluginMeta) => {
224
96
  const filter = (0, pluginutils_1.createFilter)(options.include, options.exclude);
225
97
  const propertyPolicy = (0, utils_1.resolvePropertyPolicy)(options);
@@ -493,7 +365,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
493
365
  scannedTables.createHashTable[uniqueKey];
494
366
  }
495
367
  if (scannedTables.createFunctionTable[uniqueKey]) {
496
- createFunctionImportMap[localName] = styleFunctionsOf(scannedTables.createFunctionTable[uniqueKey]);
368
+ createFunctionImportMap[localName] = (0, utils_1.styleFunctionsOf)(scannedTables.createFunctionTable[uniqueKey]);
497
369
  }
498
370
  if (scannedTables.variantsHashTable[uniqueKey]) {
499
371
  variantsImportMap[localName] =
@@ -594,6 +466,16 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
594
466
  for (const key of Object.keys(createStaticImportMap)) {
595
467
  mergedCreateStaticHashTable[key] = createStaticImportMap[key];
596
468
  }
469
+ const dynamicStyleTables = {
470
+ keyframesHashTable: mergedKeyframesTable,
471
+ viewTransitionHashTable: mergedViewTransitionTable,
472
+ createThemeHashTable: mergedCreateThemeHashTable,
473
+ createThemeObjectTable: scannedTables.createThemeObjectTable,
474
+ createHashTable: mergedCreateTable,
475
+ createStaticHashTable: mergedCreateStaticHashTable,
476
+ createStaticObjectTable: scannedTables.createStaticObjectTable,
477
+ variantsHashTable: mergedVariantsTable,
478
+ };
597
479
  const localCreateStyles = {};
598
480
  const localStyleAliases = {};
599
481
  const checkStyleAliasAssignment = (decl) => {
@@ -654,7 +536,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
654
536
  records.forEach((r) => (atomMap[r.key] = r.hash));
655
537
  hashMap[key] = atomMap;
656
538
  });
657
- const styleFunctions = styleFunctionsOf(init.arguments[0].expression);
539
+ const styleFunctions = (0, utils_1.styleFunctionsOf)(init.arguments[0].expression);
658
540
  if (utils_1.t.isIdentifier(node.id)) {
659
541
  idSpans.add(node.id.span.start);
660
542
  }
@@ -995,7 +877,16 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
995
877
  }
996
878
  return null;
997
879
  };
998
- const resolveDynamicCall = (expr) => {
880
+ const isStyleFunctionCall = (expr) => {
881
+ if (!utils_1.t.isCallExpression(expr) || !utils_1.t.isMemberExpression(expr.callee))
882
+ return false;
883
+ const callee = expr.callee;
884
+ if (!utils_1.t.isIdentifier(callee.object) || !utils_1.t.isIdentifier(callee.property))
885
+ return false;
886
+ return Boolean(localCreateStyles[callee.object.value]?.functions?.[callee.property.value] ??
887
+ createFunctionImportMap[callee.object.value]?.[callee.property.value]);
888
+ };
889
+ const resolveDynamicCall = (expr, forceRuntime = false) => {
999
890
  if (!utils_1.t.isCallExpression(expr) || !utils_1.t.isMemberExpression(expr.callee))
1000
891
  return null;
1001
892
  const callee = expr.callee;
@@ -1039,7 +930,9 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1039
930
  throwCompilationError(`Plumeria: ${getSource(expr)} leaves "${key}" unset, and a dynamic style function has no value to fall back on.`, expr);
1040
931
  return;
1041
932
  }
1042
- if (isStaticArgValue(source) && argObj[key] !== undefined)
933
+ if (!forceRuntime &&
934
+ isStaticArgValue(source) &&
935
+ argObj[key] !== undefined)
1043
936
  tempStaticTable[local] = argObj[key];
1044
937
  else
1045
938
  runtime.push({ param: local, source });
@@ -1047,6 +940,8 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1047
940
  }
1048
941
  else if (callArgs.length === 1 &&
1049
942
  callArgs[0].expression.type === 'ObjectExpression') {
943
+ if (forceRuntime)
944
+ return null;
1050
945
  const argObj = resolveObjectArg(callArgs[0].expression) ?? {};
1051
946
  func.params.forEach((p) => {
1052
947
  if (argObj[p] !== undefined)
@@ -1061,39 +956,10 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1061
956
  runtime.push({ param: p, source: callArg.expression });
1062
957
  });
1063
958
  }
1064
- const withDefault = Object.entries(func.defaults ?? {}).filter(([param]) => tempStaticTable[param] === undefined);
1065
- const cssVars = {};
1066
- const varParams = Array.from(new Set([
1067
- ...runtime.map(({ param }) => param),
1068
- ...withDefault.map(([param]) => param),
1069
- ]));
1070
- if (varParams.length > 0) {
1071
- varParams.forEach((param) => (tempStaticTable[param] = param));
1072
- const probe = (0, utils_1.objectExpressionToObject)(func.body, tempStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
1073
- const hash = (0, zss_engine_1.genBase36Hash)(probe ?? {}, 1, 8);
1074
- varParams.forEach((param) => {
1075
- const cssVar = `--${hash}-${param}`;
1076
- tempStaticTable[param] = `var(${cssVar})`;
1077
- cssVars[param] = cssVar;
1078
- });
1079
- }
1080
- const style = (0, utils_1.objectExpressionToObject)(func.body, tempStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
1081
- if (!style)
959
+ const resolved = (0, utils_1.resolveDynamicStyle)(func, runtime.map(({ param }) => param), tempStaticTable, dynamicStyleTables);
960
+ if (!resolved)
1082
961
  return null;
1083
- const varGroups = new Map();
1084
- varParams.forEach((param) => {
1085
- const cssVar = cssVars[param];
1086
- if (cssVar)
1087
- varGroups.set(param, splitVarByUnit(style, cssVar));
1088
- });
1089
- withDefault.forEach(([param, expr]) => {
1090
- const literal = expr.type === 'StringLiteral' || expr.type === 'NumericLiteral'
1091
- ? expr.value
1092
- : undefined;
1093
- if (literal === undefined)
1094
- return;
1095
- (varGroups.get(param) ?? []).forEach(({ cssVar }) => applyVarFallback(style, cssVar, literal));
1096
- });
962
+ const { style, varGroups } = resolved;
1097
963
  const vars = [];
1098
964
  runtime.forEach(({ param, source }) => {
1099
965
  const groups = varGroups.get(param);
@@ -1117,7 +983,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1117
983
  valueExpr = JSON.stringify((0, zss_engine_1.applyCssValue)(argSource.slice(1, -1), kebabProp));
1118
984
  }
1119
985
  else {
1120
- valueExpr = isUnitlessProp(prop)
986
+ valueExpr = (0, utils_1.isUnitlessProp)(prop)
1121
987
  ? argSource
1122
988
  : `(typeof (${argSource}) === 'number' ? (${argSource}) + 'px' : (${argSource}))`;
1123
989
  }
@@ -1135,6 +1001,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1135
1001
  });
1136
1002
  const conditionals = [];
1137
1003
  const dynamicVars = [];
1004
+ const propVarSpreads = [];
1138
1005
  let groupIdCounter = 0;
1139
1006
  let sourceOrder = 0;
1140
1007
  const baseChunks = [];
@@ -1353,9 +1220,23 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1353
1220
  }
1354
1221
  if (!possibilities || possibilities.length === 0)
1355
1222
  return false;
1356
- const testLHS = gates.length
1357
- ? `((${gates.join(' && ')}) ? ${source} : "")`
1223
+ const carriesVars = possibilities.some((entry) => entry.hasVars);
1224
+ if (carriesVars && !isStyleProp) {
1225
+ throwCompilationError(`Plumeria: "${varName}" carries a dynamic function key, and css.use() returns only a class name. ` +
1226
+ `Apply it to ${styleProp} on the element instead.`, expr);
1227
+ }
1228
+ const gate = gates.length ? `(${gates.join(' && ')}) && ` : '';
1229
+ if (carriesVars) {
1230
+ const spread = `...(${gate}${source} && ${source}.vars)`;
1231
+ if (!propVarSpreads.includes(spread))
1232
+ propVarSpreads.push(spread);
1233
+ }
1234
+ const token = carriesVars
1235
+ ? `((${source} && ${source}.key) || ${source})`
1358
1236
  : source;
1237
+ const testLHS = gates.length
1238
+ ? `((${gates.join(' && ')}) ? ${token} : "")`
1239
+ : token;
1359
1240
  const currentGroupId = ++groupIdCounter;
1360
1241
  const currentOrder = sourceOrder++;
1361
1242
  const seen = new Set();
@@ -1594,6 +1475,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1594
1475
  isOptimizable,
1595
1476
  baseStyle,
1596
1477
  dynamicVars,
1478
+ propVarSpreads,
1597
1479
  };
1598
1480
  }
1599
1481
  const stateSources = baseChunks.map(({ order, style }) => ({ order, style: style }));
@@ -1849,7 +1731,13 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1849
1731
  classParts.push(`(${JSON.stringify(results)}[${masterKeyExpr || '""'}] || ${baseConflictClass ? JSON.stringify(baseConflictClass) : '""'})`);
1850
1732
  }
1851
1733
  classParts.push(...dynamicClassParts);
1852
- return { classParts, isOptimizable, baseStyle, dynamicVars };
1734
+ return {
1735
+ classParts,
1736
+ isOptimizable,
1737
+ baseStyle,
1738
+ dynamicVars,
1739
+ propVarSpreads,
1740
+ };
1853
1741
  };
1854
1742
  (0, utils_1.traverse)(ast, {
1855
1743
  JSXOpeningElement({ node }) {
@@ -2050,14 +1938,40 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2050
1938
  node.value &&
2051
1939
  node.value.type === 'JSXExpressionContainer') {
2052
1940
  const expr = node.value.expression;
1941
+ const carrierFor = (subNode) => {
1942
+ const vars = [];
1943
+ let unresolved = false;
1944
+ (0, utils_1.traverse)(subNode, {
1945
+ CallExpression({ node: call }) {
1946
+ if (!isStyleFunctionCall(call))
1947
+ return;
1948
+ const resolved = resolveDynamicCall(call, true);
1949
+ if (!resolved) {
1950
+ unresolved = true;
1951
+ return;
1952
+ }
1953
+ vars.push(...resolved.vars);
1954
+ },
1955
+ });
1956
+ if (unresolved)
1957
+ return null;
1958
+ return `{ ${foldDynamicVars(vars).join(', ')} }`;
1959
+ };
2053
1960
  const replaceWithKey = (subNode) => {
2054
1961
  const entry = list.find((x) => x.spanStart === subNode.span.start &&
2055
1962
  x.filePath === resourcePath);
2056
1963
  if (entry) {
1964
+ let content = JSON.stringify(entry.key);
1965
+ if (entry.hasVars) {
1966
+ const carrier = carrierFor(subNode);
1967
+ if (!carrier)
1968
+ return false;
1969
+ content = `{ key: ${content}, vars: ${carrier} }`;
1970
+ }
2057
1971
  replacements.push({
2058
1972
  start: subNode.span.start - baseByteOffset,
2059
1973
  end: subNode.span.end - baseByteOffset,
2060
- content: JSON.stringify(entry.key),
1974
+ content,
2061
1975
  });
2062
1976
  processStyleRecords(entry.styleObj);
2063
1977
  return true;
@@ -2075,6 +1989,11 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2075
1989
  excludeSubtreeSpans(subNode);
2076
1990
  }
2077
1991
  },
1992
+ CallExpression({ node: subNode }) {
1993
+ if (replaceWithKey(subNode)) {
1994
+ excludeSubtreeSpans(subNode);
1995
+ }
1996
+ },
2078
1997
  });
2079
1998
  }
2080
1999
  }
@@ -2150,9 +2069,12 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2150
2069
  }
2151
2070
  }
2152
2071
  }
2153
- const { classParts, isOptimizable, baseStyle, dynamicVars } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
2154
- const dynamicStyleParts = foldDynamicVars(dynamicVars);
2155
- const styleParts = [...existingStyleParts, ...dynamicStyleParts];
2072
+ const { classParts, isOptimizable, baseStyle, dynamicVars, propVarSpreads, } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
2073
+ const styleParts = [
2074
+ ...existingStyleParts,
2075
+ ...foldDynamicVars(dynamicVars),
2076
+ ...propVarSpreads,
2077
+ ];
2156
2078
  const styleAttr = styleParts.length > 0 || existingStyleExpr
2157
2079
  ? ` style={{ ${[existingStyleExpr, ...styleParts].filter(Boolean).join(', ')} }}`
2158
2080
  : '';
package/dist/core.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createFilter } from '@rollup/pluginutils';
2
2
  import { parseSync } from '@swc/core';
3
3
  import * as path from 'path';
4
- import { applyCssValue, genBase36Hash, exceptionCamelCase, camelToKebabCase, isAtRule, } from 'zss-engine';
5
- import { traverse, resolvePropertyPolicy, assertPropertyPolicy, collectReferenceIdentifiers, getStyleRecords, getStateWeights, collectLocalConsts, objectExpressionToObject, t, getRootIdentifier, extractOndemandStyles, deepMerge, scanAll, resolveFileError, resolveImportPath, getLeadingCommentLength, optimizer, themeHashOf, getFileDependencies, resolveExport, resolveComponentKey, resolveThemeSelector, DEFAULT_STYLE_PROP, } from '@plumeria/utils';
4
+ import { applyCssValue, genBase36Hash, camelToKebabCase, isAtRule, } from 'zss-engine';
5
+ import { traverse, resolvePropertyPolicy, assertPropertyPolicy, collectReferenceIdentifiers, getStyleRecords, getStateWeights, collectLocalConsts, objectExpressionToObject, t, getRootIdentifier, extractOndemandStyles, deepMerge, scanAll, resolveFileError, resolveImportPath, getLeadingCommentLength, optimizer, styleFunctionsOf, isUnitlessProp, resolveDynamicStyle, themeHashOf, getFileDependencies, resolveExport, resolveComponentKey, resolveThemeSelector, DEFAULT_STYLE_PROP, } from '@plumeria/utils';
6
6
  export const TARGET_EXTENSIONS = ['ts', 'tsx', 'js', 'jsx'];
7
7
  export const EXTENSION_PATTERN = /\.(ts|tsx|js|jsx)$/;
8
8
  const unwrapExport = (node) => {
@@ -35,81 +35,6 @@ const isStaticArgValue = (node) => node.type === 'StringLiteral' ||
35
35
  (node.type === 'TemplateLiteral' && node.expressions.length === 0) ||
36
36
  t.isIdentifier(node) ||
37
37
  t.isMemberExpression(node);
38
- const namedParamsOf = (params, defaults) => {
39
- if (params.length !== 1)
40
- return undefined;
41
- const first = params[0];
42
- const pattern = (first?.pat ?? first);
43
- if (pattern?.type !== 'ObjectPattern')
44
- return undefined;
45
- const named = [];
46
- for (const prop of pattern.properties ?? []) {
47
- if (prop.type === 'AssignmentPatternProperty' && t.isIdentifier(prop.key)) {
48
- named.push({ key: prop.key.value, local: prop.key.value });
49
- if (prop.value)
50
- defaults[prop.key.value] = prop.value;
51
- }
52
- else if (prop.type === 'KeyValuePatternProperty' &&
53
- t.isIdentifier(prop.key) &&
54
- t.isIdentifier(prop.value)) {
55
- named.push({ key: prop.key.value, local: prop.value.value });
56
- }
57
- else if (prop.type === 'KeyValuePatternProperty' &&
58
- t.isIdentifier(prop.key) &&
59
- prop.value?.type === 'AssignmentPattern' &&
60
- t.isIdentifier(prop.value.left)) {
61
- const pattern = prop.value;
62
- named.push({ key: String(prop.key.value), local: pattern.left.value });
63
- defaults[pattern.left.value] = pattern.right;
64
- }
65
- else {
66
- return undefined;
67
- }
68
- }
69
- return named.length > 0 ? named : undefined;
70
- };
71
- const styleFunctionsOf = (objExpr) => {
72
- const styleFunctions = {};
73
- objExpr.properties.forEach((prop) => {
74
- if (prop.type !== 'KeyValueProperty' || prop.key.type !== 'Identifier')
75
- return;
76
- const func = prop.value;
77
- if (func.type !== 'ArrowFunctionExpression' &&
78
- func.type !== 'FunctionExpression')
79
- return;
80
- const defaults = {};
81
- const params = func.params.map((p) => {
82
- const pattern = typeof p === 'object' && p !== null && 'pat' in p ? p.pat : p;
83
- if (t.isIdentifier(pattern))
84
- return pattern.value;
85
- if (pattern?.type === 'AssignmentPattern' &&
86
- t.isIdentifier(pattern.left)) {
87
- defaults[pattern.left.value] = pattern.right;
88
- return pattern.left.value;
89
- }
90
- return 'arg';
91
- });
92
- let actualBody = func.body;
93
- if (actualBody?.type === 'ParenthesisExpression')
94
- actualBody = actualBody.expression;
95
- if (actualBody?.type === 'BlockStatement') {
96
- const first = actualBody.stmts?.[0];
97
- if (first?.type === 'ReturnStatement')
98
- actualBody = first.argument;
99
- if (actualBody?.type === 'ParenthesisExpression')
100
- actualBody = actualBody.expression;
101
- }
102
- if (actualBody && actualBody.type === 'ObjectExpression') {
103
- styleFunctions[prop.key.value] = {
104
- params,
105
- named: namedParamsOf(func.params, defaults),
106
- defaults,
107
- body: actualBody,
108
- };
109
- }
110
- });
111
- return styleFunctions;
112
- };
113
38
  const foldDynamicVars = (vars) => {
114
39
  const grouped = new Map();
115
40
  [...vars]
@@ -131,59 +56,6 @@ const foldDynamicVars = (vars) => {
131
56
  return `"${cssVar}": ${value}`;
132
57
  });
133
58
  };
134
- const isUnitlessProp = (prop) => exceptionCamelCase.includes(prop) || prop.startsWith('--');
135
- const collectVarProps = (style, cssVar, props) => {
136
- const reference = `var(${cssVar})`;
137
- for (const [prop, value] of Object.entries(style)) {
138
- if (typeof value === 'string' && value.includes(reference))
139
- props.push(prop);
140
- else if (value !== null && typeof value === 'object')
141
- collectVarProps(value, cssVar, props);
142
- }
143
- };
144
- const retargetVar = (style, cssVar, next, unitless) => {
145
- const reference = `var(${cssVar})`;
146
- for (const [prop, value] of Object.entries(style)) {
147
- if (typeof value === 'string' && value.includes(reference)) {
148
- if (isUnitlessProp(prop) === unitless)
149
- style[prop] = value
150
- .split(reference)
151
- .join(`var(${next})`);
152
- }
153
- else if (value !== null && typeof value === 'object') {
154
- retargetVar(value, cssVar, next, unitless);
155
- }
156
- }
157
- };
158
- const splitVarByUnit = (style, cssVar) => {
159
- const props = [];
160
- collectVarProps(style, cssVar, props);
161
- if (props.length === 0)
162
- return [];
163
- const lead = props[0];
164
- const split = props.find((prop) => isUnitlessProp(prop) !== isUnitlessProp(lead));
165
- if (!split)
166
- return [{ cssVar, prop: lead }];
167
- const splitVar = `${cssVar}-${camelToKebabCase(split).replace(/^-+/, '')}`;
168
- retargetVar(style, cssVar, splitVar, isUnitlessProp(split));
169
- return [
170
- { cssVar, prop: lead },
171
- { cssVar: splitVar, prop: split },
172
- ];
173
- };
174
- const applyVarFallback = (style, cssVar, literal) => {
175
- const reference = `var(${cssVar})`;
176
- for (const [prop, value] of Object.entries(style)) {
177
- if (typeof value === 'string' && value.includes(reference)) {
178
- style[prop] = value
179
- .split(reference)
180
- .join(`var(${cssVar}, ${applyCssValue(literal, camelToKebabCase(prop))})`);
181
- }
182
- else if (value !== null && typeof value === 'object') {
183
- applyVarFallback(value, cssVar, literal);
184
- }
185
- }
186
- };
187
59
  export const unpluginFactory = (options = {}, unpluginMeta) => {
188
60
  const filter = createFilter(options.include, options.exclude);
189
61
  const propertyPolicy = resolvePropertyPolicy(options);
@@ -558,6 +430,16 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
558
430
  for (const key of Object.keys(createStaticImportMap)) {
559
431
  mergedCreateStaticHashTable[key] = createStaticImportMap[key];
560
432
  }
433
+ const dynamicStyleTables = {
434
+ keyframesHashTable: mergedKeyframesTable,
435
+ viewTransitionHashTable: mergedViewTransitionTable,
436
+ createThemeHashTable: mergedCreateThemeHashTable,
437
+ createThemeObjectTable: scannedTables.createThemeObjectTable,
438
+ createHashTable: mergedCreateTable,
439
+ createStaticHashTable: mergedCreateStaticHashTable,
440
+ createStaticObjectTable: scannedTables.createStaticObjectTable,
441
+ variantsHashTable: mergedVariantsTable,
442
+ };
561
443
  const localCreateStyles = {};
562
444
  const localStyleAliases = {};
563
445
  const checkStyleAliasAssignment = (decl) => {
@@ -959,7 +841,16 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
959
841
  }
960
842
  return null;
961
843
  };
962
- const resolveDynamicCall = (expr) => {
844
+ const isStyleFunctionCall = (expr) => {
845
+ if (!t.isCallExpression(expr) || !t.isMemberExpression(expr.callee))
846
+ return false;
847
+ const callee = expr.callee;
848
+ if (!t.isIdentifier(callee.object) || !t.isIdentifier(callee.property))
849
+ return false;
850
+ return Boolean(localCreateStyles[callee.object.value]?.functions?.[callee.property.value] ??
851
+ createFunctionImportMap[callee.object.value]?.[callee.property.value]);
852
+ };
853
+ const resolveDynamicCall = (expr, forceRuntime = false) => {
963
854
  if (!t.isCallExpression(expr) || !t.isMemberExpression(expr.callee))
964
855
  return null;
965
856
  const callee = expr.callee;
@@ -1003,7 +894,9 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1003
894
  throwCompilationError(`Plumeria: ${getSource(expr)} leaves "${key}" unset, and a dynamic style function has no value to fall back on.`, expr);
1004
895
  return;
1005
896
  }
1006
- if (isStaticArgValue(source) && argObj[key] !== undefined)
897
+ if (!forceRuntime &&
898
+ isStaticArgValue(source) &&
899
+ argObj[key] !== undefined)
1007
900
  tempStaticTable[local] = argObj[key];
1008
901
  else
1009
902
  runtime.push({ param: local, source });
@@ -1011,6 +904,8 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1011
904
  }
1012
905
  else if (callArgs.length === 1 &&
1013
906
  callArgs[0].expression.type === 'ObjectExpression') {
907
+ if (forceRuntime)
908
+ return null;
1014
909
  const argObj = resolveObjectArg(callArgs[0].expression) ?? {};
1015
910
  func.params.forEach((p) => {
1016
911
  if (argObj[p] !== undefined)
@@ -1025,39 +920,10 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1025
920
  runtime.push({ param: p, source: callArg.expression });
1026
921
  });
1027
922
  }
1028
- const withDefault = Object.entries(func.defaults ?? {}).filter(([param]) => tempStaticTable[param] === undefined);
1029
- const cssVars = {};
1030
- const varParams = Array.from(new Set([
1031
- ...runtime.map(({ param }) => param),
1032
- ...withDefault.map(([param]) => param),
1033
- ]));
1034
- if (varParams.length > 0) {
1035
- varParams.forEach((param) => (tempStaticTable[param] = param));
1036
- const probe = objectExpressionToObject(func.body, tempStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
1037
- const hash = genBase36Hash(probe ?? {}, 1, 8);
1038
- varParams.forEach((param) => {
1039
- const cssVar = `--${hash}-${param}`;
1040
- tempStaticTable[param] = `var(${cssVar})`;
1041
- cssVars[param] = cssVar;
1042
- });
1043
- }
1044
- const style = objectExpressionToObject(func.body, tempStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
1045
- if (!style)
923
+ const resolved = resolveDynamicStyle(func, runtime.map(({ param }) => param), tempStaticTable, dynamicStyleTables);
924
+ if (!resolved)
1046
925
  return null;
1047
- const varGroups = new Map();
1048
- varParams.forEach((param) => {
1049
- const cssVar = cssVars[param];
1050
- if (cssVar)
1051
- varGroups.set(param, splitVarByUnit(style, cssVar));
1052
- });
1053
- withDefault.forEach(([param, expr]) => {
1054
- const literal = expr.type === 'StringLiteral' || expr.type === 'NumericLiteral'
1055
- ? expr.value
1056
- : undefined;
1057
- if (literal === undefined)
1058
- return;
1059
- (varGroups.get(param) ?? []).forEach(({ cssVar }) => applyVarFallback(style, cssVar, literal));
1060
- });
926
+ const { style, varGroups } = resolved;
1061
927
  const vars = [];
1062
928
  runtime.forEach(({ param, source }) => {
1063
929
  const groups = varGroups.get(param);
@@ -1099,6 +965,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1099
965
  });
1100
966
  const conditionals = [];
1101
967
  const dynamicVars = [];
968
+ const propVarSpreads = [];
1102
969
  let groupIdCounter = 0;
1103
970
  let sourceOrder = 0;
1104
971
  const baseChunks = [];
@@ -1317,9 +1184,23 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1317
1184
  }
1318
1185
  if (!possibilities || possibilities.length === 0)
1319
1186
  return false;
1320
- const testLHS = gates.length
1321
- ? `((${gates.join(' && ')}) ? ${source} : "")`
1187
+ const carriesVars = possibilities.some((entry) => entry.hasVars);
1188
+ if (carriesVars && !isStyleProp) {
1189
+ throwCompilationError(`Plumeria: "${varName}" carries a dynamic function key, and css.use() returns only a class name. ` +
1190
+ `Apply it to ${styleProp} on the element instead.`, expr);
1191
+ }
1192
+ const gate = gates.length ? `(${gates.join(' && ')}) && ` : '';
1193
+ if (carriesVars) {
1194
+ const spread = `...(${gate}${source} && ${source}.vars)`;
1195
+ if (!propVarSpreads.includes(spread))
1196
+ propVarSpreads.push(spread);
1197
+ }
1198
+ const token = carriesVars
1199
+ ? `((${source} && ${source}.key) || ${source})`
1322
1200
  : source;
1201
+ const testLHS = gates.length
1202
+ ? `((${gates.join(' && ')}) ? ${token} : "")`
1203
+ : token;
1323
1204
  const currentGroupId = ++groupIdCounter;
1324
1205
  const currentOrder = sourceOrder++;
1325
1206
  const seen = new Set();
@@ -1558,6 +1439,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1558
1439
  isOptimizable,
1559
1440
  baseStyle,
1560
1441
  dynamicVars,
1442
+ propVarSpreads,
1561
1443
  };
1562
1444
  }
1563
1445
  const stateSources = baseChunks.map(({ order, style }) => ({ order, style: style }));
@@ -1813,7 +1695,13 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1813
1695
  classParts.push(`(${JSON.stringify(results)}[${masterKeyExpr || '""'}] || ${baseConflictClass ? JSON.stringify(baseConflictClass) : '""'})`);
1814
1696
  }
1815
1697
  classParts.push(...dynamicClassParts);
1816
- return { classParts, isOptimizable, baseStyle, dynamicVars };
1698
+ return {
1699
+ classParts,
1700
+ isOptimizable,
1701
+ baseStyle,
1702
+ dynamicVars,
1703
+ propVarSpreads,
1704
+ };
1817
1705
  };
1818
1706
  traverse(ast, {
1819
1707
  JSXOpeningElement({ node }) {
@@ -2014,14 +1902,40 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2014
1902
  node.value &&
2015
1903
  node.value.type === 'JSXExpressionContainer') {
2016
1904
  const expr = node.value.expression;
1905
+ const carrierFor = (subNode) => {
1906
+ const vars = [];
1907
+ let unresolved = false;
1908
+ traverse(subNode, {
1909
+ CallExpression({ node: call }) {
1910
+ if (!isStyleFunctionCall(call))
1911
+ return;
1912
+ const resolved = resolveDynamicCall(call, true);
1913
+ if (!resolved) {
1914
+ unresolved = true;
1915
+ return;
1916
+ }
1917
+ vars.push(...resolved.vars);
1918
+ },
1919
+ });
1920
+ if (unresolved)
1921
+ return null;
1922
+ return `{ ${foldDynamicVars(vars).join(', ')} }`;
1923
+ };
2017
1924
  const replaceWithKey = (subNode) => {
2018
1925
  const entry = list.find((x) => x.spanStart === subNode.span.start &&
2019
1926
  x.filePath === resourcePath);
2020
1927
  if (entry) {
1928
+ let content = JSON.stringify(entry.key);
1929
+ if (entry.hasVars) {
1930
+ const carrier = carrierFor(subNode);
1931
+ if (!carrier)
1932
+ return false;
1933
+ content = `{ key: ${content}, vars: ${carrier} }`;
1934
+ }
2021
1935
  replacements.push({
2022
1936
  start: subNode.span.start - baseByteOffset,
2023
1937
  end: subNode.span.end - baseByteOffset,
2024
- content: JSON.stringify(entry.key),
1938
+ content,
2025
1939
  });
2026
1940
  processStyleRecords(entry.styleObj);
2027
1941
  return true;
@@ -2039,6 +1953,11 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2039
1953
  excludeSubtreeSpans(subNode);
2040
1954
  }
2041
1955
  },
1956
+ CallExpression({ node: subNode }) {
1957
+ if (replaceWithKey(subNode)) {
1958
+ excludeSubtreeSpans(subNode);
1959
+ }
1960
+ },
2042
1961
  });
2043
1962
  }
2044
1963
  }
@@ -2114,9 +2033,12 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2114
2033
  }
2115
2034
  }
2116
2035
  }
2117
- const { classParts, isOptimizable, baseStyle, dynamicVars } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
2118
- const dynamicStyleParts = foldDynamicVars(dynamicVars);
2119
- const styleParts = [...existingStyleParts, ...dynamicStyleParts];
2036
+ const { classParts, isOptimizable, baseStyle, dynamicVars, propVarSpreads, } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
2037
+ const styleParts = [
2038
+ ...existingStyleParts,
2039
+ ...foldDynamicVars(dynamicVars),
2040
+ ...propVarSpreads,
2041
+ ];
2120
2042
  const styleAttr = styleParts.length > 0 || existingStyleExpr
2121
2043
  ? ` style={{ ${[existingStyleExpr, ...styleParts].filter(Boolean).join(', ')} }}`
2122
2044
  : '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/unplugin",
3
- "version": "18.4.0",
3
+ "version": "18.4.1",
4
4
  "description": "Universal Plumeria plugin for various build tools",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -94,7 +94,7 @@
94
94
  "dependencies": {
95
95
  "@rollup/pluginutils": "^5.4.0",
96
96
  "unplugin": "^3.0.0",
97
- "@plumeria/utils": "^18.4.0"
97
+ "@plumeria/utils": "^18.4.1"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@swc/core": "1.15.47",