@plumeria/unplugin 18.4.1 → 18.5.0

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 +144 -38
  2. package/dist/core.mjs +145 -39
  3. package/package.json +2 -2
package/dist/core.js CHANGED
@@ -65,12 +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);
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');
74
99
  const foldDynamicVars = (vars) => {
75
100
  const grouped = new Map();
76
101
  [...vars]
@@ -247,18 +272,22 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
247
272
  }
248
273
  }
249
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
+ };
250
282
  for (const node of ast.body) {
251
283
  const statement = unwrapExport(node);
252
284
  if (isFunctionNode(statement)) {
253
- components.push({
254
- name: statement.identifier?.value ?? 'default',
255
- node: statement,
256
- });
285
+ addComponent(statement.identifier?.value ?? 'default', statement);
257
286
  }
258
287
  else if (statement?.type === 'CallExpression') {
259
288
  const fn = componentFunctionOf(statement);
260
289
  if (fn)
261
- components.push({ name: 'default', node: fn });
290
+ addComponent('default', fn);
262
291
  }
263
292
  else if (utils_1.t.isVariableDeclaration(statement)) {
264
293
  for (const decl of statement.declarations) {
@@ -266,7 +295,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
266
295
  continue;
267
296
  const fn = componentFunctionOf(decl.init);
268
297
  if (fn)
269
- components.push({ name: decl.id.value, node: fn });
298
+ addComponent(decl.id.value, fn);
270
299
  }
271
300
  }
272
301
  }
@@ -496,9 +525,43 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
496
525
  const idSpans = new Set();
497
526
  const excludedSpans = new Set();
498
527
  const referenceIdents = (0, utils_1.collectReferenceIdentifiers)(ast);
528
+ const topLevelDeclarators = new Set();
529
+ for (const statement of ast.body) {
530
+ const declaration = unwrapExport(statement);
531
+ if (utils_1.t.isVariableDeclaration(declaration)) {
532
+ declaration.declarations.forEach((decl) => topLevelDeclarators.add(decl));
533
+ }
534
+ }
535
+ const unwrapStyleExpression = (node) => {
536
+ while (node &&
537
+ [
538
+ 'ParenthesisExpression',
539
+ 'TsAsExpression',
540
+ 'TsSatisfiesExpression',
541
+ 'TsNonNullExpression',
542
+ 'TsConstAssertion',
543
+ 'TsTypeAssertion',
544
+ ].includes(node.type))
545
+ node = node.expression;
546
+ return node;
547
+ };
548
+ const literalObjectArgument = (node, seen = new Set()) => {
549
+ node = unwrapStyleExpression(node);
550
+ if (utils_1.t.isObjectExpression(node))
551
+ return node;
552
+ if (!utils_1.t.isIdentifier(node) || seen.has(node.value))
553
+ return null;
554
+ seen.add(node.value);
555
+ const declaration = [...topLevelDeclarators].find((decl) => utils_1.t.isIdentifier(decl.id) &&
556
+ decl.id.value === node.value);
557
+ return declaration?.init
558
+ ? literalObjectArgument(declaration.init, seen)
559
+ : null;
560
+ };
561
+ const registeredStyleCalls = new Set();
499
562
  const registerStyle = (node, declSpan, isExported) => {
500
563
  let propName;
501
- const init = node.init;
564
+ const init = unwrapStyleExpression(node.init);
502
565
  if (utils_1.t.isIdentifier(node.id) &&
503
566
  init &&
504
567
  utils_1.t.isCallExpression(init) &&
@@ -523,6 +586,19 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
523
586
  }
524
587
  }
