@plumeria/turbopack-loader 18.1.5 → 18.1.6

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 (2) hide show
  1. package/dist/index.js +226 -108
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -79,6 +79,70 @@ const ensureProductionCss = (virtualFilePath, options) => {
79
79
  }
80
80
  return productionCss;
81
81
  };
82
+ const isStaticArgValue = (node) => node.type === 'StringLiteral' ||
83
+ node.type === 'NumericLiteral' ||
84
+ node.type === 'BooleanLiteral' ||
85
+ (node.type === 'TemplateLiteral' && node.expressions.length === 0) ||
86
+ utils_1.t.isIdentifier(node) ||
87
+ utils_1.t.isMemberExpression(node);
88
+ const namedParamsOf = (params) => {
89
+ if (params.length !== 1)
90
+ return undefined;
91
+ const first = params[0];
92
+ const pattern = (first?.pat ?? first);
93
+ if (pattern?.type !== 'ObjectPattern')
94
+ return undefined;
95
+ const named = [];
96
+ for (const prop of pattern.properties ?? []) {
97
+ if (prop.type === 'AssignmentPatternProperty' &&
98
+ utils_1.t.isIdentifier(prop.key) &&
99
+ !prop.value) {
100
+ named.push({ key: prop.key.value, local: prop.key.value });
101
+ }
102
+ else if (prop.type === 'KeyValuePatternProperty' &&
103
+ utils_1.t.isIdentifier(prop.key) &&
104
+ utils_1.t.isIdentifier(prop.value)) {
105
+ named.push({ key: prop.key.value, local: prop.value.value });
106
+ }
107
+ else {
108
+ return undefined;
109
+ }
110
+ }
111
+ return named.length > 0 ? named : undefined;
112
+ };
113
+ const foldDynamicVars = (vars) => {
114
+ const grouped = new Map();
115
+ [...vars]
116
+ .sort((a, b) => (a.order ?? 0) - (b.order ?? 0))
117
+ .forEach((entry) => {
118
+ const list = grouped.get(entry.cssVar);
119
+ if (list)
120
+ list.push(entry);
121
+ else
122
+ grouped.set(entry.cssVar, [entry]);
123
+ });
124
+ return [...grouped].map(([cssVar, entries]) => {
125
+ let value = 'undefined';
126
+ entries.forEach((entry) => {
127
+ value = entry.test
128
+ ? `((${entry.test}) ? ${entry.valueExpr} : ${value})`
129
+ : entry.valueExpr;
130
+ });
131
+ return `"${cssVar}": ${value}`;
132
+ });
133
+ };
134
+ const findVarProp = (style, cssVar) => {
135
+ for (const [prop, value] of Object.entries(style)) {
136
+ if (typeof value === 'string' && value.includes(cssVar))
137
+ return prop;
138
+ if (value !== null && typeof value === 'object') {
139
+ const nested = findVarProp(value, cssVar);
140
+ if (nested)
141
+ return nested;
142
+ }
143
+ }
144
+ return undefined;
145
+ };
82
146
  async function loader(source) {
83
147
  const callback = this.async();
84
148
  const loaderOptions = this.getOptions?.() ??
@@ -372,6 +436,7 @@ async function loader(source) {
372
436
  }
373
437
  };
374
438
  const replacements = [];
439
+ const dynamicFnCalls = [];
375
440
  const processedDecls = new Set();
376
441
  const idSpans = new Set();
377
442
  const excludedSpans = new Set();
@@ -449,6 +514,7 @@ async function loader(source) {
449
514
  if (actualBody && actualBody.type === 'ObjectExpression') {
450
515
  styleFunctions[prop.key.value] = {
451
516
  params,
517
+ named: namedParamsOf(func.params),
452
518
  body: actualBody,
453
519
  };
454
520
  }
@@ -787,7 +853,110 @@ async function loader(source) {
787
853
  }
788
854
  return null;
789
855
  };
