@plumeria/unplugin 18.4.0 → 18.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/core.js +143 -182
  2. package/dist/core.mjs +142 -181
  3. package/package.json +2 -2
package/dist/core.js CHANGED
@@ -65,87 +65,37 @@ const componentFunctionOf = (node) => {
65
65
  }
66
66
  return undefined;
67
67
  };
68
+ const unwrapPatternDefault = (pattern) => pattern?.type === 'AssignmentPattern' ? pattern.left : pattern;
69
+ const destructuredPropAliases = (fn) => {
70
+ const aliases = new Map();
71
+ const first = fn.params[0];
72
+ const pattern = unwrapPatternDefault(first?.pat ?? first);
73
+ if (pattern?.type !== 'ObjectPattern')
74
+ return aliases;
75
+ for (const item of pattern.properties ?? []) {
76
+ if (item.type !== 'KeyValuePatternProperty')
77
+ continue;
78
+ const local = unwrapPatternDefault(item.value);
79
+ const key = item.key;
80
+ if (!utils_1.t.isIdentifier(local))
81
+ continue;
82
+ if (!utils_1.t.isIdentifier(key) && !utils_1.t.isStringLiteral(key))
83
+ continue;
84
+ const name = String(key.value);
85
+ if (name !== local.value)
86
+ aliases.set(local.value, name);
87
+ }
88
+ return aliases;
89
+ };
68
90
  const isStaticArgValue = (node) => node.type === 'StringLiteral' ||
69
91
  node.type === 'NumericLiteral' ||
70
92
  node.type === 'BooleanLiteral' ||
71
93
  (node.type === 'TemplateLiteral' && node.expressions.length === 0) ||
72
94
  utils_1.t.isIdentifier(node) ||
73
95
  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
- };
96
+ const isNoOpStyle = (node) => utils_1.t.isNullLiteral(node) ||
97
+ (utils_1.t.isBooleanLiteral(node) && node.value === false) ||
98
+ (utils_1.t.isIdentifier(node) && node.value === 'undefined');
149
99
  const foldDynamicVars = (vars) => {
150
100
  const grouped = new Map();
151
101
  [...vars]
@@ -167,59 +117,6 @@ const foldDynamicVars = (vars) => {
167
117
  return `"${cssVar}": ${value}`;
168
118
  });
169
119
  };
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
120
  const unpluginFactory = (options = {}, unpluginMeta) => {
224
121
  const filter = (0, pluginutils_1.createFilter)(options.include, options.exclude);
225
122
  const propertyPolicy = (0, utils_1.resolvePropertyPolicy)(options);
@@ -375,18 +272,22 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
375
272
  }
376
273
  }
377
274
  const components = [];
275
+ const propAliases = new Map();
276
+ const addComponent = (name, fn) => {
277
+ components.push({ name, node: fn });
278
+ const aliases = destructuredPropAliases(fn);
279
+ if (aliases.size > 0)
280
+ propAliases.set(name, aliases);
281
+ };
378
282
  for (const node of ast.body) {
379
283
  const statement = unwrapExport(node);
380
284
  if (isFunctionNode(statement)) {
381
- components.push({
382
- name: statement.identifier?.value ?? 'default',
383
- node: statement,
384
- });
285
+ addComponent(statement.identifier?.value ?? 'default', statement);
385
286
  }
386
287
  else if (statement?.type === 'CallExpression') {
387
288
  const fn = componentFunctionOf(statement);
388
289
  if (fn)
389
- components.push({ name: 'default', node: fn });
290
+ addComponent('default', fn);
390
291
  }
391
292
  else if (utils_1.t.isVariableDeclaration(statement)) {
392
293
  for (const decl of statement.declarations) {
@@ -394,7 +295,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
394
295
  continue;
395
296
  const fn = componentFunctionOf(decl.init);
396
297
  if (fn)
397
- components.push({ name: decl.id.value, node: fn });
298
+ addComponent(decl.id.value, fn);
398
299
  }
399
300
  }
400
301
  }