525
588
  if (propName && init && utils_1.t.isCallExpression(init)) {
589
+ if (['create', 'createTheme', 'createStatic'].includes(propName) &&
590
+ !topLevelDeclarators.has(node)) {
591
+ throwCompilationError(`Plumeria: css.${propName} must be assigned to a top-level variable so its styles can be resolved across files. Move this declaration outside the function or block.`, node);
592
+ }
593
+ if (['create', 'createTheme', 'createStatic'].includes(propName)) {
594
+ const argumentIndex = propName === 'createTheme' ? 1 : 0;
595
+ const argument = init.arguments[argumentIndex];
596
+ const object = argument && literalObjectArgument(argument.expression);
597
+ if (!object)
598
+ throwCompilationError(`Plumeria: css.${propName} needs a style object it can read at build time. Pass an object literal or a top-level constant containing one.`, init);
599
+ argument.expression = object;
600
+ registeredStyleCalls.add(init.span.start);
601
+ }
526
602
  if (propName === 'create' &&
527
603
  utils_1.t.isObjectExpression(init.arguments[0].expression)) {
528
604
  const obj = (0, utils_1.objectExpressionToObject)(init.arguments[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
@@ -720,6 +796,10 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
720
796
  }
721
797
  }
722
798
  if (propName) {
799
+ if (['create', 'createTheme', 'createStatic'].includes(propName) &&
800
+ !registeredStyleCalls.has(node.span.start)) {
801
+ throwCompilationError(`Plumeria: css.${propName} must be assigned to a named top-level variable. Destructuring, assignment statements, and default-exported calls cannot be compiled.`, node);
802
+ }
723
803
  const args = node.arguments;
724
804
  if (propName === 'keyframes' && args.length > 0) {
725
805
  const expr = args[0].expression;
@@ -827,6 +907,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
827
907
  return sourceBuffer.subarray(start, end).toString('utf-8');
828
908
  };
829
909
  const resolveStyleObject = (expr) => {
910
+ expr = (0, utils_1.unwrapExpression)(expr);
830
911
  if (utils_1.t.isObjectExpression(expr)) {
831
912
  return (0, utils_1.objectExpressionToObject)(expr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
832
913
  }
@@ -902,7 +983,12 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
902
983
  return null;
903
984
  const tempStaticTable = { ...mergedStaticTable };
904
985
  const runtime = [];
905
- const resolveObjectArg = (argExpr) => (0, utils_1.objectExpressionToObject)(argExpr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
986
+ const resolveObjectArg = (argExpr) => (0, utils_1.objectExpressionToObject)({
987
+ ...argExpr,
988
+ properties: argExpr.properties.filter((prop) => prop.type === 'Identifier' ||
989
+ (prop.type === 'KeyValueProperty' &&
990
+ isStaticArgValue(prop.value))),
991
+ }, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
906
992
  if (func.named) {
907
993
  const argExpr = callArgs[0]?.expression;
908
994
  if (callArgs.length > 1 ||
@@ -1121,6 +1207,9 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1121
1207
  return buildDecision(node, prefix, off, { n: 0 }, scoped);
1122
1208
  };
1123
1209
  const collectConditions = (node, currentTestStrings = [], argOrder) => {
1210
+ node = (0, utils_1.unwrapExpression)(node);
1211
+ if (isNoOpStyle(node))
1212
+ return true;
1124
1213
  let branchStyle = resolveStyleObject(node);
1125
1214
  if (!branchStyle) {
1126
1215
  const dynamic = resolveDynamicCall(node);
@@ -1201,17 +1290,20 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1201
1290
  ? expr.value
1202
1291
  : expr.property.value;
1203
1292
  const source = utils_1.t.isIdentifier(expr) ? varName : getSource(expr);
1204
- markStylePropApplied(varName, expr);
1205
1293
  const owner = ownerComponentOf(expr);
1294
+ const propName = (utils_1.t.isIdentifier(expr) && owner
1295
+ ? propAliases.get(owner)?.get(varName)
1296
+ : undefined) ?? varName;
1297
+ markStylePropApplied(propName, expr);
1206
1298
  let possibilities = owner
1207
- ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[varName]
1299
+ ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[propName]
1208
1300
  : undefined;
1209
1301
  if (!possibilities) {
1210
1302
  const candidates = [];
1211
1303
  for (const key of Object.keys(scannedTables.componentPropsTable || {})) {
1212
1304
  if (!key.startsWith(`${resourcePath}-`))
1213
1305
  continue;
1214
- const entries = scannedTables.componentPropsTable?.[key]?.[varName];
1306
+ const entries = scannedTables.componentPropsTable?.[key]?.[propName];
1215
1307
  if (entries)
1216
1308
  candidates.push(...entries);
1217
1309
  }
@@ -1266,6 +1358,8 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1266
1358
  };
1267
1359
  for (const arg of args) {
1268
1360
  const expr = arg.expression;
1361
+ if (isNoOpStyle(expr))
1362
+ continue;
1269
1363
  if (pushPropPossibilities(expr, []))
1270
1364
  continue;
1271
1365
  if (utils_1.t.isCallExpression(expr) && utils_1.t.isIdentifier(expr.callee)) {
@@ -1938,45 +2032,54 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1938
2032
  node.value &&
1939
2033
  node.value.type === 'JSXExpressionContainer') {
1940
2034
  const expr = node.value.expression;
1941
- const carrierFor = (subNode) => {
2035
+ const carrierFor = (subNode, calls) => {
1942
2036
  const vars = [];
1943
2037
  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
- });
2038
+ for (const expression of calls ?? [subNode])
2039
+ (0, utils_1.traverse)(expression, {
2040
+ CallExpression({ node: call }) {
2041
+ if (!isStyleFunctionCall(call))
2042
+ return;
2043
+ const resolved = resolveDynamicCall(call, true);
2044
+ if (!resolved) {
2045
+ unresolved = true;
2046
+ return;
2047
+ }
2048
+ vars.push(...resolved.vars);
2049
+ },
2050
+ });
1956
2051
  if (unresolved)
1957
2052
  return null;
1958
2053
  return `{ ${foldDynamicVars(vars).join(', ')} }`;
1959
2054
  };
1960
2055
  const replaceWithKey = (subNode) => {
1961
- const entry = list.find((x) => x.spanStart === subNode.span.start &&
1962
- x.filePath === resourcePath);
1963
- if (entry) {
1964
- let content = JSON.stringify(entry.key);
2056
+ const entries = list.filter((entry) => entry.spanStart === subNode.span.start &&
2057
+ entry.filePath === resourcePath);
2058
+ if (!entries.length)
2059
+ return false;
2060
+ let content = '""';
2061
+ for (const entry of [...entries].reverse()) {
2062
+ let value = JSON.stringify(entry.key);
1965
2063
  if (entry.hasVars) {
1966
- const carrier = carrierFor(subNode);
2064
+ const carrier = carrierFor(subNode, entry.dynamicCalls);
1967
2065
  if (!carrier)
1968
2066
  return false;
1969
- content = `{ key: ${content}, vars: ${carrier} }`;
2067
+ value = `{ key: ${value}, vars: ${carrier} }`;
1970
2068
  }
1971
- replacements.push({
1972
- start: subNode.span.start - baseByteOffset,
1973
- end: subNode.span.end - baseByteOffset,
1974
- content,
1975
- });
2069
+ const condition = entry.conditions
2070
+ ?.map(({ test, truthy }) => `${truthy ? '' : '!'}(${getSource(test)})`)
2071
+ .join(' && ');
2072
+ content = condition
2073
+ ? `((${condition}) ? ${value} : ${content})`
2074
+ : value;
1976
2075
  processStyleRecords(entry.styleObj);
1977
- return true;
1978
2076
  }
1979
- return false;
2077
+ replacements.push({
2078
+ start: subNode.span.start - baseByteOffset,
2079
+ end: subNode.span.end - baseByteOffset,
2080
+ content,
2081
+ });
2082
+ return true;
1980
2083
  };
1981
2084
  (0, utils_1.traverse)(expr, {
1982
2085
  MemberExpression({ node: subNode }) {
@@ -2090,10 +2193,13 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
2090
2193
  });
2091
2194
  }
2092
2195
  else {
2196
+ const keptClass = existingClassExpr
2197
+ ? `className={${existingClassExpr}}`
2198
+ : '';
2093
2199
  replacements.push({
2094
2200
  start: node.span.start - baseByteOffset,
2095
2201
  end: node.span.end - baseByteOffset,
2096
- content: styleAttr || '',
2202
+ content: `${keptClass}${styleAttr}`,
2097
2203
  });
2098
2204
  }
2099
2205
  },
package/dist/core.mjs CHANGED
@@ -2,7 +2,7 @@ import { createFilter } from '@rollup/pluginutils';
2
2
  import { parseSync } from '@swc/core';
3
3
  import * as path from 'path';
4
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';
5
+ import { traverse, resolvePropertyPolicy, assertPropertyPolicy, collectReferenceIdentifiers, getStyleRecords, getStateWeights, collectLocalConsts, objectExpressionToObject, t, getRootIdentifier, unwrapExpression, 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,12 +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);
60
+ const isNoOpStyle = (node) => t.isNullLiteral(node) ||
61
+ (t.isBooleanLiteral(node) && node.value === false) ||
62
+ (t.isIdentifier(node) && node.value === 'undefined');
38
63
  const foldDynamicVars = (vars) => {
39
64
  const grouped = new Map();
40
65
  [...vars]
@@ -211,18 +236,22 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
211
236
  }
212
237
  }
213
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
+ };
214
246
  for (const node of ast.body) {
215
247
  const statement = unwrapExport(node);
216
248
  if (isFunctionNode(statement)) {
217
- components.push({
218
- name: statement.identifier?.value ?? 'default',
219
- node: statement,
220
- });
249
+ addComponent(statement.identifier?.value ?? 'default', statement);
221
250
  }
222
251
  else if (statement?.type === 'CallExpression') {
223
252
  const fn = componentFunctionOf(statement);
224
253
  if (fn)
225
- components.push({ name: 'default', node: fn });
254
+ addComponent('default', fn);
226
255
  }
227
256
  else if (t.isVariableDeclaration(statement)) {
228
257
  for (const decl of statement.declarations) {
@@ -230,7 +259,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
230
259
  continue;
231
260
  const fn = componentFunctionOf(decl.init);
232
261
  if (fn)
233
- components.push({ name: decl.id.value, node: fn });
262
+ addComponent(decl.id.value, fn);
234
263
  }
235
264
  }
236
265
  }
@@ -460,9 +489,43 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
460
489
  const idSpans = new Set();
461
490
  const excludedSpans = new Set();
462
491
  const referenceIdents = collectReferenceIdentifiers(ast);
492
+ const topLevelDeclarators = new Set();
493
+ for (const statement of ast.body) {
494
+ const declaration = unwrapExport(statement);
495
+ if (t.isVariableDeclaration(declaration)) {
496
+ declaration.declarations.forEach((decl) => topLevelDeclarators.add(decl));
497
+ }
498
+ }
499
+ const unwrapStyleExpression = (node) => {
500
+ while (node &&
501
+ [
502
+ 'ParenthesisExpression',
503
+ 'TsAsExpression',
504
+ 'TsSatisfiesExpression',
505
+ 'TsNonNullExpression',
506
+ 'TsConstAssertion',
507
+ 'TsTypeAssertion',
508
+ ].includes(node.type))
509
+ node = node.expression;
510
+ return node;
511
+ };
512
+ const literalObjectArgument = (node, seen = new Set()) => {
513
+ node = unwrapStyleExpression(node);
514
+ if (t.isObjectExpression(node))
515
+ return node;
516
+ if (!t.isIdentifier(node) || seen.has(node.value))
517
+ return null;
518
+ seen.add(node.value);
519
+ const declaration = [...topLevelDeclarators].find((decl) => t.isIdentifier(decl.id) &&
520
+ decl.id.value === node.value);
521
+ return declaration?.init
522
+ ? literalObjectArgument(declaration.init, seen)
523
+ : null;
524
+ };
525
+ const registeredStyleCalls = new Set();
463
526
  const registerStyle = (node, declSpan, isExported) => {
464
527
  let propName;
465
- const init = node.init;
528
+ const init = unwrapStyleExpression(node.init);
466
529
  if (t.isIdentifier(node.id) &&
467
530
  init &&
468
531
  t.isCallExpression(init) &&
@@ -487,6 +550,19 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
487
550
  }
488
551
  }
489
552
  if (propName && init && t.isCallExpression(init)) {
553
+ if (['create', 'createTheme', 'createStatic'].includes(propName) &&
554
+ !topLevelDeclarators.has(node)) {
555
+ throwCompilationError(`Plumeria: css.${propName} must be assigned to a top-level variable so its styles can be resolved across files. Move this declaration outside the function or block.`, node);
556
+ }
557
+ if (['create', 'createTheme', 'createStatic'].includes(propName)) {
558
+ const argumentIndex = propName === 'createTheme' ? 1 : 0;
559
+ const argument = init.arguments[argumentIndex];
560
+ const object = argument && literalObjectArgument(argument.expression);
561
+ if (!object)
562
+ throwCompilationError(`Plumeria: css.${propName} needs a style object it can read at build time. Pass an object literal or a top-level constant containing one.`, init);
563
+ argument.expression = object;
564
+ registeredStyleCalls.add(init.span.start);
565
+ }
490
566
  if (propName === 'create' &&
491
567
  t.isObjectExpression(init.arguments[0].expression)) {
492
568
  const obj = objectExpressionToObject(init.arguments[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
@@ -684,6 +760,10 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
684
760
  }
685
761
  }
686
762
  if (propName) {
763
+ if (['create', 'createTheme', 'createStatic'].includes(propName) &&
764
+ !registeredStyleCalls.has(node.span.start)) {
765
+ throwCompilationError(`Plumeria: css.${propName} must be assigned to a named top-level variable. Destructuring, assignment statements, and default-exported calls cannot be compiled.`, node);
766
+ }
687
767
  const args = node.arguments;
688
768
  if (propName === 'keyframes' && args.length > 0) {
689
769
  const expr = args[0].expression;
@@ -791,6 +871,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
791
871
  return sourceBuffer.subarray(start, end).toString('utf-8');
792
872
  };
793
873
  const resolveStyleObject = (expr) => {
874
+ expr = unwrapExpression(expr);
794
875
  if (t.isObjectExpression(expr)) {
795
876
  return objectExpressionToObject(expr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
796
877
  }
@@ -866,7 +947,12 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
866
947
  return null;
867
948
  const tempStaticTable = { ...mergedStaticTable };
868
949
  const runtime = [];
869
- const resolveObjectArg = (argExpr) => objectExpressionToObject(argExpr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
950
+ const resolveObjectArg = (argExpr) => objectExpressionToObject({
951
+ ...argExpr,
952
+ properties: argExpr.properties.filter((prop) => prop.type === 'Identifier' ||
953
+ (prop.type === 'KeyValueProperty' &&
954
+ isStaticArgValue(prop.value))),
955
+ }, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
870
956
  if (func.named) {
871
957
  const argExpr = callArgs[0]?.expression;
872
958
  if (callArgs.length > 1 ||
@@ -1085,6 +1171,9 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1085
1171
  return buildDecision(node, prefix, off, { n: 0 }, scoped);
1086
1172
  };
1087
1173
  const collectConditions = (node, currentTestStrings = [], argOrder) => {
1174
+ node = unwrapExpression(node);
1175
+ if (isNoOpStyle(node))
1176
+ return true;
1088
1177
  let branchStyle = resolveStyleObject(node);
1089
1178
  if (!branchStyle) {
1090
1179
  const dynamic = resolveDynamicCall(node);
@@ -1165,17 +1254,20 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1165
1254
  ? expr.value
1166
1255
  : expr.property.value;
1167
1256
  const source = t.isIdentifier(expr) ? varName : getSource(expr);
1168
- markStylePropApplied(varName, expr);
1169
1257
  const owner = ownerComponentOf(expr);
1258
+ const propName = (t.isIdentifier(expr) && owner
1259
+ ? propAliases.get(owner)?.get(varName)
1260
+ : undefined) ?? varName;
1261
+ markStylePropApplied(propName, expr);
1170
1262
  let possibilities = owner
1171
- ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[varName]
1263
+ ? scannedTables.componentPropsTable?.[`${resourcePath}-${owner}`]?.[propName]
1172
1264
  : undefined;
1173
1265
  if (!possibilities) {
1174
1266
  const candidates = [];
1175
1267
  for (const key of Object.keys(scannedTables.componentPropsTable || {})) {
1176
1268
  if (!key.startsWith(`${resourcePath}-`))
1177
1269
  continue;
1178
- const entries = scannedTables.componentPropsTable?.[key]?.[varName];
1270
+ const entries = scannedTables.componentPropsTable?.[key]?.[propName];
1179
1271
  if (entries)
1180
1272
  candidates.push(...entries);
1181
1273
  }
@@ -1230,6 +1322,8 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1230
1322
  };
1231
1323
  for (const arg of args) {
1232
1324
  const expr = arg.expression;
1325
+ if (isNoOpStyle(expr))
1326
+ continue;
1233
1327
  if (pushPropPossibilities(expr, []))
1234
1328
  continue;
1235
1329
  if (t.isCallExpression(expr) && t.isIdentifier(expr.callee)) {
@@ -1902,45 +1996,54 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1902
1996
  node.value &&
1903
1997
  node.value.type === 'JSXExpressionContainer') {
1904
1998
  const expr = node.value.expression;
1905
- const carrierFor = (subNode) => {
1999
+ const carrierFor = (subNode, calls) => {
1906
2000
  const vars = [];
1907
2001
  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
- });
2002
+ for (const expression of calls ?? [subNode])
2003
+ traverse(expression, {
2004
+ CallExpression({ node: call }) {
2005
+ if (!isStyleFunctionCall(call))
2006
+ return;
2007
+ const resolved = resolveDynamicCall(call, true);
2008
+ if (!resolved) {
2009
+ unresolved = true;
2010
+ return;
2011
+ }
2012
+ vars.push(...resolved.vars);
2013
+ },
2014
+ });
1920
2015
  if (unresolved)
1921
2016
  return null;
1922
2017
  return `{ ${foldDynamicVars(vars).join(', ')} }`;
1923
2018
  };
1924
2019
  const replaceWithKey = (subNode) => {
1925
- const entry = list.find((x) => x.spanStart === subNode.span.start &&
1926
- x.filePath === resourcePath);
1927
- if (entry) {
1928
- let content = JSON.stringify(entry.key);
2020
+ const entries = list.filter((entry) => entry.spanStart === subNode.span.start &&
2021
+ entry.filePath === resourcePath);
2022
+ if (!entries.length)
2023
+ return false;
2024
+ let content = '""';
2025
+ for (const entry of [...entries].reverse()) {
2026
+ let value = JSON.stringify(entry.key);
1929
2027
  if (entry.hasVars) {
1930
- const carrier = carrierFor(subNode);
2028
+ const carrier = carrierFor(subNode, entry.dynamicCalls);
1931
2029
  if (!carrier)
1932
2030
  return false;
1933
- content = `{ key: ${content}, vars: ${carrier} }`;
2031
+ value = `{ key: ${value}, vars: ${carrier} }`;
1934
2032
  }
1935
- replacements.push({
1936
- start: subNode.span.start - baseByteOffset,
1937
- end: subNode.span.end - baseByteOffset,
1938
- content,
1939
- });
2033
+ const condition = entry.conditions
2034
+ ?.map(({ test, truthy }) => `${truthy ? '' : '!'}(${getSource(test)})`)
2035
+ .join(' && ');
2036
+ content = condition
2037
+ ? `((${condition}) ? ${value} : ${content})`
2038
+ : value;
1940
2039
  processStyleRecords(entry.styleObj);
1941
- return true;
1942
2040
  }
1943
- return false;
2041
+ replacements.push({
2042
+ start: subNode.span.start - baseByteOffset,
2043
+ end: subNode.span.end - baseByteOffset,
2044
+ content,
2045
+ });
2046
+ return true;
1944
2047
  };
1945
2048
  traverse(expr, {
1946
2049
  MemberExpression({ node: subNode }) {
@@ -2054,10 +2157,13 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
2054
2157
  });
2055
2158
  }
2056
2159
  else {
2160
+ const keptClass = existingClassExpr
2161
+ ? `className={${existingClassExpr}}`
2162
+ : '';
2057
2163
  replacements.push({
2058
2164
  start: node.span.start - baseByteOffset,
2059
2165
  end: node.span.end - baseByteOffset,
2060
- content: styleAttr || '',
2166
+ content: `${keptClass}${styleAttr}`,
2061
2167
  });
2062
2168
  }
2063
2169
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/unplugin",
3
- "version": "18.4.1",
3
+ "version": "18.5.0",
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.1"
97
+ "@plumeria/utils": "^18.5.0"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@swc/core": "1.15.47",