790
- const buildClassParts = (args, dynamicClassParts = [], existingClass = '') => {
856
+ const resolveDynamicCall = (expr) => {
857
+ if (!utils_1.t.isCallExpression(expr) || !utils_1.t.isMemberExpression(expr.callee))
858
+ return null;
859
+ const callee = expr.callee;
860
+ if (!utils_1.t.isIdentifier(callee.object) || !utils_1.t.isIdentifier(callee.property))
861
+ return null;
862
+ const styleInfo = localCreateStyles[callee.object.value];
863
+ const func = styleInfo?.functions?.[callee.property.value];
864
+ if (!func)
865
+ return null;
866
+ const callArgs = expr.arguments;
867
+ if (callArgs.some((a) => a.spread) || callArgs.length === 0)
868
+ return null;
869
+ const tempStaticTable = { ...mergedStaticTable };
870
+ const runtime = [];
871
+ const resolveObjectArg = (argExpr) => (0, utils_1.objectExpressionToObject)(argExpr, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
872
+ if (func.named) {
873
+ const argExpr = callArgs[0].expression;
874
+ if (callArgs.length !== 1 || argExpr.type !== 'ObjectExpression') {
875
+ throwCompilationError(`Plumeria: ${getSource(expr)} takes one object argument, because ${callee.property.value} destructures its parameter.`, expr);
876
+ }
877
+ const given = new Map();
878
+ argExpr.properties.forEach((prop) => {
879
+ if (prop.type === 'Identifier') {
880
+ given.set(prop.value, prop);
881
+ }
882
+ else if (prop.type === 'KeyValueProperty' &&
883
+ (utils_1.t.isIdentifier(prop.key) || utils_1.t.isStringLiteral(prop.key))) {
884
+ given.set(String(prop.key.value), prop.value);
885
+ }
886
+ });
887
+ const argObj = resolveObjectArg(argExpr) ?? {};
888
+ func.named.forEach(({ key, local }) => {
889
+ const source = given.get(key);
890
+ if (!source) {
891
+ throwCompilationError(`Plumeria: ${getSource(expr)} leaves "${key}" unset, and a dynamic style function has no value to fall back on.`, expr);
892
+ return;
893
+ }
894
+ if (isStaticArgValue(source) && argObj[key] !== undefined)
895
+ tempStaticTable[local] = argObj[key];
896
+ else
897
+ runtime.push({ param: local, source });
898
+ });
899
+ }
900
+ else if (callArgs.length === 1 &&
901
+ callArgs[0].expression.type === 'ObjectExpression') {
902
+ const argObj = resolveObjectArg(callArgs[0].expression) ?? {};
903
+ func.params.forEach((p) => {
904
+ if (argObj[p] !== undefined)
905
+ tempStaticTable[p] = argObj[p];
906
+ });
907
+ }
908
+ else {
909
+ callArgs.forEach((callArg, i) => {
910
+ const p = func.params[i];
911
+ if (!p)
912
+ return;
913
+ runtime.push({ param: p, source: callArg.expression });
914
+ });
915
+ }
916
+ const cssVars = {};
917
+ if (runtime.length > 0) {
918
+ runtime.forEach(({ param }) => (tempStaticTable[param] = param));
919
+ const probe = (0, utils_1.objectExpressionToObject)(func.body, tempStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
920
+ const hash = (0, zss_engine_1.genBase36Hash)(probe ?? {}, 1, 8);
921
+ runtime.forEach(({ param }) => {
922
+ const cssVar = `--${hash}-${param}`;
923
+ tempStaticTable[param] = `var(${cssVar})`;
924
+ cssVars[param] = cssVar;
925
+ });
926
+ }
927
+ const style = (0, utils_1.objectExpressionToObject)(func.body, tempStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
928
+ if (!style)
929
+ return null;
930
+ const vars = [];
931
+ runtime.forEach(({ param, source }) => {
932
+ const cssVar = cssVars[param];
933
+ const targetProp = cssVar ? findVarProp(style, cssVar) : undefined;
934
+ if (!targetProp)
935
+ return;
936
+ const argStart = source.span.start - baseByteOffset;
937
+ const argEnd = source.span.end - baseByteOffset;
938
+ const argSource = sourceBuffer
939
+ .subarray(argStart, argEnd)
940
+ .toString('utf-8');
941
+ let valueExpr;
942
+ const maybeNumber = Number(argSource);
943
+ if (!isNaN(maybeNumber) && argSource.trim() === String(maybeNumber)) {
944
+ valueExpr = JSON.stringify((0, zss_engine_1.applyCssValue)(maybeNumber, targetProp));
945
+ }
946
+ else if ((argSource.startsWith('"') && argSource.endsWith('"')) ||
947
+ (argSource.startsWith("'") && argSource.endsWith("'"))) {
948
+ valueExpr = JSON.stringify((0, zss_engine_1.applyCssValue)(argSource.slice(1, -1), targetProp));
949
+ }
950
+ else {
951
+ valueExpr = zss_engine_1.exceptionCamelCase.includes(targetProp)
952
+ ? argSource
953
+ : `(typeof (${argSource}) === 'number' ? (${argSource}) + 'px' : (${argSource}))`;
954
+ }
955
+ vars.push({ cssVar, valueExpr });
956
+ });
957
+ return { style, vars };
958
+ };
959
+ const buildClassParts = (args, dynamicClassParts = [], existingClass = '', isStyleProp = false) => {
791
960
  args.forEach((arg) => {
792
961
  const expr = arg.expression;
793
962
  if (utils_1.t.isIdentifier(expr) && localStyleAliases[expr.value]) {
@@ -795,6 +964,7 @@ async function loader(source) {
795
964
  }
796
965
  });
797
966
  const conditionals = [];
967
+ const dynamicVars = [];
798
968
  let groupIdCounter = 0;
799
969
  let sourceOrder = 0;
800
970
  const baseChunks = [];
@@ -910,9 +1080,26 @@ async function loader(source) {
910
1080
  const off = !scoped && groupKeys.has('') ? `${prefix}off` : '';
911
1081
  return buildDecision(node, prefix, off, { n: 0 }, scoped);
912
1082
  };
913
- const collectConditions = (node, currentTestStrings = []) => {
914
- const staticStyle = resolveStyleObject(node);
915
- if (staticStyle) {
1083
+ const collectConditions = (node, currentTestStrings = [], argOrder) => {
1084
+ let branchStyle = resolveStyleObject(node);
1085
+ if (!branchStyle) {
1086
+ const dynamic = resolveDynamicCall(node);
1087
+ if (dynamic) {
1088
+ if (!isStyleProp) {
1089
+ throwCompilationError(`Plumeria: css.use(${getSource(node)}) does not support dynamic function keys.`, node);
1090
+ }
1091
+ branchStyle = dynamic.style;
1092
+ dynamic.vars.forEach((v) => dynamicVars.push({
1093
+ ...v,
1094
+ test: currentTestStrings.length
1095
+ ? currentTestStrings.join(' && ')
1096
+ : undefined,
1097
+ order: argOrder,
1098
+ }));
1099
+ }
1100
+ }
1101
+ if (branchStyle) {
1102
+ const staticStyle = branchStyle;
916
1103
  if (currentTestStrings.length === 0) {
917
1104
  baseStyle = (0, utils_1.deepMerge)(baseStyle, staticStyle);
918
1105
  baseChunks.push({ order: sourceOrder++, style: staticStyle });
@@ -946,25 +1133,16 @@ async function loader(source) {
946
1133
  return true;
947
1134
  }
948
1135
  }
949
- collectConditions(node.consequent, [
950
- ...currentTestStrings,
951
- `(${testSource})`,
952
- ]);
953
- collectConditions(node.alternate, [
954
- ...currentTestStrings,
955
- `!(${testSource})`,
956
- ]);
1136
+ collectConditions(node.consequent, [...currentTestStrings, `(${testSource})`], argOrder);
1137
+ collectConditions(node.alternate, [...currentTestStrings, `!(${testSource})`], argOrder);
957
1138
  return true;
958
1139
  }
959
1140
  else if (node.type === 'BinaryExpression' && node.operator === '&&') {
960
- collectConditions(node.right, [
961
- ...currentTestStrings,
962
- `(${getSource(node.left)})`,
963
- ]);
1141
+ collectConditions(node.right, [...currentTestStrings, `(${getSource(node.left)})`], argOrder);
964
1142
  return true;
965
1143
  }
966
1144
  else if (node.type === 'ParenthesisExpression') {
967
- return collectConditions(node.expression, currentTestStrings);
1145
+ return collectConditions(node.expression, currentTestStrings, argOrder);
968
1146
  }
969
1147
  assertResolvable(node);
970
1148
  return false;
@@ -1209,7 +1387,7 @@ async function loader(source) {
1209
1387
  }));
1210
1388
  continue;
1211
1389
  }
1212
- const handled = collectConditions(expr);
1390
+ const handled = collectConditions(expr, [], arg.order);
1213
1391
  if (handled)
1214
1392
  continue;
1215
1393
  assertResolvable(expr);
@@ -1218,7 +1396,12 @@ async function loader(source) {
1218
1396
  }
1219
1397
  if (!isOptimizable ||
1220
1398
  (args.length === 0 && Object.keys(baseStyle).length === 0)) {
1221
- return { classParts: [...dynamicClassParts], isOptimizable, baseStyle };
1399
+ return {
1400
+ classParts: [...dynamicClassParts],
1401
+ isOptimizable,
1402
+ baseStyle,
1403
+ dynamicVars,
1404
+ };
1222
1405
  }
1223
1406
  const participation = {};
1224
1407
  const registerParticipation = (style, sourceId) => {
@@ -1466,7 +1649,7 @@ async function loader(source) {
1466
1649
  classParts.push(`(${JSON.stringify(results)}[${masterKeyExpr || '""'}] || ${baseConflictClass ? JSON.stringify(baseConflictClass) : '""'})`);
1467
1650
  }
1468
1651
  classParts.push(...dynamicClassParts);
1469
- return { classParts, isOptimizable, baseStyle };
1652
+ return { classParts, isOptimizable, baseStyle, dynamicVars };
1470
1653
  };
1471
1654
  (0, utils_1.traverse)(ast, {
1472
1655
  JSXOpeningElement({ node }) {
@@ -1700,13 +1883,13 @@ async function loader(source) {
1700
1883
  if (!node.value || node.value.type !== 'JSXExpressionContainer')
1701
1884
  return;
1702
1885
  const expr = node.value.expression;
1703
- let args = expr.type === 'ArrayExpression'
1886
+ const args = expr.type === 'ArrayExpression'
1704
1887
  ? expr.elements
1705
1888
  .filter((el) => el !== undefined)
1706
1889
  .map((el) => ({ expression: el.expression }))
1707
1890
  : [{ expression: expr }];
1708
1891
  const dynamicClassParts = [];
1709
- const dynamicStyleParts = [];
1892
+ const existingStyleParts = [];
1710
1893
  let attributes = [];
1711
1894
  for (const [, val] of jsxOpeningElementMap) {
1712
1895
  const found = val.attributes
@@ -1760,99 +1943,21 @@ async function loader(source) {
1760
1943
  if (innerExpr.type === 'ObjectExpression') {
1761
1944
  const stripped = innerSource.slice(1, -1).trim();
1762
1945
  if (stripped)
1763
- dynamicStyleParts.push(stripped);
1946
+ existingStyleParts.push(stripped);
1764
1947
  }
1765
1948
  else {
1766
1949
  existingStyleExpr = `...(${innerSource})`;
1767
1950
  }
1768
1951
  }
1769
1952
  }
1770
- args = args.filter((arg) => {
1771
- const expr = arg.expression;
1772
- if (!utils_1.t.isCallExpression(expr) || !utils_1.t.isMemberExpression(expr.callee))
1773
- return true;
1774
- const callee = expr.callee;
1775
- if (!utils_1.t.isIdentifier(callee.object) ||
1776
- !utils_1.t.isIdentifier(callee.property))
1777
- return true;
1778
- const varName = callee.object.value;
1779
- const propKey = callee.property.value;
1780
- const styleInfo = localCreateStyles[varName];
1781
- if (styleInfo?.functions?.[propKey]) {
1782
- const func = styleInfo.functions[propKey];
1783
- const callArgs = expr.arguments;
1784
- const hasSpread = callArgs.some((a) => a.spread);
1785
- if (!hasSpread && callArgs.length >= 1) {
1786
- const tempStaticTable = { ...mergedStaticTable };
1787
- const cssVarInfo = {};
1788
- if (callArgs.length === 1 &&
1789
- callArgs[0].expression.type === 'ObjectExpression') {
1790
- const argObj = (0, utils_1.objectExpressionToObject)(callArgs[0].expression, mergedStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
1791
- func.params.forEach((p) => {
1792
- if (argObj[p] !== undefined)
1793
- tempStaticTable[p] = argObj[p];
1794
- });
1795
- }
1796
- else {
1797
- callArgs.forEach((_callArg, i) => {
1798
- const p = func.params[i];
1799
- if (!p)
1800
- return;
1801
- const cssVar = `--${propKey}-${p}`;
1802
- tempStaticTable[p] = `var(${cssVar})`;
1803
- cssVarInfo[p] = { cssVar, propKey: '' };
1804
- });
1805
- }
1806
- const substituted = (0, utils_1.objectExpressionToObject)(func.body, tempStaticTable, mergedKeyframesTable, mergedViewTransitionTable, mergedCreateThemeHashTable, scannedTables.createThemeObjectTable, mergedCreateTable, mergedCreateStaticHashTable, scannedTables.createStaticObjectTable, mergedVariantsTable);
1807
- if (substituted) {
1808
- const records = processStyleRecords(substituted);
1809
- const hashes = records.map((r) => r.hash).join(' ');
1810
- if (hashes)
1811
- dynamicClassParts.push(JSON.stringify(hashes));
1812
- if (Object.keys(cssVarInfo).length > 0) {
1813
- Object.entries(cssVarInfo).forEach(([paramName, info]) => {
1814
- const targetProp = Object.keys(substituted).find((k) => typeof substituted[k] === 'string' &&
1815
- substituted[k].includes(info.cssVar));
1816
- if (targetProp) {
1817
- const paramIndex = func.params.indexOf(paramName);
1818
- const srcArg = paramIndex >= 0 && callArgs[paramIndex]
1819
- ? callArgs[paramIndex].expression
1820
- : callArgs[0].expression;
1821
- const argStart = srcArg.span.start - baseByteOffset;
1822
- const argEnd = srcArg.span.end - baseByteOffset;
1823
- const argSource = sourceBuffer
1824
- .subarray(argStart, argEnd)
1825
- .toString('utf-8');
1826
- let valueExpr;
1827
- const maybeNumber = Number(argSource);
1828
- if (!isNaN(maybeNumber) &&
1829
- argSource.trim() === String(maybeNumber)) {
1830
- valueExpr = JSON.stringify((0, zss_engine_1.applyCssValue)(maybeNumber, targetProp));
1831
- }
1832
- else if ((argSource.startsWith('"') &&
1833
- argSource.endsWith('"')) ||
1834
- (argSource.startsWith("'") && argSource.endsWith("'"))) {
1835
- valueExpr = JSON.stringify((0, zss_engine_1.applyCssValue)(argSource.slice(1, -1), targetProp));
1836
- }
1837
- else {
1838
- valueExpr = zss_engine_1.exceptionCamelCase.includes(targetProp)
1839
- ? argSource
1840
- : `(typeof (${argSource}) === 'number' ? (${argSource}) + 'px' : (${argSource}))`;
1841
- }
1842
- dynamicStyleParts.push(`"${info.cssVar}": ${valueExpr}`);
1843
- }
1844
- });
1845
- }
1846
- return false;
1847
- }
1848
- }
1849
- }
1850
- return true;
1851
- });
1852
- const styleAttr = dynamicStyleParts.length > 0 || existingStyleExpr
1853
- ? ` style={{ ${[existingStyleExpr, ...dynamicStyleParts].filter(Boolean).join(', ')} }}`
1953
+ const { classParts, isOptimizable, baseStyle, dynamicVars } = buildClassParts(args, dynamicClassParts, existingClassExpr, true);
1954
+ const styleParts = [
1955
+ ...existingStyleParts,
1956
+ ...foldDynamicVars(dynamicVars),
1957
+ ];
1958
+ const styleAttr = styleParts.length > 0 || existingStyleExpr
1959
+ ? ` style={{ ${[existingStyleExpr, ...styleParts].filter(Boolean).join(', ')} }}`
1854
1960
  : '';
1855
- const { classParts, isOptimizable, baseStyle } = buildClassParts(args, dynamicClassParts, existingClassExpr);
1856
1961
  if (isOptimizable &&
1857
1962
  (args.length > 0 ||
1858
1963
  Object.keys(baseStyle).length > 0 ||
@@ -1880,6 +1985,9 @@ async function loader(source) {
1880
1985
  utils_1.t.isIdentifier(callee.property)) {
1881
1986
  const objectName = callee.object.value;
1882
1987
  const propertyName = callee.property.value;
1988
+ if (localCreateStyles[objectName]?.functions?.[propertyName]) {
1989
+ dynamicFnCalls.push(node);
1990
+ }
1883
1991
  const alias = plumeriaAliases[objectName];
1884
1992
  if (alias === 'NAMESPACE' && propertyName === 'use') {
1885
1993
  isUseCall = true;
@@ -1953,6 +2061,16 @@ async function loader(source) {
1953
2061
  });
1954
2062
  }
1955
2063
  });
2064
+ dynamicFnCalls.forEach((call) => {
2065
+ const start = call.span.start - baseByteOffset;
2066
+ const end = call.span.end - baseByteOffset;
2067
+ const isResolved = replacements.some((r) => r.start <= start && r.end >= end);
2068
+ if (!isResolved) {
2069
+ throwCompilationError(`Plumeria: ${getSource(call)} is only supported in the ${styleProp} prop. ` +
2070
+ `A dynamic style function resolves to a class name and a CSS variable on the element itself, ` +
2071
+ `so it cannot be passed through another prop or read as a value.`, call);
2072
+ }
2073
+ });
1956
2074
  const buffer = Buffer.from(source);
1957
2075
  let offset = 0;
1958
2076
  const parts = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumeria/turbopack-loader",
3
- "version": "18.1.5",
3
+ "version": "18.1.6",
4
4
  "description": "Plumeria Turbopack-loader",
5
5
  "author": "Refirst 11",
6
6
  "license": "MIT",
@@ -22,8 +22,8 @@
22
22
  "zero-virtual.css"
23
23
  ],
24
24
  "dependencies": {
25
- "@plumeria/utils": "^18.1.5",
26
- "@plumeria/compiler": "^18.1.5"
25
+ "@plumeria/compiler": "^18.1.6",
26
+ "@plumeria/utils": "^18.1.6"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@swc/core": "1.15.47",