@@ -493,7 +394,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
493
394
  scannedTables.createHashTable[uniqueKey];
494
395
  }
495
396
  if (scannedTables.createFunctionTable[uniqueKey]) {
496
- createFunctionImportMap[localName] = styleFunctionsOf(scannedTables.createFunctionTable[uniqueKey]);
397
+ createFunctionImportMap[localName] = (0, utils_1.styleFunctionsOf)(scannedTables.createFunctionTable[uniqueKey]);
497
398
  }
498
399
  if (scannedTables.variantsHashTable[uniqueKey]) {
499
400
  variantsImportMap[localName] =
@@ -594,6 +495,16 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
594
495
  for (const key of Object.keys(createStaticImportMap)) {
595
496
  mergedCreateStaticHashTable[key] = createStaticImportMap[key];
596
497
  }
498
+ const dynamicStyleTables = {
499
+ keyframesHashTable: mergedKeyframesTable,
500
+ viewTransitionHashTable: mergedViewTransitionTable,
501
+ createThemeHashTable: mergedCreateThemeHashTable,
502
+ createThemeObjectTable: scannedTables.createThemeObjectTable,
503
+ createHashTable: mergedCreateTable,
504
+ createStaticHashTable: mergedCreateStaticHashTable,
505
+ createStaticObjectTable: scannedTables.createStaticObjectTable,
506
+ variantsHashTable: mergedVariantsTable,
507
+ };
597
508
  const localCreateStyles = {};
598
509
  const localStyleAliases = {};
599
510
  const checkStyleAliasAssignment = (decl) => {
@@ -654,7 +565,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
654
565
  records.forEach((r) => (atomMap[r.key] = r.hash));
655
566
  hashMap[key] = atomMap;
656
567
  });
657
- const styleFunctions = styleFunctionsOf(init.arguments[0].expression);
568
+ const styleFunctions = (0, utils_1.styleFunctionsOf)(init.arguments[0].expression);
658
569
  if (utils_1.t.isIdentifier(node.id)) {
659
570
  idSpans.add(node.id.span.start);
660
571
  }
@@ -995,7 +906,16 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
995
906
  }
996
907
  return null;
997
908
  };
998
- const resolveDynamicCall = (expr) => {
909
+ const isStyleFunctionCall = (expr) => {
910
+ if (!utils_1.t.isCallExpression(expr) || !utils_1.t.isMemberExpression(expr.callee))
911
+ return false;
912
+ const callee = expr.callee;
913
+ if (!utils_1.t.isIdentifier(callee.object) || !utils_1.t.isIdentifier(callee.property))
914
+ return false;
915
+ return Boolean(localCreateStyles[callee.object.value]?.functions?.[callee.property.value] ??
916
+ createFunctionImportMap[callee.object.value]?.[callee.property.value]);
917
+ };
918
+ const resolveDynamicCall = (expr, forceRuntime = false) => {
999
919
  if (!utils_1.t.isCallExpression(expr) || !utils_1.t.isMemberExpression(expr.callee))
1000
920
  return null;
1001
921
  const callee = expr.callee;
@@ -1039,7 +959,9 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1039
959
  throwCompilationError(`Plumeria: ${getSource(expr)} leaves "${key}" unset, and a dynamic style function has no value to fall back on.`, expr);
1040
960
  return;
1041
961
  }
1042
- if (isStaticArgValue(source) && argObj[key] !== undefined)
962
+ if (!forceRuntime &&
963
+ isStaticArgValue(source) &&
964
+ argObj[key] !== undefined)
1043
965
  tempStaticTable[local] = argObj[key];
1044
966
  else
1045
967
  runtime.push({ param: local, source });
@@ -1047,6 +969,8 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1047
969
  }
