@homebound/truss 2.0.0-next.1 → 2.0.0-next.13

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.
@@ -5,7 +5,7 @@ import { resolve, dirname, isAbsolute } from "path";
5
5
  // src/plugin/transform.ts
6
6
  import { parse } from "@babel/parser";
7
7
  import _traverse2 from "@babel/traverse";
8
- import _generate from "@babel/generator";
8
+ import _generate2 from "@babel/generator";
9
9
  import * as t4 from "@babel/types";
10
10
 
11
11
  // src/plugin/resolve-chain.ts
@@ -142,6 +142,17 @@ function resolveChain(chain, mapping) {
142
142
  segments.push(seg);
143
143
  continue;
144
144
  }
145
+ if (abbr === "typography") {
146
+ const resolved = resolveTypographyCall(
147
+ node,
148
+ mapping,
149
+ currentMediaQuery,
150
+ currentPseudoClass,
151
+ currentPseudoElement
152
+ );
153
+ segments.push(...resolved);
154
+ continue;
155
+ }
145
156
  if (abbr === "element") {
146
157
  if (node.args.length !== 1 || node.args[0].type !== "StringLiteral") {
147
158
  throw new UnsupportedPatternError(
@@ -215,6 +226,52 @@ function conditionKeySuffix(mediaQuery, pseudoClass, pseudoElement, breakpoints)
215
226
  if (pseudoClass) parts.push(pseudoName(pseudoClass));
216
227
  return parts.join("_");
217
228
  }
229
+ function resolveTypographyCall(node, mapping, mediaQuery, pseudoClass, pseudoElement) {
230
+ if (node.args.length !== 1) {
231
+ throw new UnsupportedPatternError(`typography() expects exactly 1 argument, got ${node.args.length}`);
232
+ }
233
+ const argAst = node.args[0];
234
+ if (argAst.type === "StringLiteral") {
235
+ return resolveTypographyEntry(argAst.value, mapping, mediaQuery, pseudoClass, pseudoElement);
236
+ }
237
+ const typography = mapping.typography ?? [];
238
+ if (typography.length === 0) {
239
+ throw new UnsupportedPatternError(`typography() is unavailable because no typography abbreviations were generated`);
240
+ }
241
+ const suffix = conditionKeySuffix(mediaQuery, pseudoClass, pseudoElement, mapping.breakpoints);
242
+ const lookupKey = suffix ? `typography__${suffix}` : "typography";
243
+ const segmentsByName = {};
244
+ for (const name of typography) {
245
+ segmentsByName[name] = resolveTypographyEntry(name, mapping, mediaQuery, pseudoClass, pseudoElement);
246
+ }
247
+ return [
248
+ {
249
+ key: lookupKey,
250
+ defs: {},
251
+ typographyLookup: {
252
+ lookupKey,
253
+ argNode: argAst,
254
+ segmentsByName
255
+ }
256
+ }
257
+ ];
258
+ }
259
+ function resolveTypographyEntry(name, mapping, mediaQuery, pseudoClass, pseudoElement) {
260
+ if (!(mapping.typography ?? []).includes(name)) {
261
+ throw new UnsupportedPatternError(`Unknown typography abbreviation "${name}"`);
262
+ }
263
+ const entry = mapping.abbreviations[name];
264
+ if (!entry) {
265
+ throw new UnsupportedPatternError(`Unknown typography abbreviation "${name}"`);
266
+ }
267
+ const resolved = resolveEntry(name, entry, mapping, mediaQuery, pseudoClass, pseudoElement, null);
268
+ for (const segment of resolved) {
269
+ if (segment.dynamicProps || segment.whenPseudo) {
270
+ throw new UnsupportedPatternError(`Typography abbreviation "${name}" cannot require runtime arguments`);
271
+ }
272
+ }
273
+ return resolved;
274
+ }
218
275
  function wrapDefsWithConditions(defs, mediaQuery, pseudoClass) {
219
276
  if (!mediaQuery && !pseudoClass) return defs;
220
277
  const result = {};
@@ -340,6 +397,7 @@ function resolveDelegateCall(abbr, entry, node, mapping, mediaQuery, pseudoClass
340
397
  pseudoElement,
341
398
  dynamicProps: targetEntry.props,
342
399
  incremented: false,
400
+ appendPx: true,
343
401
  dynamicExtraDefs: targetEntry.extraDefs,
344
402
  argNode: argAst
345
403
  };
@@ -825,6 +883,39 @@ function insertStylexNamespaceImport(ast, localName) {
825
883
  const idx = findLastImportIndex(ast);
826
884
  ast.program.body.splice(idx + 1, 0, stylexImport);
827
885
  }
886
+ function findNamedImportBinding(ast, source, importedName) {
887
+ for (const node of ast.program.body) {
888
+ if (!t.isImportDeclaration(node) || node.source.value !== source) continue;
889
+ for (const spec of node.specifiers) {
890
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported, { name: importedName })) {
891
+ return spec.local.name;
892
+ }
893
+ }
894
+ }
895
+ return null;
896
+ }
897
+ function upsertNamedImports(ast, source, imports) {
898
+ if (imports.length === 0) return;
899
+ for (const node of ast.program.body) {
900
+ if (!t.isImportDeclaration(node) || node.source.value !== source) continue;
901
+ for (const entry of imports) {
902
+ const exists = node.specifiers.some(function(spec) {
903
+ return t.isImportSpecifier(spec) && t.isIdentifier(spec.imported, { name: entry.importedName });
904
+ });
905
+ if (exists) continue;
906
+ node.specifiers.push(t.importSpecifier(t.identifier(entry.localName), t.identifier(entry.importedName)));
907
+ }
908
+ return;
909
+ }
910
+ const importDecl = t.importDeclaration(
911
+ imports.map(function(entry) {
912
+ return t.importSpecifier(t.identifier(entry.localName), t.identifier(entry.importedName));
913
+ }),
914
+ t.stringLiteral(source)
915
+ );
916
+ const idx = findLastImportIndex(ast);
917
+ ast.program.body.splice(idx + 1, 0, importDecl);
918
+ }
828
919
  function extractChain(node, cssBinding) {
829
920
  const chain = [];
830
921
  let current = node;
@@ -869,12 +960,17 @@ function extractChain(node, cssBinding) {
869
960
  import * as t2 from "@babel/types";
870
961
  function collectCreateData(chains) {
871
962
  const createEntries = /* @__PURE__ */ new Map();
963
+ const runtimeLookups = /* @__PURE__ */ new Map();
872
964
  let needsMaybeInc = false;
873
965
  for (const chain of chains) {
874
966
  for (const part of chain.parts) {
875
967
  const segs = part.type === "unconditional" ? part.segments : [...part.thenSegments, ...part.elseSegments];
876
968
  for (const seg of segs) {
877
969
  if (seg.error) continue;
970
+ if (seg.typographyLookup) {
971
+ collectTypographyLookup(createEntries, runtimeLookups, seg);
972
+ continue;
973
+ }
878
974
  if (seg.dynamicProps) {
879
975
  if (!createEntries.has(seg.key)) {
880
976
  createEntries.set(seg.key, {
@@ -903,7 +999,36 @@ function collectCreateData(chains) {
903
999
  }
904
1000
  }
905
1001
  }
906
- return { createEntries, needsMaybeInc };
1002
+ return { createEntries, runtimeLookups, needsMaybeInc };
1003
+ }
1004
+ function collectTypographyLookup(createEntries, runtimeLookups, seg) {
1005
+ const lookup = seg.typographyLookup;
1006
+ if (!lookup) return;
1007
+ if (!runtimeLookups.has(lookup.lookupKey)) {
1008
+ runtimeLookups.set(lookup.lookupKey, {
1009
+ lookupKey: lookup.lookupKey,
1010
+ refsByName: Object.fromEntries(
1011
+ Object.entries(lookup.segmentsByName).map(function([name, segments]) {
1012
+ return [
1013
+ name,
1014
+ segments.map(function(segment) {
1015
+ return segment.key;
1016
+ })
1017
+ ];
1018
+ })
1019
+ )
1020
+ });
1021
+ }
1022
+ for (const segments of Object.values(lookup.segmentsByName)) {
1023
+ for (const segment of segments) {
1024
+ if (createEntries.has(segment.key)) continue;
1025
+ createEntries.set(segment.key, {
1026
+ key: segment.key,
1027
+ defs: segment.defs,
1028
+ whenPseudo: segment.whenPseudo
1029
+ });
1030
+ }
1031
+ }
907
1032
  }
908
1033
  function buildCreateProperties(createEntries, stylexNamespaceName) {
909
1034
  const createProperties = [];
@@ -1045,6 +1170,18 @@ function buildCreateDeclaration(createVarName, stylexNamespaceName, createProper
1045
1170
  ]);
1046
1171
  return t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(createVarName), createCall)]);
1047
1172
  }
1173
+ function buildRuntimeLookupDeclaration(lookupName, createVarName, lookup) {
1174
+ const properties = [];
1175
+ for (const [name, refs] of Object.entries(lookup.refsByName)) {
1176
+ const values = refs.map(function(refKey) {
1177
+ return t2.memberExpression(t2.identifier(createVarName), t2.identifier(refKey));
1178
+ });
1179
+ properties.push(t2.objectProperty(toPropertyKey(name), t2.arrayExpression(values)));
1180
+ }
1181
+ return t2.variableDeclaration("const", [
1182
+ t2.variableDeclarator(t2.identifier(lookupName), t2.objectExpression(properties))
1183
+ ]);
1184
+ }
1048
1185
  function defsToAst(defs) {
1049
1186
  const properties = [];
1050
1187
  for (const [key, value] of Object.entries(defs)) {
@@ -1077,7 +1214,9 @@ function isValidIdentifier(s) {
1077
1214
 
1078
1215
  // src/plugin/rewrite-sites.ts
1079
1216
  import _traverse from "@babel/traverse";
1217
+ import _generate from "@babel/generator";
1080
1218
  import * as t3 from "@babel/types";
1219
+ var generate = _generate.default ?? _generate;
1081
1220
  var traverse = _traverse.default ?? _traverse;
1082
1221
  function rewriteExpressionSites(options) {
1083
1222
  for (const site of options.sites) {
@@ -1088,53 +1227,31 @@ function rewriteExpressionSites(options) {
1088
1227
  t3.memberExpression(t3.identifier(options.stylexNamespaceName), t3.identifier("props")),
1089
1228
  propsArgs
1090
1229
  );
1091
- const openingElement = cssAttrPath.parentPath;
1092
- let existingClassNameExpr = null;
1093
- if (openingElement && openingElement.isJSXOpeningElement()) {
1094
- const attrs = openingElement.node.attributes;
1095
- for (let i = 0; i < attrs.length; i++) {
1096
- const attr = attrs[i];
1097
- if (t3.isJSXAttribute(attr) && t3.isJSXIdentifier(attr.name, { name: "className" })) {
1098
- if (t3.isStringLiteral(attr.value)) {
1099
- existingClassNameExpr = attr.value;
1100
- } else if (t3.isJSXExpressionContainer(attr.value) && t3.isExpression(attr.value.expression)) {
1101
- existingClassNameExpr = attr.value.expression;
1102
- }
1103
- attrs.splice(i, 1);
1104
- break;
1105
- }
1106
- }
1107
- }
1108
- let spreadExpr;
1109
- if (existingClassNameExpr) {
1110
- const rId = t3.identifier("__r");
1111
- const mergedClassName = t3.callExpression(
1112
- t3.memberExpression(
1113
- t3.binaryExpression(
1114
- "+",
1115
- t3.binaryExpression("+", existingClassNameExpr, t3.stringLiteral(" ")),
1116
- t3.logicalExpression("||", t3.memberExpression(rId, t3.identifier("className")), t3.stringLiteral(""))
1117
- ),
1118
- t3.identifier("trim")
1119
- ),
1120
- []
1121
- );
1122
- spreadExpr = t3.callExpression(
1123
- t3.arrowFunctionExpression(
1124
- [rId],
1125
- t3.objectExpression([t3.spreadElement(rId), t3.objectProperty(t3.identifier("className"), mergedClassName)])
1126
- ),
1127
- [propsCall]
1128
- );
1129
- } else {
1130
- spreadExpr = propsCall;
1131
- }
1132
- cssAttrPath.replaceWith(t3.jsxSpreadAttribute(spreadExpr));
1230
+ cssAttrPath.replaceWith(
1231
+ t3.jsxSpreadAttribute(
1232
+ buildCssSpreadExpression(
1233
+ cssAttrPath,
1234
+ propsCall,
1235
+ options.mergePropsHelperName,
1236
+ options.needsMergePropsHelper,
1237
+ options.stylexNamespaceName
1238
+ )
1239
+ )
1240
+ );
1133
1241
  continue;
1134
1242
  }
1135
1243
  site.path.replaceWith(t3.arrayExpression(propsArgs));
1136
1244
  }
1137
- rewriteCssArrayExpressions(options.ast, options.stylexNamespaceName);
1245
+ rewriteStyleObjectExpressions(options.ast);
1246
+ rewriteCssAttributeExpressions(
1247
+ options.ast,
1248
+ options.stylexNamespaceName,
1249
+ options.mergePropsHelperName,
1250
+ options.needsMergePropsHelper,
1251
+ options.asStyleArrayHelperName,
1252
+ options.needsAsStyleArrayHelper,
1253
+ options.skippedCssPropMessages
1254
+ );
1138
1255
  }
1139
1256
  function getCssAttributePath(path) {
1140
1257
  const parentPath = path.parentPath;
@@ -1165,7 +1282,7 @@ function buildPropsArgsFromChain(chain, options) {
1165
1282
  }
1166
1283
  const thenArgs = buildPropsArgs(part.thenSegments, options);
1167
1284
  const elseArgs = buildPropsArgs(part.elseSegments, options);
1168
- if (thenArgs.length === 1 && elseArgs.length === 1) {
1285
+ if (thenArgs.length === 1 && elseArgs.length === 1 && !t3.isSpreadElement(thenArgs[0]) && !t3.isSpreadElement(elseArgs[0])) {
1169
1286
  args.push(t3.conditionalExpression(part.conditionNode, thenArgs[0], elseArgs[0]));
1170
1287
  } else if (thenArgs.length > 0 || elseArgs.length > 0) {
1171
1288
  args.push(
@@ -1181,6 +1298,19 @@ function buildPropsArgs(segments, options) {
1181
1298
  const args = [];
1182
1299
  for (const seg of segments) {
1183
1300
  if (seg.error) continue;
1301
+ if (seg.typographyLookup) {
1302
+ const lookupName = options.runtimeLookupNames.get(seg.typographyLookup.lookupKey);
1303
+ if (!lookupName) {
1304
+ continue;
1305
+ }
1306
+ const lookupAccess = t3.memberExpression(
1307
+ t3.identifier(lookupName),
1308
+ seg.typographyLookup.argNode,
1309
+ true
1310
+ );
1311
+ args.push(t3.spreadElement(t3.logicalExpression("??", lookupAccess, t3.arrayExpression([]))));
1312
+ continue;
1313
+ }
1184
1314
  const ref = t3.memberExpression(t3.identifier(options.createVarName), t3.identifier(seg.key));
1185
1315
  if (seg.dynamicProps && seg.argNode) {
1186
1316
  let argExpr;
@@ -1188,6 +1318,12 @@ function buildPropsArgs(segments, options) {
1188
1318
  argExpr = t3.callExpression(t3.identifier(options.maybeIncHelperName), [seg.argNode]);
1189
1319
  } else if (seg.incremented) {
1190
1320
  argExpr = seg.argNode;
1321
+ } else if (seg.appendPx) {
1322
+ argExpr = t3.binaryExpression(
1323
+ "+",
1324
+ t3.callExpression(t3.identifier("String"), [seg.argNode]),
1325
+ t3.stringLiteral("px")
1326
+ );
1191
1327
  } else {
1192
1328
  argExpr = t3.callExpression(t3.identifier("String"), [seg.argNode]);
1193
1329
  }
@@ -1198,46 +1334,394 @@ function buildPropsArgs(segments, options) {
1198
1334
  }
1199
1335
  return args;
1200
1336
  }
1201
- function rewriteCssArrayExpressions(ast, stylexNamespaceName) {
1337
+ function rewriteCssAttributeExpressions(ast, stylexNamespaceName, mergePropsHelperName, needsMergePropsHelper, asStyleArrayHelperName, needsAsStyleArrayHelper, skippedCssPropMessages) {
1202
1338
  traverse(ast, {
1203
1339
  JSXAttribute(path) {
1204
1340
  if (!t3.isJSXIdentifier(path.node.name, { name: "css" })) return;
1205
1341
  const value = path.node.value;
1206
1342
  if (!t3.isJSXExpressionContainer(value)) return;
1207
- const expr = value.expression;
1208
- if (!t3.isArrayExpression(expr)) return;
1209
- const propsArgs = [];
1210
- for (const el of expr.elements) {
1211
- if (t3.isSpreadElement(el)) {
1212
- const arg = el.argument;
1213
- if (t3.isArrayExpression(arg)) {
1214
- for (const inner of arg.elements) {
1215
- if (!inner) continue;
1216
- if (t3.isSpreadElement(inner)) {
1217
- propsArgs.push(t3.spreadElement(inner.argument));
1218
- } else {
1219
- propsArgs.push(inner);
1220
- }
1221
- }
1222
- } else {
1223
- propsArgs.push(t3.spreadElement(arg));
1224
- }
1225
- } else if (el) {
1226
- propsArgs.push(el);
1227
- }
1343
+ if (!t3.isExpression(value.expression)) return;
1344
+ if (!isCssRewriteableExpression(value.expression, path)) {
1345
+ skippedCssPropMessages.push({
1346
+ message: explainSkippedCssRewrite(value.expression, path),
1347
+ line: path.node.loc?.start.line ?? null
1348
+ });
1349
+ return;
1350
+ }
1351
+ const propsArgs = lowerCssExpressionToPropsArgs(
1352
+ value.expression,
1353
+ path,
1354
+ asStyleArrayHelperName,
1355
+ needsAsStyleArrayHelper
1356
+ );
1357
+ if (!propsArgs) {
1358
+ skippedCssPropMessages.push({
1359
+ message: explainSkippedCssRewrite(value.expression, path),
1360
+ line: path.node.loc?.start.line ?? null
1361
+ });
1362
+ return;
1228
1363
  }
1229
1364
  const propsCall = t3.callExpression(
1230
1365
  t3.memberExpression(t3.identifier(stylexNamespaceName), t3.identifier("props")),
1231
1366
  propsArgs
1232
1367
  );
1233
- path.replaceWith(t3.jsxSpreadAttribute(propsCall));
1368
+ path.replaceWith(
1369
+ t3.jsxSpreadAttribute(
1370
+ buildCssSpreadExpression(path, propsCall, mergePropsHelperName, needsMergePropsHelper, stylexNamespaceName)
1371
+ )
1372
+ );
1373
+ }
1374
+ });
1375
+ }
1376
+ function isCssRewriteableExpression(expr, path) {
1377
+ return !!lowerCssExpressionToPropsArgs(expr, path, "asStyleArray", { current: false });
1378
+ }
1379
+ function lowerCssExpressionToPropsArgs(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1380
+ return buildStyleObjectPropsArgs(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) ?? buildStyleArrayLikePropsArgsFromExpression(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) ?? buildUnknownCssValuePropsArgs(expr, asStyleArrayHelperName, needsAsStyleArrayHelper);
1381
+ }
1382
+ function explainSkippedCssRewrite(expr, path) {
1383
+ if (t3.isObjectExpression(expr)) {
1384
+ for (const prop of expr.properties) {
1385
+ if (!t3.isSpreadElement(prop)) {
1386
+ return `[truss] Unsupported pattern: Could not rewrite css prop: object contains a non-spread property (${formatNodeSnippet(expr)})`;
1387
+ }
1388
+ const normalizedArg = normalizeStyleArrayLikeExpression(prop.argument, path, /* @__PURE__ */ new Set());
1389
+ if (!normalizedArg) {
1390
+ return `[truss] Unsupported pattern: Could not rewrite css prop: spread argument is not style-array-like (${formatNodeSnippet(prop.argument)})`;
1391
+ }
1392
+ }
1393
+ return `[truss] Unsupported pattern: Could not rewrite css prop: object spread composition was not recognized (${formatNodeSnippet(expr)})`;
1394
+ }
1395
+ return `[truss] Unsupported pattern: Could not rewrite css prop: expression is not style-array-like (${formatNodeSnippet(expr)})`;
1396
+ }
1397
+ function formatNodeSnippet(node) {
1398
+ return generate(node, { compact: true, comments: true }).code;
1399
+ }
1400
+ function buildCssSpreadExpression(path, propsCall, mergePropsHelperName, needsMergePropsHelper, stylexNamespaceName) {
1401
+ const existingClassNameExpr = removeExistingClassNameAttribute(path);
1402
+ if (!existingClassNameExpr) return propsCall;
1403
+ needsMergePropsHelper.current = true;
1404
+ return t3.callExpression(t3.identifier(mergePropsHelperName), [
1405
+ t3.identifier(stylexNamespaceName),
1406
+ existingClassNameExpr,
1407
+ ...propsCall.arguments
1408
+ ]);
1409
+ }
1410
+ function removeExistingClassNameAttribute(path) {
1411
+ const openingElement = path.parentPath;
1412
+ if (!openingElement || !openingElement.isJSXOpeningElement()) return null;
1413
+ const attrs = openingElement.node.attributes;
1414
+ for (let i = 0; i < attrs.length; i++) {
1415
+ const attr = attrs[i];
1416
+ if (!t3.isJSXAttribute(attr) || !t3.isJSXIdentifier(attr.name, { name: "className" })) continue;
1417
+ let classNameExpr = null;
1418
+ if (t3.isStringLiteral(attr.value)) {
1419
+ classNameExpr = attr.value;
1420
+ } else if (t3.isJSXExpressionContainer(attr.value) && t3.isExpression(attr.value.expression)) {
1421
+ classNameExpr = attr.value.expression;
1422
+ }
1423
+ attrs.splice(i, 1);
1424
+ return classNameExpr;
1425
+ }
1426
+ return null;
1427
+ }
1428
+ function buildStyleObjectPropsArgs(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1429
+ if (!t3.isObjectExpression(expr) || expr.properties.length === 0) return null;
1430
+ const propsArgs = [];
1431
+ for (const prop of expr.properties) {
1432
+ if (!t3.isSpreadElement(prop)) return null;
1433
+ const normalizedArg = normalizeStyleArrayLikeExpression(prop.argument, path, /* @__PURE__ */ new Set());
1434
+ if (!normalizedArg) {
1435
+ propsArgs.push(
1436
+ t3.spreadElement(
1437
+ buildUnknownObjectSpreadFallback(prop.argument, asStyleArrayHelperName, needsAsStyleArrayHelper)
1438
+ )
1439
+ );
1440
+ continue;
1441
+ }
1442
+ const nestedArgs = buildStyleArrayLikePropsArgs(normalizedArg, path, /* @__PURE__ */ new Set());
1443
+ if (nestedArgs && t3.isArrayExpression(normalizedArg)) {
1444
+ propsArgs.push(...nestedArgs);
1445
+ } else {
1446
+ propsArgs.push(t3.spreadElement(buildSafeSpreadArgument(normalizedArg)));
1447
+ }
1448
+ }
1449
+ return propsArgs.length > 0 ? propsArgs : null;
1450
+ }
1451
+ function buildStyleArrayLikePropsArgsFromExpression(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1452
+ const normalizedExpr = normalizeStyleArrayLikeExpression(expr, path, /* @__PURE__ */ new Set());
1453
+ if (!normalizedExpr) return null;
1454
+ return buildStyleArrayLikePropsArgs(normalizedExpr, path, /* @__PURE__ */ new Set());
1455
+ }
1456
+ function buildStyleArrayLikePropsArgs(expr, path, seen) {
1457
+ if (seen.has(expr)) return null;
1458
+ seen.add(expr);
1459
+ if (t3.isArrayExpression(expr)) {
1460
+ const propsArgs = [];
1461
+ for (const el of expr.elements) {
1462
+ if (!el) continue;
1463
+ if (t3.isSpreadElement(el)) {
1464
+ const normalizedArg = normalizeStyleArrayLikeExpression(el.argument, path, /* @__PURE__ */ new Set());
1465
+ if (!normalizedArg) {
1466
+ propsArgs.push(t3.spreadElement(el.argument));
1467
+ continue;
1468
+ }
1469
+ const nestedArgs = buildStyleArrayLikePropsArgs(normalizedArg, path, seen);
1470
+ if (nestedArgs && t3.isArrayExpression(normalizedArg)) {
1471
+ propsArgs.push(...nestedArgs);
1472
+ } else {
1473
+ propsArgs.push(t3.spreadElement(buildSafeSpreadArgument(normalizedArg)));
1474
+ }
1475
+ continue;
1476
+ }
1477
+ propsArgs.push(el);
1478
+ }
1479
+ return propsArgs;
1480
+ }
1481
+ if (t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isConditionalExpression(expr) || t3.isLogicalExpression(expr) || t3.isCallExpression(expr)) {
1482
+ return [t3.spreadElement(buildSafeSpreadArgument(expr))];
1483
+ }
1484
+ return null;
1485
+ }
1486
+ function buildUnknownCssValuePropsArgs(expr, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1487
+ if (!(t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isCallExpression(expr))) return null;
1488
+ return [t3.spreadElement(buildUnknownObjectSpreadFallback(expr, asStyleArrayHelperName, needsAsStyleArrayHelper))];
1489
+ }
1490
+ function rewriteStyleObjectExpressions(ast) {
1491
+ traverse(ast, {
1492
+ ObjectExpression(path) {
1493
+ const rewritten = tryBuildStyleArrayFromObject(path);
1494
+ if (!rewritten) return;
1495
+ path.replaceWith(rewritten);
1234
1496
  }
1235
1497
  });
1236
1498
  }
1499
+ function tryBuildStyleArrayFromObject(path) {
1500
+ if (path.node.properties.length === 0) return null;
1501
+ let sawStyleArray = false;
1502
+ const elements = [];
1503
+ for (const prop of path.node.properties) {
1504
+ if (!t3.isSpreadElement(prop)) {
1505
+ return null;
1506
+ }
1507
+ const normalizedArg = normalizeStyleArrayLikeExpression(
1508
+ prop.argument,
1509
+ path,
1510
+ /* @__PURE__ */ new Set()
1511
+ // I.e. `...Css.df.$`, `...(cond ? Css.df.$ : {})`, or `...styles.wrapper`
1512
+ );
1513
+ if (!normalizedArg) {
1514
+ elements.push(t3.spreadElement(buildInlineAsStyleArrayExpression(prop.argument)));
1515
+ continue;
1516
+ }
1517
+ if (isKnownStyleArrayLike(normalizedArg, path, /* @__PURE__ */ new Set())) {
1518
+ sawStyleArray = true;
1519
+ }
1520
+ if (t3.isArrayExpression(normalizedArg)) {
1521
+ elements.push(...normalizedArg.elements);
1522
+ continue;
1523
+ }
1524
+ elements.push(t3.spreadElement(buildSafeSpreadArgument(normalizedArg)));
1525
+ }
1526
+ if (!sawStyleArray) return null;
1527
+ return t3.arrayExpression(elements);
1528
+ }
1529
+ function normalizeStyleArrayLikeExpression(expr, path, seen) {
1530
+ if (seen.has(expr)) return null;
1531
+ seen.add(expr);
1532
+ if (t3.isArrayExpression(expr)) return expr;
1533
+ if (t3.isLogicalExpression(expr) && expr.operator === "&&") {
1534
+ const consequent = normalizeStyleArrayLikeExpression(expr.right, path, seen);
1535
+ if (!consequent) return null;
1536
+ return t3.conditionalExpression(expr.left, consequent, t3.arrayExpression([]));
1537
+ }
1538
+ if (t3.isLogicalExpression(expr) && (expr.operator === "||" || expr.operator === "??")) {
1539
+ const left = normalizeStyleArrayLikeExpression(expr.left, path, seen);
1540
+ const right = normalizeStyleArrayLikeBranch(expr.right, path, seen);
1541
+ if (!left || !right) return null;
1542
+ return t3.logicalExpression(expr.operator, left, right);
1543
+ }
1544
+ if (t3.isConditionalExpression(expr)) {
1545
+ const consequent = normalizeStyleArrayLikeBranch(expr.consequent, path, seen);
1546
+ const alternate = normalizeStyleArrayLikeBranch(expr.alternate, path, seen);
1547
+ if (!consequent || !alternate) return null;
1548
+ return t3.conditionalExpression(expr.test, consequent, alternate);
1549
+ }
1550
+ if (t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isCallExpression(expr)) {
1551
+ const nestedSeen = new Set(seen);
1552
+ nestedSeen.delete(expr);
1553
+ if (isStyleArrayLike(expr, path, nestedSeen)) return expr;
1554
+ }
1555
+ return null;
1556
+ }
1557
+ function normalizeStyleArrayLikeBranch(expr, path, seen) {
1558
+ if (isEmptyObjectExpression(expr)) {
1559
+ return t3.arrayExpression([]);
1560
+ }
1561
+ return normalizeStyleArrayLikeExpression(expr, path, seen);
1562
+ }
1563
+ function isStyleArrayLike(expr, path, seen) {
1564
+ if (seen.has(expr)) return false;
1565
+ seen.add(expr);
1566
+ if (t3.isArrayExpression(expr)) return true;
1567
+ if (t3.isLogicalExpression(expr) && expr.operator === "&&") {
1568
+ return isStyleArrayLike(expr.right, path, seen);
1569
+ }
1570
+ if (t3.isLogicalExpression(expr) && (expr.operator === "||" || expr.operator === "??")) {
1571
+ return isStyleArrayLike(expr.left, path, seen) && isStyleArrayLikeBranch(expr.right, path, seen);
1572
+ }
1573
+ if (t3.isConditionalExpression(expr)) {
1574
+ return isStyleArrayLikeBranch(expr.consequent, path, seen) && isStyleArrayLikeBranch(expr.alternate, path, seen);
1575
+ }
1576
+ if (t3.isIdentifier(expr)) {
1577
+ const binding = path.scope.getBinding(expr.name);
1578
+ const bindingPath = binding?.path;
1579
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1580
+ const init = bindingPath.node.init;
1581
+ return !!(init && isStyleArrayLike(init, bindingPath, seen));
1582
+ }
1583
+ if (t3.isCallExpression(expr)) {
1584
+ const returnExpr = getCallStyleArrayLikeExpression(expr, path);
1585
+ return returnExpr ? isStyleArrayLike(returnExpr, path, seen) : true;
1586
+ }
1587
+ if (t3.isMemberExpression(expr)) {
1588
+ const object = expr.object;
1589
+ if (!t3.isIdentifier(object)) return false;
1590
+ const binding = path.scope.getBinding(object.name);
1591
+ const bindingPath = binding?.path;
1592
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1593
+ const init = bindingPath.node.init;
1594
+ if (!init || !t3.isObjectExpression(init)) return false;
1595
+ const propertyName = getStaticMemberPropertyName(expr, path);
1596
+ if (!propertyName) return false;
1597
+ for (const prop of init.properties) {
1598
+ if (!t3.isObjectProperty(prop) || prop.computed) continue;
1599
+ if (!isMatchingPropertyName(prop.key, propertyName)) continue;
1600
+ const value = prop.value;
1601
+ return t3.isExpression(value) && isStyleArrayLike(value, bindingPath, seen);
1602
+ }
1603
+ }
1604
+ return false;
1605
+ }
1606
+ function isKnownStyleArrayLike(expr, path, seen) {
1607
+ if (seen.has(expr)) return false;
1608
+ seen.add(expr);
1609
+ if (t3.isArrayExpression(expr)) return true;
1610
+ if (t3.isLogicalExpression(expr) && expr.operator === "&&") {
1611
+ return isKnownStyleArrayLike(expr.right, path, seen);
1612
+ }
1613
+ if (t3.isLogicalExpression(expr) && (expr.operator === "||" || expr.operator === "??")) {
1614
+ return isKnownStyleArrayLike(expr.left, path, seen) && isStyleArrayLikeBranch(expr.right, path, seen);
1615
+ }
1616
+ if (t3.isConditionalExpression(expr)) {
1617
+ return isStyleArrayLikeBranch(expr.consequent, path, seen) && isStyleArrayLikeBranch(expr.alternate, path, seen);
1618
+ }
1619
+ if (t3.isIdentifier(expr)) {
1620
+ const binding = path.scope.getBinding(expr.name);
1621
+ const bindingPath = binding?.path;
1622
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1623
+ const init = bindingPath.node.init;
1624
+ return !!(init && isKnownStyleArrayLike(init, bindingPath, seen));
1625
+ }
1626
+ if (t3.isCallExpression(expr)) {
1627
+ const returnExpr = getCallStyleArrayLikeExpression(expr, path);
1628
+ return !!(returnExpr && isKnownStyleArrayLike(returnExpr, path, seen));
1629
+ }
1630
+ if (t3.isMemberExpression(expr)) {
1631
+ const object = expr.object;
1632
+ if (!t3.isIdentifier(object)) return false;
1633
+ const binding = path.scope.getBinding(object.name);
1634
+ const bindingPath = binding?.path;
1635
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1636
+ const init = bindingPath.node.init;
1637
+ if (!init || !t3.isObjectExpression(init)) return false;
1638
+ const propertyName = getStaticMemberPropertyName(expr, path);
1639
+ if (!propertyName) return false;
1640
+ for (const prop of init.properties) {
1641
+ if (!t3.isObjectProperty(prop) || prop.computed) continue;
1642
+ if (!isMatchingPropertyName(prop.key, propertyName)) continue;
1643
+ const value = prop.value;
1644
+ return t3.isExpression(value) && isKnownStyleArrayLike(value, bindingPath, seen);
1645
+ }
1646
+ }
1647
+ return false;
1648
+ }
1649
+ function isStyleArrayLikeBranch(expr, path, seen) {
1650
+ return isEmptyObjectExpression(expr) || isStyleArrayLike(expr, path, seen);
1651
+ }
1652
+ function isMatchingPropertyName(key, name) {
1653
+ return t3.isIdentifier(key) && key.name === name || t3.isStringLiteral(key) && key.value === name;
1654
+ }
1655
+ function isEmptyObjectExpression(expr) {
1656
+ return t3.isObjectExpression(expr) && expr.properties.length === 0;
1657
+ }
1658
+ function buildUnknownObjectSpreadFallback(expr, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1659
+ needsAsStyleArrayHelper.current = true;
1660
+ return t3.callExpression(t3.identifier(asStyleArrayHelperName), [expr]);
1661
+ }
1662
+ function buildInlineAsStyleArrayExpression(expr) {
1663
+ return t3.conditionalExpression(
1664
+ t3.callExpression(t3.memberExpression(t3.identifier("Array"), t3.identifier("isArray")), [expr]),
1665
+ expr,
1666
+ t3.conditionalExpression(expr, t3.arrayExpression([expr]), t3.arrayExpression([]))
1667
+ );
1668
+ }
1669
+ function buildSafeSpreadArgument(expr) {
1670
+ return t3.isConditionalExpression(expr) || t3.isLogicalExpression(expr) ? t3.parenthesizedExpression(expr) : expr;
1671
+ }
1672
+ function getStaticMemberPropertyName(expr, path) {
1673
+ if (!expr.computed && t3.isIdentifier(expr.property)) {
1674
+ return expr.property.name;
1675
+ }
1676
+ if (t3.isStringLiteral(expr.property)) {
1677
+ return expr.property.value;
1678
+ }
1679
+ if (t3.isIdentifier(expr.property)) {
1680
+ const binding = path.scope.getBinding(expr.property.name);
1681
+ const bindingPath = binding?.path;
1682
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return null;
1683
+ const init = bindingPath.node.init;
1684
+ return t3.isStringLiteral(init) ? init.value : null;
1685
+ }
1686
+ return null;
1687
+ }
1688
+ function getCallStyleArrayLikeExpression(expr, path) {
1689
+ const localReturnExpr = getLocalFunctionReturnExpression(expr, path);
1690
+ if (localReturnExpr) return localReturnExpr;
1691
+ const firstArg = expr.arguments[0];
1692
+ if (firstArg && !t3.isSpreadElement(firstArg) && (t3.isArrowFunctionExpression(firstArg) || t3.isFunctionExpression(firstArg))) {
1693
+ return getFunctionLikeReturnExpression(firstArg);
1694
+ }
1695
+ return null;
1696
+ }
1697
+ function getLocalFunctionReturnExpression(expr, path) {
1698
+ if (!t3.isIdentifier(expr.callee)) return null;
1699
+ const binding = path.scope.getBinding(expr.callee.name);
1700
+ const bindingPath = binding?.path;
1701
+ if (!bindingPath) return null;
1702
+ if (bindingPath.isFunctionDeclaration()) {
1703
+ return getFunctionLikeReturnExpression(bindingPath.node);
1704
+ }
1705
+ if (bindingPath.isVariableDeclarator()) {
1706
+ const init = bindingPath.node.init;
1707
+ if (init && (t3.isArrowFunctionExpression(init) || t3.isFunctionExpression(init))) {
1708
+ return getFunctionLikeReturnExpression(init);
1709
+ }
1710
+ }
1711
+ return null;
1712
+ }
1713
+ function getFunctionLikeReturnExpression(fn) {
1714
+ if (t3.isExpression(fn.body)) {
1715
+ return fn.body;
1716
+ }
1717
+ if (fn.body.body.length !== 1) return null;
1718
+ const stmt = fn.body.body[0];
1719
+ return t3.isReturnStatement(stmt) && stmt.argument && t3.isExpression(stmt.argument) ? stmt.argument : null;
1720
+ }
1237
1721
 
1238
1722
  // src/plugin/transform.ts
1239
1723
  var traverse2 = _traverse2.default ?? _traverse2;
1240
- var generate = _generate.default ?? _generate;
1724
+ var generate2 = _generate2.default ?? _generate2;
1241
1725
  function transformTruss(code, filename, mapping) {
1242
1726
  if (!code.includes("Css")) return null;
1243
1727
  const ast = parse(code, {
@@ -1268,24 +1752,50 @@ function transformTruss(code, filename, mapping) {
1268
1752
  }
1269
1753
  });
1270
1754
  if (sites.length === 0) return null;
1271
- const { createEntries, needsMaybeInc } = collectCreateData(sites.map((s) => s.resolvedChain));
1755
+ const { createEntries, runtimeLookups, needsMaybeInc } = collectCreateData(sites.map((s) => s.resolvedChain));
1272
1756
  const usedTopLevelNames = collectTopLevelBindings(ast);
1273
1757
  const existingStylexNamespace = findStylexNamespaceImport(ast);
1274
1758
  const stylexNamespaceName = existingStylexNamespace ?? reservePreferredName(usedTopLevelNames, "stylex");
1275
1759
  const createVarName = reservePreferredName(usedTopLevelNames, "css", "css_");
1276
1760
  const maybeIncHelperName = needsMaybeInc ? reservePreferredName(usedTopLevelNames, "__maybeInc") : null;
1761
+ const existingMergePropsHelperName = findNamedImportBinding(ast, "@homebound/truss/runtime", "mergeProps");
1762
+ const mergePropsHelperName = existingMergePropsHelperName ?? reservePreferredName(usedTopLevelNames, "mergeProps");
1763
+ const needsMergePropsHelper = { current: false };
1764
+ const existingAsStyleArrayHelperName = findNamedImportBinding(ast, "@homebound/truss/runtime", "asStyleArray");
1765
+ const asStyleArrayHelperName = existingAsStyleArrayHelperName ?? reservePreferredName(usedTopLevelNames, "asStyleArray");
1766
+ const needsAsStyleArrayHelper = { current: false };
1767
+ const runtimeLookupNames = /* @__PURE__ */ new Map();
1768
+ for (const [lookupKey] of runtimeLookups) {
1769
+ runtimeLookupNames.set(lookupKey, reservePreferredName(usedTopLevelNames, `__${lookupKey}`));
1770
+ }
1277
1771
  const createProperties = buildCreateProperties(createEntries, stylexNamespaceName);
1278
1772
  rewriteExpressionSites({
1279
1773
  ast,
1280
1774
  sites,
1281
1775
  createVarName,
1282
1776
  stylexNamespaceName,
1283
- maybeIncHelperName
1777
+ maybeIncHelperName,
1778
+ mergePropsHelperName,
1779
+ needsMergePropsHelper,
1780
+ asStyleArrayHelperName,
1781
+ needsAsStyleArrayHelper,
1782
+ skippedCssPropMessages: errorMessages,
1783
+ runtimeLookupNames
1284
1784
  });
1285
1785
  removeCssImport(ast, cssBindingName);
1286
1786
  if (!findStylexNamespaceImport(ast)) {
1287
1787
  insertStylexNamespaceImport(ast, stylexNamespaceName);
1288
1788
  }
1789
+ if (needsMergePropsHelper.current || needsAsStyleArrayHelper.current) {
1790
+ const runtimeImports = [];
1791
+ if (needsMergePropsHelper.current) {
1792
+ runtimeImports.push({ importedName: "mergeProps", localName: mergePropsHelperName });
1793
+ }
1794
+ if (needsAsStyleArrayHelper.current) {
1795
+ runtimeImports.push({ importedName: "asStyleArray", localName: asStyleArrayHelperName });
1796
+ }
1797
+ upsertNamedImports(ast, "@homebound/truss/runtime", runtimeImports);
1798
+ }
1289
1799
  const markerVarNames = collectReferencedMarkerNames(createEntries);
1290
1800
  const hoistedMarkerDecls = hoistMarkerDeclarations(ast, markerVarNames);
1291
1801
  const declarationsToInsert = [];
@@ -1295,6 +1805,11 @@ function transformTruss(code, filename, mapping) {
1295
1805
  declarationsToInsert.push(...hoistedMarkerDecls);
1296
1806
  if (createProperties.length > 0) {
1297
1807
  declarationsToInsert.push(buildCreateDeclaration(createVarName, stylexNamespaceName, createProperties));
1808
+ for (const [lookupKey, lookup] of runtimeLookups) {
1809
+ const lookupName = runtimeLookupNames.get(lookupKey);
1810
+ if (!lookupName) continue;
1811
+ declarationsToInsert.push(buildRuntimeLookupDeclaration(lookupName, createVarName, lookup));
1812
+ }
1298
1813
  }
1299
1814
  for (const { message, line } of errorMessages) {
1300
1815
  const location = line !== null ? `${filename}:${line}` : filename;
@@ -1307,10 +1822,12 @@ function transformTruss(code, filename, mapping) {
1307
1822
  declarationsToInsert.push(consoleError);
1308
1823
  }
1309
1824
  if (declarationsToInsert.length > 0) {
1310
- const insertIndex = findLastImportIndex(ast) + 1;
1311
- ast.program.body.splice(insertIndex, 0, ...declarationsToInsert);
1825
+ const insertIndex = ast.program.body.findIndex(function(node) {
1826
+ return !t4.isImportDeclaration(node);
1827
+ });
1828
+ ast.program.body.splice(insertIndex === -1 ? ast.program.body.length : insertIndex, 0, ...declarationsToInsert);
1312
1829
  }
1313
- const output = generate(ast, {
1830
+ const output = generate2(ast, {
1314
1831
  sourceFileName: filename,
1315
1832
  retainLines: false
1316
1833
  });
@@ -1358,7 +1875,72 @@ function hoistMarkerDeclarations(ast, names) {
1358
1875
 
1359
1876
  // src/plugin/transform-css.ts
1360
1877
  import { parse as parse2 } from "@babel/parser";
1878
+ import * as t6 from "@babel/types";
1879
+
1880
+ // src/plugin/css-ts-utils.ts
1361
1881
  import * as t5 from "@babel/types";
1882
+ function collectStaticStringBindings(ast) {
1883
+ const bindings = /* @__PURE__ */ new Map();
1884
+ let changed = true;
1885
+ while (changed) {
1886
+ changed = false;
1887
+ for (const node of ast.program.body) {
1888
+ const declaration = getTopLevelVariableDeclaration(node);
1889
+ if (!declaration) continue;
1890
+ for (const declarator of declaration.declarations) {
1891
+ if (!t5.isIdentifier(declarator.id) || !declarator.init) continue;
1892
+ if (bindings.has(declarator.id.name)) continue;
1893
+ const value = resolveStaticString(declarator.init, bindings);
1894
+ if (value === null) continue;
1895
+ bindings.set(declarator.id.name, value);
1896
+ changed = true;
1897
+ }
1898
+ }
1899
+ }
1900
+ return bindings;
1901
+ }
1902
+ function resolveStaticString(node, bindings) {
1903
+ if (!node) return null;
1904
+ if (t5.isStringLiteral(node)) return node.value;
1905
+ if (t5.isTemplateLiteral(node)) {
1906
+ let value = "";
1907
+ for (let i = 0; i < node.quasis.length; i++) {
1908
+ value += node.quasis[i].value.cooked ?? "";
1909
+ if (i >= node.expressions.length) continue;
1910
+ const expressionValue = resolveStaticString(node.expressions[i], bindings);
1911
+ if (expressionValue === null) return null;
1912
+ value += expressionValue;
1913
+ }
1914
+ return value;
1915
+ }
1916
+ if (t5.isIdentifier(node)) {
1917
+ return bindings.get(node.name) ?? null;
1918
+ }
1919
+ if (t5.isTSAsExpression(node) || t5.isTSSatisfiesExpression(node) || t5.isTSNonNullExpression(node)) {
1920
+ return resolveStaticString(node.expression, bindings);
1921
+ }
1922
+ if (t5.isParenthesizedExpression(node)) {
1923
+ return resolveStaticString(node.expression, bindings);
1924
+ }
1925
+ if (t5.isBinaryExpression(node, { operator: "+" })) {
1926
+ const left = resolveStaticString(node.left, bindings);
1927
+ const right = resolveStaticString(node.right, bindings);
1928
+ if (left === null || right === null) return null;
1929
+ return left + right;
1930
+ }
1931
+ return null;
1932
+ }
1933
+ function getTopLevelVariableDeclaration(node) {
1934
+ if (t5.isVariableDeclaration(node)) {
1935
+ return node;
1936
+ }
1937
+ if (t5.isExportNamedDeclaration(node) && node.declaration && t5.isVariableDeclaration(node.declaration)) {
1938
+ return node.declaration;
1939
+ }
1940
+ return null;
1941
+ }
1942
+
1943
+ // src/plugin/transform-css.ts
1362
1944
  function transformCssTs(code, filename, mapping) {
1363
1945
  const ast = parse2(code, {
1364
1946
  sourceType: "module",
@@ -1370,28 +1952,29 @@ function transformCssTs(code, filename, mapping) {
1370
1952
  return `/* [truss] ${filename}: no Css import found */
1371
1953
  `;
1372
1954
  }
1373
- const defaultExport = findDefaultExportObject(ast);
1374
- if (!defaultExport) {
1375
- return `/* [truss] ${filename}: expected \`export default { ... }\` with an object literal */
1955
+ const cssExport = findNamedCssExportObject(ast);
1956
+ if (!cssExport) {
1957
+ return `/* [truss] ${filename}: expected \`export const css = { ... }\` with an object literal */
1376
1958
  `;
1377
1959
  }
1378
1960
  const rules = [];
1379
- for (const prop of defaultExport.properties) {
1380
- if (t5.isSpreadElement(prop)) {
1961
+ const stringBindings = collectStaticStringBindings(ast);
1962
+ for (const prop of cssExport.properties) {
1963
+ if (t6.isSpreadElement(prop)) {
1381
1964
  rules.push(`/* [truss] unsupported: spread elements in css.ts export */`);
1382
1965
  continue;
1383
1966
  }
1384
- if (!t5.isObjectProperty(prop)) {
1967
+ if (!t6.isObjectProperty(prop)) {
1385
1968
  rules.push(`/* [truss] unsupported: non-property in css.ts export */`);
1386
1969
  continue;
1387
1970
  }
1388
- const selector = objectPropertyStringKey(prop);
1971
+ const selector = objectPropertyStringKey(prop, stringBindings);
1389
1972
  if (selector === null) {
1390
1973
  rules.push(`/* [truss] unsupported: non-string-literal key in css.ts export */`);
1391
1974
  continue;
1392
1975
  }
1393
1976
  const valueNode = prop.value;
1394
- if (!t5.isExpression(valueNode)) {
1977
+ if (!t6.isExpression(valueNode)) {
1395
1978
  rules.push(`/* [truss] unsupported: "${selector}" value is not an expression */`);
1396
1979
  continue;
1397
1980
  }
@@ -1404,23 +1987,32 @@ function transformCssTs(code, filename, mapping) {
1404
1987
  }
1405
1988
  return rules.join("\n\n") + "\n";
1406
1989
  }
1407
- function findDefaultExportObject(ast) {
1990
+ function findNamedCssExportObject(ast) {
1408
1991
  for (const node of ast.program.body) {
1409
- if (!t5.isExportDefaultDeclaration(node)) continue;
1410
- const decl = node.declaration;
1411
- if (t5.isObjectExpression(decl)) return decl;
1412
- if (t5.isTSAsExpression(decl) && t5.isObjectExpression(decl.expression)) return decl.expression;
1413
- if (t5.isTSSatisfiesExpression(decl) && t5.isObjectExpression(decl.expression)) return decl.expression;
1992
+ if (!t6.isExportNamedDeclaration(node) || !node.declaration) continue;
1993
+ if (!t6.isVariableDeclaration(node.declaration)) continue;
1994
+ for (const declarator of node.declaration.declarations) {
1995
+ if (!t6.isIdentifier(declarator.id, { name: "css" })) continue;
1996
+ const value = unwrapObjectExpression(declarator.init);
1997
+ if (value) return value;
1998
+ }
1414
1999
  }
1415
2000
  return null;
1416
2001
  }
1417
- function objectPropertyStringKey(prop) {
1418
- if (t5.isStringLiteral(prop.key)) return prop.key.value;
1419
- if (t5.isIdentifier(prop.key) && !prop.computed) return prop.key.name;
2002
+ function unwrapObjectExpression(node) {
2003
+ if (!node) return null;
2004
+ if (t6.isObjectExpression(node)) return node;
2005
+ if (t6.isTSAsExpression(node) || t6.isTSSatisfiesExpression(node)) return unwrapObjectExpression(node.expression);
2006
+ return null;
2007
+ }
2008
+ function objectPropertyStringKey(prop, stringBindings) {
2009
+ if (t6.isStringLiteral(prop.key)) return prop.key.value;
2010
+ if (t6.isIdentifier(prop.key) && !prop.computed) return prop.key.name;
2011
+ if (prop.computed) return resolveStaticString(prop.key, stringBindings);
1420
2012
  return null;
1421
2013
  }
1422
2014
  function resolveCssExpression(node, cssBindingName, mapping, filename) {
1423
- if (!t5.isMemberExpression(node) || node.computed || !t5.isIdentifier(node.property, { name: "$" })) {
2015
+ if (!t6.isMemberExpression(node) || node.computed || !t6.isIdentifier(node.property, { name: "$" })) {
1424
2016
  return { error: "value must be a Css.*.$ expression" };
1425
2017
  }
1426
2018
  const chain = extractChain(node.object, cssBindingName);
@@ -1450,6 +2042,9 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
1450
2042
  if (seg.dynamicProps && !seg.argResolved) {
1451
2043
  return { error: `dynamic value with variable argument is not supported in .css.ts files` };
1452
2044
  }
2045
+ if (seg.typographyLookup) {
2046
+ return { error: `typography() with a runtime key is not supported in .css.ts files` };
2047
+ }
1453
2048
  if (seg.mediaQuery) {
1454
2049
  return { error: `media query modifiers (ifSm, ifMd, etc.) are not supported in .css.ts files` };
1455
2050
  }
@@ -1486,8 +2081,61 @@ ${body}
1486
2081
  }`;
1487
2082
  }
1488
2083
 
2084
+ // src/plugin/rewrite-css-ts-imports.ts
2085
+ import { parse as parse3 } from "@babel/parser";
2086
+ import _generate3 from "@babel/generator";
2087
+ import * as t7 from "@babel/types";
2088
+ var generate3 = _generate3.default ?? _generate3;
2089
+ function rewriteCssTsImports(code, filename) {
2090
+ if (!code.includes(".css.ts")) {
2091
+ return { code, changed: false };
2092
+ }
2093
+ const ast = parse3(code, {
2094
+ sourceType: "module",
2095
+ plugins: ["typescript", "jsx"],
2096
+ sourceFilename: filename
2097
+ });
2098
+ const existingCssSideEffects = /* @__PURE__ */ new Set();
2099
+ const neededCssSideEffects = /* @__PURE__ */ new Set();
2100
+ let changed = false;
2101
+ for (const node of ast.program.body) {
2102
+ if (!t7.isImportDeclaration(node)) continue;
2103
+ if (typeof node.source.value !== "string") continue;
2104
+ if (!node.source.value.endsWith(".css.ts")) continue;
2105
+ if (node.specifiers.length === 0) {
2106
+ node.source = t7.stringLiteral(toVirtualCssSpecifier(node.source.value));
2107
+ existingCssSideEffects.add(node.source.value);
2108
+ changed = true;
2109
+ continue;
2110
+ }
2111
+ neededCssSideEffects.add(toVirtualCssSpecifier(node.source.value));
2112
+ }
2113
+ const sideEffectImports = [];
2114
+ for (const source of neededCssSideEffects) {
2115
+ if (existingCssSideEffects.has(source)) continue;
2116
+ sideEffectImports.push(t7.importDeclaration([], t7.stringLiteral(source)));
2117
+ changed = true;
2118
+ }
2119
+ if (!changed) {
2120
+ return { code, changed: false };
2121
+ }
2122
+ if (sideEffectImports.length > 0) {
2123
+ const insertIndex = findLastImportIndex(ast) + 1;
2124
+ ast.program.body.splice(insertIndex, 0, ...sideEffectImports);
2125
+ }
2126
+ const output = generate3(ast, {
2127
+ sourceFileName: filename,
2128
+ retainLines: false
2129
+ });
2130
+ return { code: output.code, changed: true };
2131
+ }
2132
+ function toVirtualCssSpecifier(source) {
2133
+ return `${source}?truss-css`;
2134
+ }
2135
+
1489
2136
  // src/plugin/index.ts
1490
2137
  var VIRTUAL_CSS_PREFIX = "\0truss-css:";
2138
+ var CSS_TS_QUERY = "?truss-css";
1491
2139
  function trussPlugin(opts) {
1492
2140
  let mapping = null;
1493
2141
  let projectRoot;
@@ -1511,15 +2159,8 @@ function trussPlugin(opts) {
1511
2159
  ensureMapping();
1512
2160
  },
1513
2161
  resolveId(source, importer) {
1514
- if (!source.endsWith(".css.ts")) return null;
1515
- let absolutePath;
1516
- if (isAbsolute(source)) {
1517
- absolutePath = source;
1518
- } else if (importer) {
1519
- absolutePath = resolve(dirname(importer), source);
1520
- } else {
1521
- absolutePath = resolve(projectRoot || process.cwd(), source);
1522
- }
2162
+ if (!source.endsWith(CSS_TS_QUERY)) return null;
2163
+ const absolutePath = resolveImportPath(source.slice(0, -CSS_TS_QUERY.length), importer, projectRoot);
1523
2164
  if (!existsSync(absolutePath)) return null;
1524
2165
  return VIRTUAL_CSS_PREFIX + absolutePath.slice(0, -3);
1525
2166
  },
@@ -1531,17 +2172,38 @@ function trussPlugin(opts) {
1531
2172
  },
1532
2173
  transform(code, id) {
1533
2174
  if (!/\.[cm]?[jt]sx?(\?|$)/.test(id)) return null;
1534
- if (!code.includes("Css")) return null;
2175
+ const rewrittenImports = rewriteCssTsImports(code, id);
2176
+ const rewrittenCode = rewrittenImports.code;
2177
+ const hasCssDsl = rewrittenCode.includes("Css");
2178
+ if (!hasCssDsl && !rewrittenImports.changed) return null;
1535
2179
  const fileId = stripQueryAndHash(id);
1536
2180
  if (isNodeModulesFile(fileId) && !isWhitelistedExternalPackageFile(fileId, externalPackages)) {
1537
2181
  return null;
1538
2182
  }
1539
- const result = transformTruss(code, id, ensureMapping());
1540
- if (!result) return null;
2183
+ if (fileId.endsWith(".css.ts")) {
2184
+ return rewrittenImports.changed ? { code: rewrittenCode, map: null } : null;
2185
+ }
2186
+ if (!hasCssDsl) {
2187
+ return { code: rewrittenCode, map: null };
2188
+ }
2189
+ const result = transformTruss(rewrittenCode, id, ensureMapping());
2190
+ if (!result) {
2191
+ if (!rewrittenImports.changed) return null;
2192
+ return { code: rewrittenCode, map: null };
2193
+ }
1541
2194
  return { code: result.code, map: result.map };
1542
2195
  }
1543
2196
  };
1544
2197
  }
2198
+ function resolveImportPath(source, importer, projectRoot) {
2199
+ if (isAbsolute(source)) {
2200
+ return source;
2201
+ }
2202
+ if (importer) {
2203
+ return resolve(dirname(importer), source);
2204
+ }
2205
+ return resolve(projectRoot || process.cwd(), source);
2206
+ }
1545
2207
  function stripQueryAndHash(id) {
1546
2208
  const queryIndex = id.indexOf("?");
1547
2209
  const hashIndex = id.indexOf("#");