@homebound/truss 2.0.0-next.9 → 2.0.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.
@@ -5,8 +5,9 @@ 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
+ import { basename } from "path";
10
11
 
11
12
  // src/plugin/resolve-chain.ts
12
13
  function resolveFullChain(chain, mapping) {
@@ -397,15 +398,32 @@ function resolveDelegateCall(abbr, entry, node, mapping, mediaQuery, pseudoClass
397
398
  pseudoElement,
398
399
  dynamicProps: targetEntry.props,
399
400
  incremented: false,
401
+ appendPx: true,
400
402
  dynamicExtraDefs: targetEntry.extraDefs,
401
403
  argNode: argAst
402
404
  };
403
405
  }
404
406
  }
405
407
  function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement) {
408
+ if (node.args.length === 1) {
409
+ const styleArg = node.args[0];
410
+ if (styleArg.type === "SpreadElement") {
411
+ throw new UnsupportedPatternError(`add() does not support spread arguments`);
412
+ }
413
+ if (styleArg.type === "ObjectExpression") {
414
+ throw new UnsupportedPatternError(
415
+ `add(cssProp) does not accept object literals -- pass an existing CssProp expression instead`
416
+ );
417
+ }
418
+ return {
419
+ key: "__composed_css_prop",
420
+ defs: {},
421
+ styleArrayArg: styleArg
422
+ };
423
+ }
406
424
  if (node.args.length !== 2) {
407
425
  throw new UnsupportedPatternError(
408
- `add() requires exactly 2 arguments (property name and value), got ${node.args.length}. The add({...}) object overload is not supported -- use add("propName", value) instead`
426
+ `add() requires exactly 2 arguments (property name and value), got ${node.args.length}. Supported overloads are add(cssProp) and add("propName", value)`
409
427
  );
410
428
  }
411
429
  const propArg = node.args[0];
@@ -512,7 +530,7 @@ function mergeOverlappingConditions(segments) {
512
530
  const propToIndices = /* @__PURE__ */ new Map();
513
531
  for (let i = 0; i < segments.length; i++) {
514
532
  const seg = segments[i];
515
- if (seg.dynamicProps || seg.whenPseudo || seg.error) continue;
533
+ if (seg.dynamicProps || seg.styleArrayArg || seg.whenPseudo || seg.error) continue;
516
534
  for (const prop of Object.keys(seg.defs)) {
517
535
  if (!propToIndices.has(prop)) propToIndices.set(prop, []);
518
536
  propToIndices.get(prop).push(i);
@@ -882,6 +900,39 @@ function insertStylexNamespaceImport(ast, localName) {
882
900
  const idx = findLastImportIndex(ast);
883
901
  ast.program.body.splice(idx + 1, 0, stylexImport);
884
902
  }
903
+ function findNamedImportBinding(ast, source, importedName) {
904
+ for (const node of ast.program.body) {
905
+ if (!t.isImportDeclaration(node) || node.source.value !== source) continue;
906
+ for (const spec of node.specifiers) {
907
+ if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported, { name: importedName })) {
908
+ return spec.local.name;
909
+ }
910
+ }
911
+ }
912
+ return null;
913
+ }
914
+ function upsertNamedImports(ast, source, imports) {
915
+ if (imports.length === 0) return;
916
+ for (const node of ast.program.body) {
917
+ if (!t.isImportDeclaration(node) || node.source.value !== source) continue;
918
+ for (const entry of imports) {
919
+ const exists = node.specifiers.some(function(spec) {
920
+ return t.isImportSpecifier(spec) && t.isIdentifier(spec.imported, { name: entry.importedName });
921
+ });
922
+ if (exists) continue;
923
+ node.specifiers.push(t.importSpecifier(t.identifier(entry.localName), t.identifier(entry.importedName)));
924
+ }
925
+ return;
926
+ }
927
+ const importDecl = t.importDeclaration(
928
+ imports.map(function(entry) {
929
+ return t.importSpecifier(t.identifier(entry.localName), t.identifier(entry.importedName));
930
+ }),
931
+ t.stringLiteral(source)
932
+ );
933
+ const idx = findLastImportIndex(ast);
934
+ ast.program.body.splice(idx + 1, 0, importDecl);
935
+ }
885
936
  function extractChain(node, cssBinding) {
886
937
  const chain = [];
887
938
  let current = node;
@@ -937,6 +988,9 @@ function collectCreateData(chains) {
937
988
  collectTypographyLookup(createEntries, runtimeLookups, seg);
938
989
  continue;
939
990
  }
991
+ if (seg.styleArrayArg) {
992
+ continue;
993
+ }
940
994
  if (seg.dynamicProps) {
941
995
  if (!createEntries.has(seg.key)) {
942
996
  createEntries.set(seg.key, {
@@ -1130,44 +1184,6 @@ function buildMaybeIncDeclaration(helperName, increment) {
1130
1184
  t2.variableDeclarator(t2.identifier(helperName), t2.arrowFunctionExpression([incParam], body))
1131
1185
  ]);
1132
1186
  }
1133
- function buildMergePropsDeclaration(helperName, stylexNamespaceName) {
1134
- const explicitClassNameParam = t2.identifier("explicitClassName");
1135
- const stylesRestParam = t2.restElement(t2.identifier("styles"));
1136
- const sxId = t2.identifier("sx");
1137
- return t2.functionDeclaration(
1138
- t2.identifier(helperName),
1139
- [explicitClassNameParam, stylesRestParam],
1140
- t2.blockStatement([
1141
- t2.variableDeclaration("const", [
1142
- t2.variableDeclarator(
1143
- sxId,
1144
- t2.callExpression(t2.memberExpression(t2.identifier(stylexNamespaceName), t2.identifier("props")), [
1145
- t2.spreadElement(t2.identifier("styles"))
1146
- ])
1147
- )
1148
- ]),
1149
- t2.returnStatement(
1150
- t2.objectExpression([
1151
- t2.spreadElement(sxId),
1152
- t2.objectProperty(
1153
- t2.identifier("className"),
1154
- t2.callExpression(
1155
- t2.memberExpression(
1156
- t2.binaryExpression(
1157
- "+",
1158
- t2.binaryExpression("+", explicitClassNameParam, t2.stringLiteral(" ")),
1159
- t2.logicalExpression("||", t2.memberExpression(sxId, t2.identifier("className")), t2.stringLiteral(""))
1160
- ),
1161
- t2.identifier("trim")
1162
- ),
1163
- []
1164
- )
1165
- )
1166
- ])
1167
- )
1168
- ])
1169
- );
1170
- }
1171
1187
  function buildCreateDeclaration(createVarName, stylexNamespaceName, createProperties) {
1172
1188
  const createCall = t2.callExpression(t2.memberExpression(t2.identifier(stylexNamespaceName), t2.identifier("create")), [
1173
1189
  t2.objectExpression(createProperties)
@@ -1218,32 +1234,46 @@ function isValidIdentifier(s) {
1218
1234
 
1219
1235
  // src/plugin/rewrite-sites.ts
1220
1236
  import _traverse from "@babel/traverse";
1237
+ import _generate from "@babel/generator";
1221
1238
  import * as t3 from "@babel/types";
1239
+ var generate = _generate.default ?? _generate;
1222
1240
  var traverse = _traverse.default ?? _traverse;
1223
1241
  function rewriteExpressionSites(options) {
1224
1242
  for (const site of options.sites) {
1225
1243
  const propsArgs = buildPropsArgsFromChain(site.resolvedChain, options);
1226
1244
  const cssAttrPath = getCssAttributePath(site.path);
1227
1245
  if (cssAttrPath) {
1228
- const propsCall = t3.callExpression(
1229
- t3.memberExpression(t3.identifier(options.stylexNamespaceName), t3.identifier("props")),
1230
- propsArgs
1231
- );
1232
1246
  cssAttrPath.replaceWith(
1233
1247
  t3.jsxSpreadAttribute(
1234
- buildCssSpreadExpression(cssAttrPath, propsCall, options.mergePropsHelperName, options.needsMergePropsHelper)
1248
+ buildCssSpreadExpression(
1249
+ cssAttrPath,
1250
+ propsArgs,
1251
+ site.path.node.loc?.start.line ?? null,
1252
+ options.mergePropsHelperName,
1253
+ options.needsMergePropsHelper,
1254
+ options
1255
+ )
1235
1256
  )
1236
1257
  );
1237
1258
  continue;
1238
1259
  }
1239
- site.path.replaceWith(t3.arrayExpression(propsArgs));
1260
+ site.path.replaceWith(buildStyleArrayExpression(propsArgs, site.path.node.loc?.start.line ?? null, options));
1240
1261
  }
1241
1262
  rewriteStyleObjectExpressions(options.ast);
1242
1263
  rewriteCssAttributeExpressions(
1243
1264
  options.ast,
1265
+ options.filename,
1266
+ options.debug,
1244
1267
  options.stylexNamespaceName,
1245
1268
  options.mergePropsHelperName,
1246
- options.needsMergePropsHelper
1269
+ options.needsMergePropsHelper,
1270
+ options.trussPropsHelperName,
1271
+ options.needsTrussPropsHelper,
1272
+ options.trussDebugInfoName,
1273
+ options.needsTrussDebugInfo,
1274
+ options.asStyleArrayHelperName,
1275
+ options.needsAsStyleArrayHelper,
1276
+ options.skippedCssPropMessages
1247
1277
  );
1248
1278
  }
1249
1279
  function getCssAttributePath(path) {
@@ -1304,6 +1334,18 @@ function buildPropsArgs(segments, options) {
1304
1334
  args.push(t3.spreadElement(t3.logicalExpression("??", lookupAccess, t3.arrayExpression([]))));
1305
1335
  continue;
1306
1336
  }
1337
+ if (seg.styleArrayArg) {
1338
+ args.push(
1339
+ t3.spreadElement(
1340
+ buildUnknownObjectSpreadFallback(
1341
+ seg.styleArrayArg,
1342
+ options.asStyleArrayHelperName,
1343
+ options.needsAsStyleArrayHelper
1344
+ )
1345
+ )
1346
+ );
1347
+ continue;
1348
+ }
1307
1349
  const ref = t3.memberExpression(t3.identifier(options.createVarName), t3.identifier(seg.key));
1308
1350
  if (seg.dynamicProps && seg.argNode) {
1309
1351
  let argExpr;
@@ -1311,6 +1353,12 @@ function buildPropsArgs(segments, options) {
1311
1353
  argExpr = t3.callExpression(t3.identifier(options.maybeIncHelperName), [seg.argNode]);
1312
1354
  } else if (seg.incremented) {
1313
1355
  argExpr = seg.argNode;
1356
+ } else if (seg.appendPx) {
1357
+ argExpr = t3.binaryExpression(
1358
+ "+",
1359
+ t3.callExpression(t3.identifier("String"), [seg.argNode]),
1360
+ t3.stringLiteral("px")
1361
+ );
1314
1362
  } else {
1315
1363
  argExpr = t3.callExpression(t3.identifier("String"), [seg.argNode]);
1316
1364
  }
@@ -1321,30 +1369,118 @@ function buildPropsArgs(segments, options) {
1321
1369
  }
1322
1370
  return args;
1323
1371
  }
1324
- function rewriteCssAttributeExpressions(ast, stylexNamespaceName, mergePropsHelperName, needsMergePropsHelper) {
1372
+ function rewriteCssAttributeExpressions(ast, filename, debug, stylexNamespaceName, mergePropsHelperName, needsMergePropsHelper, trussPropsHelperName, needsTrussPropsHelper, trussDebugInfoName, needsTrussDebugInfo, asStyleArrayHelperName, needsAsStyleArrayHelper, skippedCssPropMessages) {
1325
1373
  traverse(ast, {
1326
1374
  JSXAttribute(path) {
1327
1375
  if (!t3.isJSXIdentifier(path.node.name, { name: "css" })) return;
1328
1376
  const value = path.node.value;
1329
1377
  if (!t3.isJSXExpressionContainer(value)) return;
1330
1378
  if (!t3.isExpression(value.expression)) return;
1331
- const propsArgs = buildStyleObjectPropsArgs(value.expression, path) ?? buildStyleArrayLikePropsArgsFromExpression(value.expression, path);
1332
- if (!propsArgs) return;
1333
- const propsCall = t3.callExpression(
1334
- t3.memberExpression(t3.identifier(stylexNamespaceName), t3.identifier("props")),
1335
- propsArgs
1379
+ if (!isCssRewriteableExpression(value.expression, path)) {
1380
+ skippedCssPropMessages.push({
1381
+ message: explainSkippedCssRewrite(value.expression, path),
1382
+ line: path.node.loc?.start.line ?? null
1383
+ });
1384
+ return;
1385
+ }
1386
+ const propsArgs = lowerCssExpressionToPropsArgs(
1387
+ value.expression,
1388
+ path,
1389
+ asStyleArrayHelperName,
1390
+ needsAsStyleArrayHelper
1336
1391
  );
1392
+ if (!propsArgs) {
1393
+ skippedCssPropMessages.push({
1394
+ message: explainSkippedCssRewrite(value.expression, path),
1395
+ line: path.node.loc?.start.line ?? null
1396
+ });
1397
+ return;
1398
+ }
1337
1399
  path.replaceWith(
1338
- t3.jsxSpreadAttribute(buildCssSpreadExpression(path, propsCall, mergePropsHelperName, needsMergePropsHelper))
1400
+ t3.jsxSpreadAttribute(
1401
+ buildCssSpreadExpression(
1402
+ path,
1403
+ propsArgs,
1404
+ path.node.loc?.start.line ?? null,
1405
+ mergePropsHelperName,
1406
+ needsMergePropsHelper,
1407
+ {
1408
+ filename,
1409
+ debug,
1410
+ stylexNamespaceName,
1411
+ trussPropsHelperName,
1412
+ needsTrussPropsHelper,
1413
+ trussDebugInfoName,
1414
+ needsTrussDebugInfo
1415
+ }
1416
+ )
1417
+ )
1339
1418
  );
1340
1419
  }
1341
1420
  });
1342
1421
  }
1343
- function buildCssSpreadExpression(path, propsCall, mergePropsHelperName, needsMergePropsHelper) {
1422
+ function buildStyleArrayExpression(propsArgs, line, options) {
1423
+ const elements = buildDebugElements(line, options);
1424
+ elements.push(...propsArgs);
1425
+ return t3.arrayExpression(elements);
1426
+ }
1427
+ function buildPropsCall(propsArgs, line, options) {
1428
+ if (!options.debug) {
1429
+ return t3.callExpression(
1430
+ t3.memberExpression(t3.identifier(options.stylexNamespaceName), t3.identifier("props")),
1431
+ propsArgs
1432
+ );
1433
+ }
1434
+ options.needsTrussPropsHelper.current = true;
1435
+ const args = buildDebugElements(line, options);
1436
+ args.push(...propsArgs);
1437
+ return t3.callExpression(t3.identifier(options.trussPropsHelperName), [
1438
+ t3.identifier(options.stylexNamespaceName),
1439
+ ...args
1440
+ ]);
1441
+ }
1442
+ function buildDebugElements(line, options) {
1443
+ if (!options.debug || line === null) {
1444
+ return [];
1445
+ }
1446
+ options.needsTrussDebugInfo.current = true;
1447
+ return [t3.newExpression(t3.identifier(options.trussDebugInfoName), [t3.stringLiteral(`${options.filename}:${line}`)])];
1448
+ }
1449
+ function isCssRewriteableExpression(expr, path) {
1450
+ return !!lowerCssExpressionToPropsArgs(expr, path, "asStyleArray", { current: false });
1451
+ }
1452
+ function lowerCssExpressionToPropsArgs(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1453
+ return buildStyleObjectPropsArgs(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) ?? buildStyleArrayLikePropsArgsFromExpression(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) ?? buildUnknownCssValuePropsArgs(expr, asStyleArrayHelperName, needsAsStyleArrayHelper);
1454
+ }
1455
+ function explainSkippedCssRewrite(expr, path) {
1456
+ if (t3.isObjectExpression(expr)) {
1457
+ for (const prop of expr.properties) {
1458
+ if (!t3.isSpreadElement(prop)) {
1459
+ return `[truss] Unsupported pattern: Could not rewrite css prop: object contains a non-spread property (${formatNodeSnippet(expr)})`;
1460
+ }
1461
+ const normalizedArg = normalizeStyleArrayLikeExpression(prop.argument, path, /* @__PURE__ */ new Set());
1462
+ if (!normalizedArg) {
1463
+ return `[truss] Unsupported pattern: Could not rewrite css prop: spread argument is not style-array-like (${formatNodeSnippet(prop.argument)})`;
1464
+ }
1465
+ }
1466
+ return `[truss] Unsupported pattern: Could not rewrite css prop: object spread composition was not recognized (${formatNodeSnippet(expr)})`;
1467
+ }
1468
+ return `[truss] Unsupported pattern: Could not rewrite css prop: expression is not style-array-like (${formatNodeSnippet(expr)})`;
1469
+ }
1470
+ function formatNodeSnippet(node) {
1471
+ return generate(node, { compact: true, comments: true }).code;
1472
+ }
1473
+ function buildCssSpreadExpression(path, propsArgs, line, mergePropsHelperName, needsMergePropsHelper, options) {
1344
1474
  const existingClassNameExpr = removeExistingClassNameAttribute(path);
1345
- if (!existingClassNameExpr) return propsCall;
1475
+ if (!existingClassNameExpr) return buildPropsCall(propsArgs, line, options);
1346
1476
  needsMergePropsHelper.current = true;
1347
- return t3.callExpression(t3.identifier(mergePropsHelperName), [existingClassNameExpr, ...propsCall.arguments]);
1477
+ const args = buildDebugElements(line, options);
1478
+ args.push(...propsArgs);
1479
+ return t3.callExpression(t3.identifier(mergePropsHelperName), [
1480
+ t3.identifier(options.stylexNamespaceName),
1481
+ existingClassNameExpr,
1482
+ ...args
1483
+ ]);
1348
1484
  }
1349
1485
  function removeExistingClassNameAttribute(path) {
1350
1486
  const openingElement = path.parentPath;
@@ -1364,30 +1500,30 @@ function removeExistingClassNameAttribute(path) {
1364
1500
  }
1365
1501
  return null;
1366
1502
  }
1367
- function buildStyleObjectPropsArgs(expr, path) {
1503
+ function buildStyleObjectPropsArgs(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1368
1504
  if (!t3.isObjectExpression(expr) || expr.properties.length === 0) return null;
1369
- let sawStyleArray = false;
1370
1505
  const propsArgs = [];
1371
1506
  for (const prop of expr.properties) {
1372
1507
  if (!t3.isSpreadElement(prop)) return null;
1373
1508
  const normalizedArg = normalizeStyleArrayLikeExpression(prop.argument, path, /* @__PURE__ */ new Set());
1374
1509
  if (!normalizedArg) {
1375
- propsArgs.push(t3.spreadElement(prop.argument));
1510
+ propsArgs.push(
1511
+ t3.spreadElement(
1512
+ buildUnknownObjectSpreadFallback(prop.argument, asStyleArrayHelperName, needsAsStyleArrayHelper)
1513
+ )
1514
+ );
1376
1515
  continue;
1377
1516
  }
1378
- if (isStyleArrayLike(normalizedArg, path, /* @__PURE__ */ new Set())) {
1379
- sawStyleArray = true;
1380
- }
1381
1517
  const nestedArgs = buildStyleArrayLikePropsArgs(normalizedArg, path, /* @__PURE__ */ new Set());
1382
1518
  if (nestedArgs && t3.isArrayExpression(normalizedArg)) {
1383
1519
  propsArgs.push(...nestedArgs);
1384
1520
  } else {
1385
- propsArgs.push(t3.spreadElement(normalizedArg));
1521
+ propsArgs.push(t3.spreadElement(buildSafeSpreadArgument(normalizedArg)));
1386
1522
  }
1387
1523
  }
1388
- return sawStyleArray ? propsArgs : null;
1524
+ return propsArgs.length > 0 ? propsArgs : null;
1389
1525
  }
1390
- function buildStyleArrayLikePropsArgsFromExpression(expr, path) {
1526
+ function buildStyleArrayLikePropsArgsFromExpression(expr, path, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1391
1527
  const normalizedExpr = normalizeStyleArrayLikeExpression(expr, path, /* @__PURE__ */ new Set());
1392
1528
  if (!normalizedExpr) return null;
1393
1529
  return buildStyleArrayLikePropsArgs(normalizedExpr, path, /* @__PURE__ */ new Set());
@@ -1409,7 +1545,7 @@ function buildStyleArrayLikePropsArgs(expr, path, seen) {
1409
1545
  if (nestedArgs && t3.isArrayExpression(normalizedArg)) {
1410
1546
  propsArgs.push(...nestedArgs);
1411
1547
  } else {
1412
- propsArgs.push(t3.spreadElement(normalizedArg));
1548
+ propsArgs.push(t3.spreadElement(buildSafeSpreadArgument(normalizedArg)));
1413
1549
  }
1414
1550
  continue;
1415
1551
  }
@@ -1417,11 +1553,15 @@ function buildStyleArrayLikePropsArgs(expr, path, seen) {
1417
1553
  }
1418
1554
  return propsArgs;
1419
1555
  }
1420
- if (t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isConditionalExpression(expr)) {
1421
- return [t3.spreadElement(expr)];
1556
+ if (t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isConditionalExpression(expr) || t3.isLogicalExpression(expr) || t3.isCallExpression(expr)) {
1557
+ return [t3.spreadElement(buildSafeSpreadArgument(expr))];
1422
1558
  }
1423
1559
  return null;
1424
1560
  }
1561
+ function buildUnknownCssValuePropsArgs(expr, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1562
+ if (!(t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isCallExpression(expr))) return null;
1563
+ return [t3.spreadElement(buildUnknownObjectSpreadFallback(expr, asStyleArrayHelperName, needsAsStyleArrayHelper))];
1564
+ }
1425
1565
  function rewriteStyleObjectExpressions(ast) {
1426
1566
  traverse(ast, {
1427
1567
  ObjectExpression(path) {
@@ -1446,16 +1586,17 @@ function tryBuildStyleArrayFromObject(path) {
1446
1586
  // I.e. `...Css.df.$`, `...(cond ? Css.df.$ : {})`, or `...styles.wrapper`
1447
1587
  );
1448
1588
  if (!normalizedArg) {
1449
- return null;
1589
+ elements.push(t3.spreadElement(buildInlineAsStyleArrayExpression(prop.argument)));
1590
+ continue;
1450
1591
  }
1451
- if (isStyleArrayLike(normalizedArg, path, /* @__PURE__ */ new Set())) {
1592
+ if (isKnownStyleArrayLike(normalizedArg, path, /* @__PURE__ */ new Set())) {
1452
1593
  sawStyleArray = true;
1453
1594
  }
1454
1595
  if (t3.isArrayExpression(normalizedArg)) {
1455
1596
  elements.push(...normalizedArg.elements);
1456
1597
  continue;
1457
1598
  }
1458
- elements.push(t3.spreadElement(normalizedArg));
1599
+ elements.push(t3.spreadElement(buildSafeSpreadArgument(normalizedArg)));
1459
1600
  }
1460
1601
  if (!sawStyleArray) return null;
1461
1602
  return t3.arrayExpression(elements);
@@ -1464,13 +1605,24 @@ function normalizeStyleArrayLikeExpression(expr, path, seen) {
1464
1605
  if (seen.has(expr)) return null;
1465
1606
  seen.add(expr);
1466
1607
  if (t3.isArrayExpression(expr)) return expr;
1608
+ if (t3.isLogicalExpression(expr) && expr.operator === "&&") {
1609
+ const consequent = normalizeStyleArrayLikeExpression(expr.right, path, seen);
1610
+ if (!consequent) return null;
1611
+ return t3.conditionalExpression(expr.left, consequent, t3.arrayExpression([]));
1612
+ }
1613
+ if (t3.isLogicalExpression(expr) && (expr.operator === "||" || expr.operator === "??")) {
1614
+ const left = normalizeStyleArrayLikeExpression(expr.left, path, seen);
1615
+ const right = normalizeStyleArrayLikeBranch(expr.right, path, seen);
1616
+ if (!left || !right) return null;
1617
+ return t3.logicalExpression(expr.operator, left, right);
1618
+ }
1467
1619
  if (t3.isConditionalExpression(expr)) {
1468
1620
  const consequent = normalizeStyleArrayLikeBranch(expr.consequent, path, seen);
1469
1621
  const alternate = normalizeStyleArrayLikeBranch(expr.alternate, path, seen);
1470
1622
  if (!consequent || !alternate) return null;
1471
1623
  return t3.conditionalExpression(expr.test, consequent, alternate);
1472
1624
  }
1473
- if (t3.isIdentifier(expr) || t3.isMemberExpression(expr)) {
1625
+ if (t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isCallExpression(expr)) {
1474
1626
  const nestedSeen = new Set(seen);
1475
1627
  nestedSeen.delete(expr);
1476
1628
  if (isStyleArrayLike(expr, path, nestedSeen)) return expr;
@@ -1487,6 +1639,12 @@ function isStyleArrayLike(expr, path, seen) {
1487
1639
  if (seen.has(expr)) return false;
1488
1640
  seen.add(expr);
1489
1641
  if (t3.isArrayExpression(expr)) return true;
1642
+ if (t3.isLogicalExpression(expr) && expr.operator === "&&") {
1643
+ return isStyleArrayLike(expr.right, path, seen);
1644
+ }
1645
+ if (t3.isLogicalExpression(expr) && (expr.operator === "||" || expr.operator === "??")) {
1646
+ return isStyleArrayLike(expr.left, path, seen) && isStyleArrayLikeBranch(expr.right, path, seen);
1647
+ }
1490
1648
  if (t3.isConditionalExpression(expr)) {
1491
1649
  return isStyleArrayLikeBranch(expr.consequent, path, seen) && isStyleArrayLikeBranch(expr.alternate, path, seen);
1492
1650
  }
@@ -1497,7 +1655,11 @@ function isStyleArrayLike(expr, path, seen) {
1497
1655
  const init = bindingPath.node.init;
1498
1656
  return !!(init && isStyleArrayLike(init, bindingPath, seen));
1499
1657
  }
1500
- if (t3.isMemberExpression(expr) && !expr.computed && t3.isIdentifier(expr.property)) {
1658
+ if (t3.isCallExpression(expr)) {
1659
+ const returnExpr = getCallStyleArrayLikeExpression(expr, path);
1660
+ return returnExpr ? isStyleArrayLike(returnExpr, path, seen) : true;
1661
+ }
1662
+ if (t3.isMemberExpression(expr)) {
1501
1663
  const object = expr.object;
1502
1664
  if (!t3.isIdentifier(object)) return false;
1503
1665
  const binding = path.scope.getBinding(object.name);
@@ -1505,7 +1667,8 @@ function isStyleArrayLike(expr, path, seen) {
1505
1667
  if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1506
1668
  const init = bindingPath.node.init;
1507
1669
  if (!init || !t3.isObjectExpression(init)) return false;
1508
- const propertyName = expr.property.name;
1670
+ const propertyName = getStaticMemberPropertyName(expr, path);
1671
+ if (!propertyName) return false;
1509
1672
  for (const prop of init.properties) {
1510
1673
  if (!t3.isObjectProperty(prop) || prop.computed) continue;
1511
1674
  if (!isMatchingPropertyName(prop.key, propertyName)) continue;
@@ -1515,6 +1678,49 @@ function isStyleArrayLike(expr, path, seen) {
1515
1678
  }
1516
1679
  return false;
1517
1680
  }
1681
+ function isKnownStyleArrayLike(expr, path, seen) {
1682
+ if (seen.has(expr)) return false;
1683
+ seen.add(expr);
1684
+ if (t3.isArrayExpression(expr)) return true;
1685
+ if (t3.isLogicalExpression(expr) && expr.operator === "&&") {
1686
+ return isKnownStyleArrayLike(expr.right, path, seen);
1687
+ }
1688
+ if (t3.isLogicalExpression(expr) && (expr.operator === "||" || expr.operator === "??")) {
1689
+ return isKnownStyleArrayLike(expr.left, path, seen) && isStyleArrayLikeBranch(expr.right, path, seen);
1690
+ }
1691
+ if (t3.isConditionalExpression(expr)) {
1692
+ return isStyleArrayLikeBranch(expr.consequent, path, seen) && isStyleArrayLikeBranch(expr.alternate, path, seen);
1693
+ }
1694
+ if (t3.isIdentifier(expr)) {
1695
+ const binding = path.scope.getBinding(expr.name);
1696
+ const bindingPath = binding?.path;
1697
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1698
+ const init = bindingPath.node.init;
1699
+ return !!(init && isKnownStyleArrayLike(init, bindingPath, seen));
1700
+ }
1701
+ if (t3.isCallExpression(expr)) {
1702
+ const returnExpr = getCallStyleArrayLikeExpression(expr, path);
1703
+ return !!(returnExpr && isKnownStyleArrayLike(returnExpr, path, seen));
1704
+ }
1705
+ if (t3.isMemberExpression(expr)) {
1706
+ const object = expr.object;
1707
+ if (!t3.isIdentifier(object)) return false;
1708
+ const binding = path.scope.getBinding(object.name);
1709
+ const bindingPath = binding?.path;
1710
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1711
+ const init = bindingPath.node.init;
1712
+ if (!init || !t3.isObjectExpression(init)) return false;
1713
+ const propertyName = getStaticMemberPropertyName(expr, path);
1714
+ if (!propertyName) return false;
1715
+ for (const prop of init.properties) {
1716
+ if (!t3.isObjectProperty(prop) || prop.computed) continue;
1717
+ if (!isMatchingPropertyName(prop.key, propertyName)) continue;
1718
+ const value = prop.value;
1719
+ return t3.isExpression(value) && isKnownStyleArrayLike(value, bindingPath, seen);
1720
+ }
1721
+ }
1722
+ return false;
1723
+ }
1518
1724
  function isStyleArrayLikeBranch(expr, path, seen) {
1519
1725
  return isEmptyObjectExpression(expr) || isStyleArrayLike(expr, path, seen);
1520
1726
  }
@@ -1524,11 +1730,74 @@ function isMatchingPropertyName(key, name) {
1524
1730
  function isEmptyObjectExpression(expr) {
1525
1731
  return t3.isObjectExpression(expr) && expr.properties.length === 0;
1526
1732
  }
1733
+ function buildUnknownObjectSpreadFallback(expr, asStyleArrayHelperName, needsAsStyleArrayHelper) {
1734
+ needsAsStyleArrayHelper.current = true;
1735
+ return t3.callExpression(t3.identifier(asStyleArrayHelperName), [expr]);
1736
+ }
1737
+ function buildInlineAsStyleArrayExpression(expr) {
1738
+ return t3.conditionalExpression(
1739
+ t3.callExpression(t3.memberExpression(t3.identifier("Array"), t3.identifier("isArray")), [expr]),
1740
+ expr,
1741
+ t3.conditionalExpression(expr, t3.arrayExpression([expr]), t3.arrayExpression([]))
1742
+ );
1743
+ }
1744
+ function buildSafeSpreadArgument(expr) {
1745
+ return t3.isConditionalExpression(expr) || t3.isLogicalExpression(expr) ? t3.parenthesizedExpression(expr) : expr;
1746
+ }
1747
+ function getStaticMemberPropertyName(expr, path) {
1748
+ if (!expr.computed && t3.isIdentifier(expr.property)) {
1749
+ return expr.property.name;
1750
+ }
1751
+ if (t3.isStringLiteral(expr.property)) {
1752
+ return expr.property.value;
1753
+ }
1754
+ if (t3.isIdentifier(expr.property)) {
1755
+ const binding = path.scope.getBinding(expr.property.name);
1756
+ const bindingPath = binding?.path;
1757
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return null;
1758
+ const init = bindingPath.node.init;
1759
+ return t3.isStringLiteral(init) ? init.value : null;
1760
+ }
1761
+ return null;
1762
+ }
1763
+ function getCallStyleArrayLikeExpression(expr, path) {
1764
+ const localReturnExpr = getLocalFunctionReturnExpression(expr, path);
1765
+ if (localReturnExpr) return localReturnExpr;
1766
+ const firstArg = expr.arguments[0];
1767
+ if (firstArg && !t3.isSpreadElement(firstArg) && (t3.isArrowFunctionExpression(firstArg) || t3.isFunctionExpression(firstArg))) {
1768
+ return getFunctionLikeReturnExpression(firstArg);
1769
+ }
1770
+ return null;
1771
+ }
1772
+ function getLocalFunctionReturnExpression(expr, path) {
1773
+ if (!t3.isIdentifier(expr.callee)) return null;
1774
+ const binding = path.scope.getBinding(expr.callee.name);
1775
+ const bindingPath = binding?.path;
1776
+ if (!bindingPath) return null;
1777
+ if (bindingPath.isFunctionDeclaration()) {
1778
+ return getFunctionLikeReturnExpression(bindingPath.node);
1779
+ }
1780
+ if (bindingPath.isVariableDeclarator()) {
1781
+ const init = bindingPath.node.init;
1782
+ if (init && (t3.isArrowFunctionExpression(init) || t3.isFunctionExpression(init))) {
1783
+ return getFunctionLikeReturnExpression(init);
1784
+ }
1785
+ }
1786
+ return null;
1787
+ }
1788
+ function getFunctionLikeReturnExpression(fn) {
1789
+ if (t3.isExpression(fn.body)) {
1790
+ return fn.body;
1791
+ }
1792
+ if (fn.body.body.length !== 1) return null;
1793
+ const stmt = fn.body.body[0];
1794
+ return t3.isReturnStatement(stmt) && stmt.argument && t3.isExpression(stmt.argument) ? stmt.argument : null;
1795
+ }
1527
1796
 
1528
1797
  // src/plugin/transform.ts
1529
1798
  var traverse2 = _traverse2.default ?? _traverse2;
1530
- var generate = _generate.default ?? _generate;
1531
- function transformTruss(code, filename, mapping) {
1799
+ var generate2 = _generate2.default ?? _generate2;
1800
+ function transformTruss(code, filename, mapping, options = {}) {
1532
1801
  if (!code.includes("Css")) return null;
1533
1802
  const ast = parse(code, {
1534
1803
  sourceType: "module",
@@ -1564,8 +1833,18 @@ function transformTruss(code, filename, mapping) {
1564
1833
  const stylexNamespaceName = existingStylexNamespace ?? reservePreferredName(usedTopLevelNames, "stylex");
1565
1834
  const createVarName = reservePreferredName(usedTopLevelNames, "css", "css_");
1566
1835
  const maybeIncHelperName = needsMaybeInc ? reservePreferredName(usedTopLevelNames, "__maybeInc") : null;
1567
- const mergePropsHelperName = reservePreferredName(usedTopLevelNames, "__mergeProps");
1836
+ const existingMergePropsHelperName = findNamedImportBinding(ast, "@homebound/truss/runtime", "mergeProps");
1837
+ const mergePropsHelperName = existingMergePropsHelperName ?? reservePreferredName(usedTopLevelNames, "mergeProps");
1568
1838
  const needsMergePropsHelper = { current: false };
1839
+ const existingAsStyleArrayHelperName = findNamedImportBinding(ast, "@homebound/truss/runtime", "asStyleArray");
1840
+ const asStyleArrayHelperName = existingAsStyleArrayHelperName ?? reservePreferredName(usedTopLevelNames, "asStyleArray");
1841
+ const needsAsStyleArrayHelper = { current: false };
1842
+ const existingTrussPropsHelperName = findNamedImportBinding(ast, "@homebound/truss/runtime", "trussProps");
1843
+ const trussPropsHelperName = existingTrussPropsHelperName ?? reservePreferredName(usedTopLevelNames, "trussProps");
1844
+ const needsTrussPropsHelper = { current: false };
1845
+ const existingTrussDebugInfoName = findNamedImportBinding(ast, "@homebound/truss/runtime", "TrussDebugInfo");
1846
+ const trussDebugInfoName = existingTrussDebugInfoName ?? reservePreferredName(usedTopLevelNames, "TrussDebugInfo");
1847
+ const needsTrussDebugInfo = { current: false };
1569
1848
  const runtimeLookupNames = /* @__PURE__ */ new Map();
1570
1849
  for (const [lookupKey] of runtimeLookups) {
1571
1850
  runtimeLookupNames.set(lookupKey, reservePreferredName(usedTopLevelNames, `__${lookupKey}`));
@@ -1574,26 +1853,48 @@ function transformTruss(code, filename, mapping) {
1574
1853
  rewriteExpressionSites({
1575
1854
  ast,
1576
1855
  sites,
1856
+ filename: basename(filename),
1857
+ debug: options.debug ?? false,
1577
1858
  createVarName,
1578
1859
  stylexNamespaceName,
1579
1860
  maybeIncHelperName,
1580
1861
  mergePropsHelperName,
1581
1862
  needsMergePropsHelper,
1863
+ trussPropsHelperName,
1864
+ needsTrussPropsHelper,
1865
+ trussDebugInfoName,
1866
+ needsTrussDebugInfo,
1867
+ asStyleArrayHelperName,
1868
+ needsAsStyleArrayHelper,
1869
+ skippedCssPropMessages: errorMessages,
1582
1870
  runtimeLookupNames
1583
1871
  });
1584
1872
  removeCssImport(ast, cssBindingName);
1585
1873
  if (!findStylexNamespaceImport(ast)) {
1586
1874
  insertStylexNamespaceImport(ast, stylexNamespaceName);
1587
1875
  }
1876
+ if (needsMergePropsHelper.current || needsAsStyleArrayHelper.current || needsTrussPropsHelper.current || needsTrussDebugInfo.current) {
1877
+ const runtimeImports = [];
1878
+ if (needsMergePropsHelper.current) {
1879
+ runtimeImports.push({ importedName: "mergeProps", localName: mergePropsHelperName });
1880
+ }
1881
+ if (needsAsStyleArrayHelper.current) {
1882
+ runtimeImports.push({ importedName: "asStyleArray", localName: asStyleArrayHelperName });
1883
+ }
1884
+ if (needsTrussPropsHelper.current) {
1885
+ runtimeImports.push({ importedName: "trussProps", localName: trussPropsHelperName });
1886
+ }
1887
+ if (needsTrussDebugInfo.current) {
1888
+ runtimeImports.push({ importedName: "TrussDebugInfo", localName: trussDebugInfoName });
1889
+ }
1890
+ upsertNamedImports(ast, "@homebound/truss/runtime", runtimeImports);
1891
+ }
1588
1892
  const markerVarNames = collectReferencedMarkerNames(createEntries);
1589
1893
  const hoistedMarkerDecls = hoistMarkerDeclarations(ast, markerVarNames);
1590
1894
  const declarationsToInsert = [];
1591
1895
  if (maybeIncHelperName) {
1592
1896
  declarationsToInsert.push(buildMaybeIncDeclaration(maybeIncHelperName, mapping.increment));
1593
1897
  }
1594
- if (needsMergePropsHelper.current) {
1595
- declarationsToInsert.push(buildMergePropsDeclaration(mergePropsHelperName, stylexNamespaceName));
1596
- }
1597
1898
  declarationsToInsert.push(...hoistedMarkerDecls);
1598
1899
  if (createProperties.length > 0) {
1599
1900
  declarationsToInsert.push(buildCreateDeclaration(createVarName, stylexNamespaceName, createProperties));
@@ -1614,10 +1915,12 @@ function transformTruss(code, filename, mapping) {
1614
1915
  declarationsToInsert.push(consoleError);
1615
1916
  }
1616
1917
  if (declarationsToInsert.length > 0) {
1617
- const insertIndex = findLastImportIndex(ast) + 1;
1618
- ast.program.body.splice(insertIndex, 0, ...declarationsToInsert);
1918
+ const insertIndex = ast.program.body.findIndex(function(node) {
1919
+ return !t4.isImportDeclaration(node);
1920
+ });
1921
+ ast.program.body.splice(insertIndex === -1 ? ast.program.body.length : insertIndex, 0, ...declarationsToInsert);
1619
1922
  }
1620
- const output = generate(ast, {
1923
+ const output = generate2(ast, {
1621
1924
  sourceFileName: filename,
1622
1925
  retainLines: false
1623
1926
  });
@@ -1835,6 +2138,9 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
1835
2138
  if (seg.typographyLookup) {
1836
2139
  return { error: `typography() with a runtime key is not supported in .css.ts files` };
1837
2140
  }
2141
+ if (seg.styleArrayArg) {
2142
+ return { error: `add(cssProp) is not supported in .css.ts files` };
2143
+ }
1838
2144
  if (seg.mediaQuery) {
1839
2145
  return { error: `media query modifiers (ifSm, ifMd, etc.) are not supported in .css.ts files` };
1840
2146
  }
@@ -1873,9 +2179,9 @@ ${body}
1873
2179
 
1874
2180
  // src/plugin/rewrite-css-ts-imports.ts
1875
2181
  import { parse as parse3 } from "@babel/parser";
1876
- import _generate2 from "@babel/generator";
2182
+ import _generate3 from "@babel/generator";
1877
2183
  import * as t7 from "@babel/types";
1878
- var generate2 = _generate2.default ?? _generate2;
2184
+ var generate3 = _generate3.default ?? _generate3;
1879
2185
  function rewriteCssTsImports(code, filename) {
1880
2186
  if (!code.includes(".css.ts")) {
1881
2187
  return { code, changed: false };
@@ -1913,7 +2219,7 @@ function rewriteCssTsImports(code, filename) {
1913
2219
  const insertIndex = findLastImportIndex(ast) + 1;
1914
2220
  ast.program.body.splice(insertIndex, 0, ...sideEffectImports);
1915
2221
  }
1916
- const output = generate2(ast, {
2222
+ const output = generate3(ast, {
1917
2223
  sourceFileName: filename,
1918
2224
  retainLines: false
1919
2225
  });
@@ -1929,6 +2235,7 @@ var CSS_TS_QUERY = "?truss-css";
1929
2235
  function trussPlugin(opts) {
1930
2236
  let mapping = null;
1931
2237
  let projectRoot;
2238
+ let debug = false;
1932
2239
  const externalPackages = opts.externalPackages ?? [];
1933
2240
  function mappingPath() {
1934
2241
  return resolve(projectRoot || process.cwd(), opts.mapping);
@@ -1944,6 +2251,7 @@ function trussPlugin(opts) {
1944
2251
  enforce: "pre",
1945
2252
  configResolved(config) {
1946
2253
  projectRoot = config.root;
2254
+ debug = config.command === "serve" || config.mode === "development" || config.mode === "test";
1947
2255
  },
1948
2256
  buildStart() {
1949
2257
  ensureMapping();
@@ -1976,7 +2284,7 @@ function trussPlugin(opts) {
1976
2284
  if (!hasCssDsl) {
1977
2285
  return { code: rewrittenCode, map: null };
1978
2286
  }
1979
- const result = transformTruss(rewrittenCode, id, ensureMapping());
2287
+ const result = transformTruss(rewrittenCode, fileId, ensureMapping(), { debug });
1980
2288
  if (!result) {
1981
2289
  if (!rewrittenImports.changed) return null;
1982
2290
  return { code: rewrittenCode, map: null };