1048
970
  else if (callArgs.length === 1 &&
1049
971
  callArgs[0].expression.type === 'ObjectExpression') {
972
+ if (forceRuntime)
973
+ return null;
1050
974
  const argObj = resolveObjectArg(callArgs[0].expression) ?? {};
1051
975
  func.params.forEach((p) => {
1052
976
  if (argObj[p] !== undefined)
@@ -1061,39 +985,10 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1061
985
  runtime.push({ param: p, source: callArg.expression });
1062
986
  });
1063
987
  }
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)
988
+ const resolved = (0, utils_1.resolveDynamicStyle)(func, runtime.map(({ param }) => param), tempStaticTable, dynamicStyleTables);
989
+ if (!resolved)
1082
990
  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
- });
991
+ const { style, varGroups } = resolved;
1097
992
  const vars = [];
1098
993
  runtime.forEach(({ param, source }) => {
1099
994
  const groups = varGroups.get(param);
@@ -1117,7 +1012,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1117
1012
  valueExpr = JSON.stringify((0, zss_engine_1.applyCssValue)(argSource.slice(1, -1), kebabProp));
1118
1013
  }
1119
1014
  else {
1120
- valueExpr = isUnitlessProp(prop)
1015
+ valueExpr = (0, utils_1.isUnitlessProp)(prop)
1121
1016
  ? argSource
1122
1017
  : `(typeof (${argSource}) === 'number' ? (${argSource}) + 'px' : (${argSource}))`;
1123
1018
  }
@@ -1135,6 +1030,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1135
1030
  });
1136
1031
  const conditionals = [];
1137
1032
  const dynamicVars = [];
1033
+ const propVarSpreads = [];
1138
1034
  let groupIdCounter = 0;
1139
1035
  let sourceOrder = 0;
1140
1036
  const baseChunks = [];
@@ -1254,6 +1150,8 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1254
1150
  return buildDecision(node, prefix, off, { n: 0 }, scoped);
1255
1151
  };
1256
1152
  const collectConditions = (node, currentTestStrings = [], argOrder) => {
1153
+ if (isNoOpStyle(node))
1154
+ return true;
1257
1155
  let branchStyle = resolveStyleObject(node);
1258
1156
  if (!branchStyle) {
1259
1157
  const dynamic = resolveDynamicCall(node);
@@ -1334,17 +1232,20 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1334
1232
  ? expr.value
1335
1233
  : expr.property.value;
1336
1234
  const source = utils_1.t.isIdentifier(expr) ? varName : getSource(expr);
1337
- markStylePropApplied(varName, expr);
1338
1235
  const owner = ownerComponentOf(expr);
1236
+ const propName = (utils_1.t.isIdentifier(expr) && owner
1237
+ ? propAliases.get(owner)?.get(varName)
1238
+ : undefined) ?? varName;
1239
+ markStylePropApplied(propName, expr);
1339
1240
  let possibilities = owner
1340
- ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[varName]
1241
+ ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[propName]
1341
1242
  : undefined;
1342
1243
  if (!possibilities) {
1343
1244
  const candidates = [];
1344
1245
  for (const key of Object.keys(scannedTables.componentPropsTable || {})) {
1345
1246
  if (!key.startsWith(`${resourcePath}-`))
1346
1247
  continue;
1347
- const entries = scannedTables.componentPropsTable?.[key]?.[varName];
1248
+ const entries = scannedTables.componentPropsTable?.[key]?.[propName];
1348
1249
  if (entries)
1349
1250
  candidates.push(...entries);
1350
1251
  }
@@ -1353,9 +1254,23 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1353
1254
  }
1354
1255
  if (!possibilities || possibilities.length === 0)
1355
1256
  return false;
1356
- const testLHS = gates.length
1357
- ? `((${gates.join(' && ')}) ? ${source} : "")`
1257
+ const carriesVars = possibilities.some((entry) => entry.hasVars);
1258
+ if (carriesVars && !isStyleProp) {
1259
+ throwCompilationError(`Plumeria: "${varName}" carries a dynamic function key, and css.use() returns only a class name. ` +
1260
+ `Apply it to ${styleProp} on the element instead.`, expr);
1261
+ }
1262
+ const gate = gates.length ? `(${gates.join(' && ')}) && ` : '';
1263
+ if (carriesVars) {
1264
+ const spread = `...(${gate}${source} && ${source}.vars)`;
1265
+ if (!propVarSpreads.includes(spread))
1266
+ propVarSpreads.push(spread);
1267
+ }
1268
+ const token = carriesVars
1269
+ ? `((${source} && ${source}.key) || ${source})`
1358
1270
  : source;
1271
+ const testLHS = gates.length
1272
+ ? `((${gates.join(' && ')}) ? ${token} : "")`
1273
+ : token;
1359
1274
  const currentGroupId = ++groupIdCounter;
1360
1275
  const currentOrder = sourceOrder++;
1361
1276
  const seen = new Set();
@@ -1385,6 +1300,8 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1385
1300
  };
1386
1301
  for (const arg of args) {
1387
1302
  const expr = arg.expression;
1303
+ if (isNoOpStyle(expr))
1304
+ continue;
1388
1305
  if (pushPropPossibilities(expr, []))
1389
1306
  continue;
1390
1307
  if (utils_1.t.isCallExpression(expr) && utils_1.t.isIdentifier(expr.callee)) {
@@ -1594,6 +1511,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1594
1511
  isOptimizable,
1595
1512
  baseStyle,
1596
1513
  dynamicVars,
1514
+ propVarSpreads,
1597
1515
  };
1598
1516
  }
1599
1517
  const stateSources = baseChunks.map(({ order, style }) => ({ order, style: style }));
@@ -1849,7 +1767,13 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1849
1767
  classParts.push(`(${JSON.stringify(results)}[${masterKeyExpr || '""'}] || ${baseConflictClass ? JSON.stringify(baseConflictClass) : '""'})`);
1850
1768
  }
