@plumeria/unplugin 18.4.2 → 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 +95 -28
  2. package/dist/core.mjs +96 -29
  3. package/package.json +2 -2
package/dist/core.js CHANGED
@@ -525,9 +525,43 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
525
525
  const idSpans = new Set();
526
526
  const excludedSpans = new Set();
527
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();
528
562
  const registerStyle = (node, declSpan, isExported) => {
529
563
  let propName;
530
- const init = node.init;
564
+ const init = unwrapStyleExpression(node.init);
531
565
  if (utils_1.t.isIdentifier(node.id) &&
532
566
  init &&
533
567
  utils_1.t.isCallExpression(init) &&
@@ -552,6 +586,19 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
552
586
  }
553
587
  }
554
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
+ }
555
602
  if (propName === 'create' &&
556
603
  utils_1.t.isObjectExpression(init.arguments[0].expression)) {
557
604
  const obj = (0, utils_1.objectExpressionToObject)(init.arguments[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
@@ -749,6 +796,10 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
749
796
  }
750
797
  }
751
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
+ }
752
803
  const args = node.arguments;
753
804
  if (propName === 'keyframes' && args.length > 0) {
754
805
  const expr = args[0].expression;
@@ -856,6 +907,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
856
907
  return sourceBuffer.subarray(start, end).toString('utf-8');
857
908
  };
858
909
  const resolveStyleObject = (expr) => {
910
+ expr = (0, utils_1.unwrapExpression)(expr);
859
911
  if (utils_1.t.isObjectExpression(expr)) {
860
912
  return (0, utils_1.objectExpressionToObject)(expr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
861
913
  }
@@ -931,7 +983,12 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
931
983
  return null;
932
984
  const tempStaticTable = { ...mergedStaticTable };
933
985
  const runtime = [];
934
- 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);
935
992
  if (func.named) {
936
993
  const argExpr = callArgs[0]?.expression;
937
994
  if (callArgs.length > 1 ||
@@ -1150,6 +1207,7 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1150
1207
  return buildDecision(node, prefix, off, { n: 0 }, scoped);
1151
1208
  };
1152
1209
  const collectConditions = (node, currentTestStrings = [], argOrder) => {
1210
+ node = (0, utils_1.unwrapExpression)(node);
1153
1211
  if (isNoOpStyle(node))
1154
1212
  return true;
1155
1213
  let branchStyle = resolveStyleObject(node);
@@ -1974,45 +2032,54 @@ const unpluginFactory = (options = {}, unpluginMeta) => {
1974
2032
  node.value &&
1975
2033
  node.value.type === 'JSXExpressionContainer') {
1976
2034
  const expr = node.value.expression;
1977
- const carrierFor = (subNode) => {
2035
+ const carrierFor = (subNode, calls) => {
1978
2036
  const vars = [];
1979
2037
  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
- });
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
+ });
1992
2051
  if (unresolved)
1993
2052
  return null;
1994
2053
  return `{ ${foldDynamicVars(vars).join(', ')} }`;
1995
2054
  };
1996
2055
  const replaceWithKey = (subNode) => {
1997
- const entry = list.find((x) => x.spanStart === subNode.span.start &&
1998
- x.filePath === resourcePath);
1999
- if (entry) {
2000
- 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);
2001
2063
  if (entry.hasVars) {
2002
- const carrier = carrierFor(subNode);
2064
+ const carrier = carrierFor(subNode, entry.dynamicCalls);
2003
2065
  if (!carrier)
2004
2066
  return false;
2005
- content = `{ key: ${content}, vars: ${carrier} }`;
2067
+ value = `{ key: ${value}, vars: ${carrier} }`;
2006
2068
  }
2007
- replacements.push({
2008
- start: subNode.span.start - baseByteOffset,
2009
- end: subNode.span.end - baseByteOffset,
2010
- content,
2011
- });
2069
+ const condition = entry.conditions
2070
+ ?.map(({ test, truthy }) => `${truthy ? '' : '!'}(${getSource(test)})`)
2071
+ .join(' && ');
2072
+ content = condition
2073
+ ? `((${condition}) ? ${value} : ${content})`
2074
+ : value;
2012
2075
  processStyleRecords(entry.styleObj);
2013
- return true;
2014
2076
  }
2015
- return false;
2077
+ replacements.push({
2078
+ start: subNode.span.start - baseByteOffset,
2079
+ end: subNode.span.end - baseByteOffset,
2080
+ content,
2081
+ });
2082
+ return true;
2016
2083
  };