1851
1769
  classParts.push(...dynamicClassParts);
1852
- return { classParts, isOptimizable, baseStyle, dynamicVars };
1770
+ return {
1771
+ classParts,
1772
+ isOptimizable,
1773
+ baseStyle,
1774
+ dynamicVars,
1775
+ propVarSpreads,
1776
+ };
1853
1777
  };
1854
1778
  (0, utils_1.traverse)(ast, {
1855
1779
  JSXOpeningElement({ node }) {
@@ -2050,14 +1974,40 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2050
1974
  node.value &&
2051
1975
  node.value.type === 'JSXExpressionContainer') {
2052
1976
  const expr = node.value.expression;
1977
+ const carrierFor = (subNode) => {
1978
+ const vars = [];
1979
+ let unresolved = false;
1980
+ (0, utils_1.traverse)(subNode, {
1981
+ CallExpression({ node: call }) {
1982
+ if (!isStyleFunctionCall(call))
1983
+ return;
1984
+ const resolved = resolveDynamicCall(call, true);
1985
+ if (!resolved) {
1986
+ unresolved = true;
1987
+ return;
1988
+ }
1989
+ vars.push(...resolved.vars);
1990
+ },
1991
+ });
1992
+ if (unresolved)
1993
+ return null;
1994
+ return `{ ${foldDynamicVars(vars).join(', ')} }`;
1995
+ };
2053
1996
  const replaceWithKey = (subNode) => {
2054
1997
  const entry = list.find((x) => x.spanStart === subNode.span.start &&
2055
1998
  x.filePath === resourcePath);
2056
1999
  if (entry) {
2000
+ let content = JSON.stringify(entry.key);
2001
+ if (entry.hasVars) {
2002
+ const carrier = carrierFor(subNode);
2003
+ if (!carrier)
2004
+ return false;
2005
+ content = `{ key: ${content}, vars: ${carrier} }`;
2006
+ }
2057
2007
  replacements.push({
2058
2008
  start: subNode.span.start - baseByteOffset,
2059
2009
  end: subNode.span.end - baseByteOffset,
2060
- content: JSON.stringify(entry.key),
2010
+ content,
2061
2011
  });
2062
2012
  processStyleRecords(entry.styleObj);
2063
2013
  return true;
@@ -2075,6 +2025,11 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2075
2025
  excludeSubtreeSpans(subNode);
2076
2026
  }
2077
2027
  },
2028
+ CallExpression({ node: subNode }) {
2029
+ if (replaceWithKey(subNode)) {
2030
+ excludeSubtreeSpans(subNode);
2031
+ }
2032
+ },
2078
2033
  });
2079
2034
  }
2080
2035
  }
@@ -2150,9 +2105,12 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2150
2105
  }
2151
2106
  }
2152
2107
  }
2153
- const { classParts, isOptimizable, baseStyle, dynamicVars } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
2154
- const dynamicStyleParts = foldDynamicVars(dynamicVars);
2155
- const styleParts = [...existingStyleParts, ...dynamicStyleParts];
2108
+ const { classParts, isOptimizable, baseStyle, dynamicVars, propVarSpreads, } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
2109
+ const styleParts = [
2110
+ ...existingStyleParts,
2111
+ ...foldDynamicVars(dynamicVars),
2112
+ ...propVarSpreads,
2113
+ ];
2156
2114
  const styleAttr = styleParts.length > 0 || existingStyleExpr
2157
2115
  ? ` style={{ ${[existingStyleExpr, ...styleParts].filter(Boolean).join(', ')} }}`
2158
2116
  : '';
@@ -2168,10 +2126,13 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2168
2126
  });
2169
2127
  }
2170
2128
  else {
2129
+ const keptClass = existingClassExpr
2130
+ ? `className={${existingClassExpr}}`
2131
+ : '';
2171
2132
  replacements.push({
2172
2133
  start: node.span.start - baseByteOffset,
2173
2134
  end: node.span.end - baseByteOffset,
2174
- content: styleAttr || '',
2135
+ content: `${keptClass}${styleAttr}`,
2175
2136
  });
2176
2137
  }
2177
2138
  },
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) => {
@@ -29,87 +29,37 @@ const componentFunctionOf = (node) => {
29
29
  }
30
30
  return undefined;
31
31
  };
32
+ const unwrapPatternDefault = (pattern) => pattern?.type === 'AssignmentPattern' ? pattern.left : pattern;
33
+ const destructuredPropAliases = (fn) => {
34
+ const aliases = new Map();
35
+ const first = fn.params[0];
36
+ const pattern = unwrapPatternDefault(first?.pat ?? first);
37
+ if (pattern?.type !== 'ObjectPattern')
38
+ return aliases;
39
+ for (const item of pattern.properties ?? []) {
40
+ if (item.type !== 'KeyValuePatternProperty')
41
+ continue;
42
+ const local = unwrapPatternDefault(item.value);
43
+ const key = item.key;
44
+ if (!t.isIdentifier(local))
45
+ continue;
46
+ if (!t.isIdentifier(key) && !t.isStringLiteral(key))
47
+ continue;
48
+ const name = String(key.value);
49
+ if (name !== local.value)
50
+ aliases.set(local.value, name);
51
+ }
52
+ return aliases;
53
+ };
32
54
  const isStaticArgValue = (node) => node.type === 'StringLiteral' ||
33
55
  node.type === 'NumericLiteral' ||
34
56
  node.type === 'BooleanLiteral' ||
35
57
  (node.type === 'TemplateLiteral' && node.expressions.length === 0) ||
36
58
  t.isIdentifier(node) ||
37
59
  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
- };
60
+ const isNoOpStyle = (node) => t.isNullLiteral(node) ||
61
+ (t.isBooleanLiteral(node) && node.value === false) ||
62
+ (t.isIdentifier(node) && node.value === 'undefined');
113
63
  const foldDynamicVars = (vars) => {
114
64
  const grouped = new Map();
115
65
  [...vars]
@@ -131,59 +81,6 @@ const foldDynamicVars = (vars) => {
131
81
  return `"${cssVar}": ${value}`;
132
82
  });
133
83
  };
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
84
  export const unpluginFactory = (options = {}, unpluginMeta) => {
188
85
  const filter = createFilter(options.include, options.exclude);
189
86
  const propertyPolicy = resolvePropertyPolicy(options);
@@ -339,18 +236,22 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
339
236
  }
340
237
  }
341
238
  const components = [];