2017
2084
  (0, utils_1.traverse)(expr, {
2018
2085
  MemberExpression({ node: subNode }) {
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) => {
@@ -489,9 +489,43 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
489
489
  const idSpans = new Set();
490
490
  const excludedSpans = new Set();
491
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();
492
526
  const registerStyle = (node, declSpan, isExported) => {
493
527
  let propName;
494
- const init = node.init;
528
+ const init = unwrapStyleExpression(node.init);
495
529
  if (t.isIdentifier(node.id) &&
496
530
  init &&
497
531
  t.isCallExpression(init) &&
@@ -516,6 +550,19 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
516
550
  }
517
551
  }
518
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
+ }
519
566
  if (propName === 'create' &&
520
567
  t.isObjectExpression(init.arguments[0].expression)) {
521
568
  const obj = objectExpressionToObject(init.arguments[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
@@ -713,6 +760,10 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
713
760
  }
714
761
  }
715
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
+ }
716
767
  const args = node.arguments;
717
768
  if (propName === 'keyframes' && args.length > 0) {
718
769
  const expr = args[0].expression;
@@ -820,6 +871,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
820
871
  return sourceBuffer.subarray(start, end).toString('utf-8');
821
872
  };
822
873
  const resolveStyleObject = (expr) => {
874
+ expr = unwrapExpression(expr);
823
875
  if (t.isObjectExpression(expr)) {
824
876
  return objectExpressionToObject(expr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
825
877
  }
@@ -895,7 +947,12 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
895
947
  return null;
896
948
  const tempStaticTable = { ...mergedStaticTable };
897
949
  const runtime = [];
898
- 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);
899
956
  if (func.named) {
900
957
  const argExpr = callArgs[0]?.expression;
901
958
  if (callArgs.length > 1 ||
@@ -1114,6 +1171,7 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1114
1171
  return buildDecision(node, prefix, off, { n: 0 }, scoped);
1115
1172
  };
1116
1173
  const collectConditions = (node, currentTestStrings = [], argOrder) => {
1174
+ node = unwrapExpression(node);
1117
1175
  if (isNoOpStyle(node))
1118
1176
  return true;
1119
1177
  let branchStyle = resolveStyleObject(node);
@@ -1938,45 +1996,54 @@ export const unpluginFactory = (options = {}, unpluginMeta) => {
1938
1996
  node.value &&
1939
1997
  node.value.type === 'JSXExpressionContainer') {
1940
1998
  const expr = node.value.expression;
1941
- const carrierFor = (subNode) => {
1999
+ const carrierFor = (subNode, calls) => {
1942
2000
  const vars = [];
1943
2001
  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
- });
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
+ });
1956
2015
  if (unresolved)
1957
2016
  return null;
1958
2017
  return `{ ${foldDynamicVars(vars).join(', ')} }`;
1959
2018
  };
1960
2019
  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);
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);
1965
2027
  if (entry.hasVars) {
1966
- const carrier = carrierFor(subNode);
2028
+ const carrier = carrierFor(subNode, entry.dynamicCalls);
1967
2029
  if (!carrier)
1968
2030
  return false;
1969
- content = `{ key: ${content}, vars: ${carrier} }`;
2031
+ value = `{ key: ${value}, vars: ${carrier} }`;
1970
2032
  }
1971
- replacements.push({
1972
- start: subNode.span.start - baseByteOffset,
1973
- end: subNode.span.end - baseByteOffset,
1974
- content,
1975
- });
2033
+ const condition = entry.conditions
2034
+ ?.map(({ test, truthy }) => `${truthy ? '' : '!'}(${getSource(test)})`)
2035
+ .join(' && ');
2036
+ content = condition
2037
+ ? `((${condition}) ? ${value} : ${content})`
2038
+ : value;
1976
2039
  processStyleRecords(entry.styleObj);
1977
- return true;
1978
2040
  }
1979
- return false;
2041
+ replacements.push({
2042
+ start: subNode.span.start - baseByteOffset,
2043
+ end: subNode.span.end - baseByteOffset,
2044
+ content,
2045
+ });
2046
+ return true;
1980
2047
  };
1981
2048
  traverse(expr, {
1982
2049
  MemberExpression({ node: subNode }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/unplugin",
3
- "version": "18.4.2",
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.2"
97
+ "@plumeria/utils": "^18.5.0"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@swc/core": "1.15.47",