239
+ const propAliases = new Map();
240
+ const addComponent = (name, fn) => {
241
+ components.push({ name, node: fn });
242
+ const aliases = destructuredPropAliases(fn);
243
+ if (aliases.size > 0)
244
+ propAliases.set(name, aliases);
245
+ };
342
246
  for (const node of ast.body) {
343
247
  const statement = unwrapExport(node);
344
248
  if (isFunctionNode(statement)) {
345
- components.push({
346
- name: statement.identifier?.value ?? 'default',
347
- node: statement,
348
- });
249
+ addComponent(statement.identifier?.value ?? 'default', statement);
349
250
  }
350
251
  else if (statement?.type === 'CallExpression') {
351
252
  const fn = componentFunctionOf(statement);
352
253
  if (fn)
353
- components.push({ name: 'default', node: fn });
254
+ addComponent('default', fn);
354
255
  }
355
256
  else if (t.isVariableDeclaration(statement)) {
356
257
  for (const decl of statement.declarations) {
@@ -358,7 +259,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
358
259
  continue;
359
260
  const fn = componentFunctionOf(decl.init);
360
261
  if (fn)
361
- components.push({ name: decl.id.value, node: fn });
262
+ addComponent(decl.id.value, fn);
362
263
  }
363
264
  }
364
265
  }
@@ -558,6 +459,16 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
558
459
  for (const key of Object.keys(createStaticImportMap)) {
559
460
  mergedCreateStaticHashTable[key] = createStaticImportMap[key];
560
461
  }
462
+ const dynamicStyleTables = {
463
+ keyframesHashTable: mergedKeyframesTable,
464
+ viewTransitionHashTable: mergedViewTransitionTable,
465
+ createThemeHashTable: mergedCreateThemeHashTable,
466
+ createThemeObjectTable: scannedTables.createThemeObjectTable,
467
+ createHashTable: mergedCreateTable,
468
+ createStaticHashTable: mergedCreateStaticHashTable,
469
+ createStaticObjectTable: scannedTables.createStaticObjectTable,
470
+ variantsHashTable: mergedVariantsTable,
471
+ };
561
472
  const localCreateStyles = {};
562
473
  const localStyleAliases = {};
563
474
  const checkStyleAliasAssignment = (decl) => {
@@ -959,7 +870,16 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
959
870
  }
960
871
  return null;
961
872
  };
962
- const resolveDynamicCall = (expr) => {
873
+ const isStyleFunctionCall = (expr) => {
874
+ if (!t.isCallExpression(expr) || !t.isMemberExpression(expr.callee))
875
+ return false;
876
+ const callee = expr.callee;
877
+ if (!t.isIdentifier(callee.object) || !t.isIdentifier(callee.property))
878
+ return false;
879
+ return Boolean(localCreateStyles[callee.object.value]?.functions?.[callee.property.value] ??
880
+ createFunctionImportMap[callee.object.value]?.[callee.property.value]);
881
+ };
882
+ const resolveDynamicCall = (expr, forceRuntime = false) => {
963
883
  if (!t.isCallExpression(expr) || !t.isMemberExpression(expr.callee))
964
884
  return null;
965
885
  const callee = expr.callee;
@@ -1003,7 +923,9 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1003
923
  throwCompilationError(`Plumeria: ${getSource(expr)} leaves "${key}" unset, and a dynamic style function has no value to fall back on.`, expr);
1004
924
  return;
1005
925
  }
1006
- if (isStaticArgValue(source) && argObj[key] !== undefined)
926
+ if (!forceRuntime &&
927
+ isStaticArgValue(source) &&
928
+ argObj[key] !== undefined)
1007
929
  tempStaticTable[local] = argObj[key];
1008
930
  else
1009
931
  runtime.push({ param: local, source });
@@ -1011,6 +933,8 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1011
933
  }
1012
934
  else if (callArgs.length === 1 &&
1013
935
  callArgs[0].expression.type === 'ObjectExpression') {
936
+ if (forceRuntime)
937
+ return null;
1014
938
  const argObj = resolveObjectArg(callArgs[0].expression) ?? {};
1015
939
  func.params.forEach((p) => {
1016
940
  if (argObj[p] !== undefined)
@@ -1025,39 +949,10 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1025
949
  runtime.push({ param: p, source: callArg.expression });
1026
950
  });
1027
951
  }
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)
952
+ const resolved = resolveDynamicStyle(func, runtime.map(({ param }) => param), tempStaticTable, dynamicStyleTables);
953
+ if (!resolved)
1046
954
  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
- });
955
+ const { style, varGroups } = resolved;
1061
956
  const vars = [];
1062
957
  runtime.forEach(({ param, source }) => {
1063
958
  const groups = varGroups.get(param);
@@ -1099,6 +994,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1099
994
  });
1100
995
  const conditionals = [];
1101
996
  const dynamicVars = [];
997
+ const propVarSpreads = [];
1102
998
  let groupIdCounter = 0;
1103
999
  let sourceOrder = 0;
1104
1000
  const baseChunks = [];
@@ -1218,6 +1114,8 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1218
1114
  return buildDecision(node, prefix, off, { n: 0 }, scoped);
1219
1115
  };
1220
1116
  const collectConditions = (node, currentTestStrings = [], argOrder) => {
1117
+ if (isNoOpStyle(node))
1118
+ return true;
1221
1119
  let branchStyle = resolveStyleObject(node);
1222
1120
  if (!branchStyle) {
1223
1121
  const dynamic = resolveDynamicCall(node);
@@ -1298,17 +1196,20 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1298
1196
  ? expr.value
1299
1197
  : expr.property.value;
1300
1198
  const source = t.isIdentifier(expr) ? varName : getSource(expr);
1301
- markStylePropApplied(varName, expr);
1302
1199
  const owner = ownerComponentOf(expr);
1200
+ const propName = (t.isIdentifier(expr) && owner
1201
+ ? propAliases.get(owner)?.get(varName)
1202
+ : undefined) ?? varName;
1203
+ markStylePropApplied(propName, expr);
1303
1204
  let possibilities = owner
1304
- ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[varName]
1205
+ ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[propName]
1305
1206
  : undefined;
1306
1207
  if (!possibilities) {
1307
1208
  const candidates = [];
1308
1209
  for (const key of Object.keys(scannedTables.componentPropsTable || {})) {
1309
1210
  if (!key.startsWith(`${resourcePath}-`))
1310
1211
  continue;
1311
- const entries = scannedTables.componentPropsTable?.[key]?.[varName];
1212
+ const entries = scannedTables.componentPropsTable?.[key]?.[propName];
1312
1213
  if (entries)
1313
1214
  candidates.push(...entries);
1314
1215
  }
@@ -1317,9 +1218,23 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1317
1218
  }
1318
1219
  if (!possibilities || possibilities.length === 0)
1319
1220
  return false;
1320
- const testLHS = gates.length
1321
- ? `((${gates.join(' && ')}) ? ${source} : "")`
1221
+ const carriesVars = possibilities.some((entry) => entry.hasVars);
1222
+ if (carriesVars && !isStyleProp) {
1223
+ throwCompilationError(`Plumeria: "${varName}" carries a dynamic function key, and css.use() returns only a class name. ` +
1224
+ `Apply it to ${styleProp} on the element instead.`, expr);
1225
+ }
1226
+ const gate = gates.length ? `(${gates.join(' && ')}) && ` : '';
1227
+ if (carriesVars) {
1228
+ const spread = `...(${gate}${source} && ${source}.vars)`;
1229
+ if (!propVarSpreads.includes(spread))
1230
+ propVarSpreads.push(spread);
1231
+ }
1232
+ const token = carriesVars
1233
+ ? `((${source} && ${source}.key) || ${source})`
1322
1234
  : source;
1235
+ const testLHS = gates.length
1236
+ ? `((${gates.join(' && ')}) ? ${token} : "")`
1237
+ : token;
1323
1238
  const currentGroupId = ++groupIdCounter;
1324
1239
  const currentOrder = sourceOrder++;
1325
1240
  const seen = new Set();
@@ -1349,6 +1264,8 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1349
1264
  };
1350
1265
  for (const arg of args) {
1351
1266
  const expr = arg.expression;
1267
+ if (isNoOpStyle(expr))
1268
+ continue;
1352
1269
  if (pushPropPossibilities(expr, []))
1353
1270
  continue;
1354
1271
  if (t.isCallExpression(expr) && t.isIdentifier(expr.callee)) {
@@ -1558,6 +1475,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1558
1475
  isOptimizable,
1559
1476
  baseStyle,
1560
1477
  dynamicVars,
1478
+ propVarSpreads,
1561
1479
  };
1562
1480
  }
1563
1481
  const stateSources = baseChunks.map(({ order, style }) => ({ order, style: style }));
@@ -1813,7 +1731,13 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1813
1731
  classParts.push(`(${JSON.stringify(results)}[${masterKeyExpr || '""'}] || ${baseConflictClass ? JSON.stringify(baseConflictClass) : '""'})`);
1814
1732
  }
1815
1733
  classParts.push(...dynamicClassParts);
1816
- return { classParts, isOptimizable, baseStyle, dynamicVars };
1734
+ return {
1735
+ classParts,
1736
+ isOptimizable,
1737
+ baseStyle,
1738
+ dynamicVars,
1739
+ propVarSpreads,
1740
+ };
1817
1741
  };
1818
1742
  traverse(ast, {
1819
1743
  JSXOpeningElement({ node }) {
@@ -2014,14 +1938,40 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2014
1938
  node.value &&
2015
1939
  node.value.type === 'JSXExpressionContainer') {
2016
1940
  const expr = node.value.expression;
1941
+ const carrierFor = (subNode) => {
1942
+ const vars = [];
1943
+ let unresolved = false;
1944
+ 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
+ };
2017
1960
  const replaceWithKey = (subNode) => {
2018
1961
  const entry = list.find((x) => x.spanStart === subNode.span.start &&
2019
1962
  x.filePath === resourcePath);
2020
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
+ }
2021
1971
  replacements.push({
2022
1972
  start: subNode.span.start - baseByteOffset,
2023
1973
  end: subNode.span.end - baseByteOffset,
2024
- content: JSON.stringify(entry.key),
1974
+ content,
2025
1975
  });
2026
1976
  processStyleRecords(entry.styleObj);
2027
1977
  return true;
@@ -2039,6 +1989,11 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2039
1989
  excludeSubtreeSpans(subNode);
2040
1990
  }
2041
1991
  },
1992
+ CallExpression({ node: subNode }) {
1993
+ if (replaceWithKey(subNode)) {
1994
+ excludeSubtreeSpans(subNode);
1995
+ }
1996
+ },
2042
1997
  });
2043
1998
  }
2044
1999
  }
@@ -2114,9 +2069,12 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2114
2069
  }
2115
2070
  }
2116
2071
  }
2117
- const { classParts, isOptimizable, baseStyle, dynamicVars } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
2118
- const dynamicStyleParts = foldDynamicVars(dynamicVars);
2119
- 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
+ ];
2120
2078
  const styleAttr = styleParts.length > 0 || existingStyleExpr
2121
2079
  ? ` style={{ ${[existingStyleExpr, ...styleParts].filter(Boolean).join(', ')} }}`
2122
2080
  : '';
@@ -2132,10 +2090,13 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2132
2090
  });
2133
2091
  }
2134
2092
  else {
2093
+ const keptClass = existingClassExpr
2094
+ ? `className={${existingClassExpr}}`
2095
+ : '';
2135
2096
  replacements.push({
2136
2097
  start: node.span.start - baseByteOffset,
2137
2098
  end: node.span.end - baseByteOffset,
2138
- content: styleAttr || '',
2099
+ content: `${keptClass}${styleAttr}`,
2139
2100
  });
2140
2101
  }
2141
2102
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/unplugin",
3
- "version": "18.4.0",
3
+ "version": "18.4.2",
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.2"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@swc/core": "1.15.47",