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

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.
@@ -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 = {};
@@ -869,12 +926,17 @@ function extractChain(node, cssBinding) {
869
926
  import * as t2 from "@babel/types";
870
927
  function collectCreateData(chains) {
871
928
  const createEntries = /* @__PURE__ */ new Map();
929
+ const runtimeLookups = /* @__PURE__ */ new Map();
872
930
  let needsMaybeInc = false;
873
931
  for (const chain of chains) {
874
932
  for (const part of chain.parts) {
875
933
  const segs = part.type === "unconditional" ? part.segments : [...part.thenSegments, ...part.elseSegments];
876
934
  for (const seg of segs) {
877
935
  if (seg.error) continue;
936
+ if (seg.typographyLookup) {
937
+ collectTypographyLookup(createEntries, runtimeLookups, seg);
938
+ continue;
939
+ }
878
940
  if (seg.dynamicProps) {
879
941
  if (!createEntries.has(seg.key)) {
880
942
  createEntries.set(seg.key, {
@@ -903,7 +965,36 @@ function collectCreateData(chains) {
903
965
  }
904
966
  }
905
967
  }
906
- return { createEntries, needsMaybeInc };
968
+ return { createEntries, runtimeLookups, needsMaybeInc };
969
+ }
970
+ function collectTypographyLookup(createEntries, runtimeLookups, seg) {
971
+ const lookup = seg.typographyLookup;
972
+ if (!lookup) return;
973
+ if (!runtimeLookups.has(lookup.lookupKey)) {
974
+ runtimeLookups.set(lookup.lookupKey, {
975
+ lookupKey: lookup.lookupKey,
976
+ refsByName: Object.fromEntries(
977
+ Object.entries(lookup.segmentsByName).map(function([name, segments]) {
978
+ return [
979
+ name,
980
+ segments.map(function(segment) {
981
+ return segment.key;
982
+ })
983
+ ];
984
+ })
985
+ )
986
+ });
987
+ }
988
+ for (const segments of Object.values(lookup.segmentsByName)) {
989
+ for (const segment of segments) {
990
+ if (createEntries.has(segment.key)) continue;
991
+ createEntries.set(segment.key, {
992
+ key: segment.key,
993
+ defs: segment.defs,
994
+ whenPseudo: segment.whenPseudo
995
+ });
996
+ }
997
+ }
907
998
  }
908
999
  function buildCreateProperties(createEntries, stylexNamespaceName) {
909
1000
  const createProperties = [];
@@ -1045,6 +1136,18 @@ function buildCreateDeclaration(createVarName, stylexNamespaceName, createProper
1045
1136
  ]);
1046
1137
  return t2.variableDeclaration("const", [t2.variableDeclarator(t2.identifier(createVarName), createCall)]);
1047
1138
  }
1139
+ function buildRuntimeLookupDeclaration(lookupName, createVarName, lookup) {
1140
+ const properties = [];
1141
+ for (const [name, refs] of Object.entries(lookup.refsByName)) {
1142
+ const values = refs.map(function(refKey) {
1143
+ return t2.memberExpression(t2.identifier(createVarName), t2.identifier(refKey));
1144
+ });
1145
+ properties.push(t2.objectProperty(toPropertyKey(name), t2.arrayExpression(values)));
1146
+ }
1147
+ return t2.variableDeclaration("const", [
1148
+ t2.variableDeclarator(t2.identifier(lookupName), t2.objectExpression(properties))
1149
+ ]);
1150
+ }
1048
1151
  function defsToAst(defs) {
1049
1152
  const properties = [];
1050
1153
  for (const [key, value] of Object.entries(defs)) {
@@ -1134,6 +1237,7 @@ function rewriteExpressionSites(options) {
1134
1237
  }
1135
1238
  site.path.replaceWith(t3.arrayExpression(propsArgs));
1136
1239
  }
1240
+ rewriteStyleObjectExpressions(options.ast);
1137
1241
  rewriteCssArrayExpressions(options.ast, options.stylexNamespaceName);
1138
1242
  }
1139
1243
  function getCssAttributePath(path) {
@@ -1165,7 +1269,7 @@ function buildPropsArgsFromChain(chain, options) {
1165
1269
  }
1166
1270
  const thenArgs = buildPropsArgs(part.thenSegments, options);
1167
1271
  const elseArgs = buildPropsArgs(part.elseSegments, options);
1168
- if (thenArgs.length === 1 && elseArgs.length === 1) {
1272
+ if (thenArgs.length === 1 && elseArgs.length === 1 && !t3.isSpreadElement(thenArgs[0]) && !t3.isSpreadElement(elseArgs[0])) {
1169
1273
  args.push(t3.conditionalExpression(part.conditionNode, thenArgs[0], elseArgs[0]));
1170
1274
  } else if (thenArgs.length > 0 || elseArgs.length > 0) {
1171
1275
  args.push(
@@ -1181,6 +1285,19 @@ function buildPropsArgs(segments, options) {
1181
1285
  const args = [];
1182
1286
  for (const seg of segments) {
1183
1287
  if (seg.error) continue;
1288
+ if (seg.typographyLookup) {
1289
+ const lookupName = options.runtimeLookupNames.get(seg.typographyLookup.lookupKey);
1290
+ if (!lookupName) {
1291
+ continue;
1292
+ }
1293
+ const lookupAccess = t3.memberExpression(
1294
+ t3.identifier(lookupName),
1295
+ seg.typographyLookup.argNode,
1296
+ true
1297
+ );
1298
+ args.push(t3.spreadElement(t3.logicalExpression("??", lookupAccess, t3.arrayExpression([]))));
1299
+ continue;
1300
+ }
1184
1301
  const ref = t3.memberExpression(t3.identifier(options.createVarName), t3.identifier(seg.key));
1185
1302
  if (seg.dynamicProps && seg.argNode) {
1186
1303
  let argExpr;
@@ -1234,6 +1351,108 @@ function rewriteCssArrayExpressions(ast, stylexNamespaceName) {
1234
1351
  }
1235
1352
  });
1236
1353
  }
1354
+ function rewriteStyleObjectExpressions(ast) {
1355
+ traverse(ast, {
1356
+ ObjectExpression(path) {
1357
+ const rewritten = tryBuildStyleArrayFromObject(path);
1358
+ if (!rewritten) return;
1359
+ path.replaceWith(rewritten);
1360
+ }
1361
+ });
1362
+ }
1363
+ function tryBuildStyleArrayFromObject(path) {
1364
+ if (path.node.properties.length === 0) return null;
1365
+ let sawStyleArray = false;
1366
+ const elements = [];
1367
+ for (const prop of path.node.properties) {
1368
+ if (!t3.isSpreadElement(prop)) {
1369
+ return null;
1370
+ }
1371
+ const normalizedArg = normalizeStyleArrayExpression(
1372
+ prop.argument,
1373
+ path,
1374
+ /* @__PURE__ */ new Set()
1375
+ // I.e. `...Css.df.$`, `...(cond ? Css.df.$ : {})`, or `...styles.wrapper`
1376
+ );
1377
+ if (!normalizedArg) {
1378
+ return null;
1379
+ }
1380
+ if (isStyleArrayLikeExpression(normalizedArg, path, /* @__PURE__ */ new Set())) {
1381
+ sawStyleArray = true;
1382
+ }
1383
+ if (t3.isArrayExpression(normalizedArg)) {
1384
+ elements.push(...normalizedArg.elements);
1385
+ continue;
1386
+ }
1387
+ elements.push(t3.spreadElement(normalizedArg));
1388
+ }
1389
+ if (!sawStyleArray) return null;
1390
+ return t3.arrayExpression(elements);
1391
+ }
1392
+ function normalizeStyleArrayExpression(expr, path, seen) {
1393
+ if (seen.has(expr)) return null;
1394
+ seen.add(expr);
1395
+ if (t3.isArrayExpression(expr)) return expr;
1396
+ if (t3.isConditionalExpression(expr)) {
1397
+ const consequent = normalizeConditionalBranch(expr.consequent, path, seen);
1398
+ const alternate = normalizeConditionalBranch(expr.alternate, path, seen);
1399
+ if (!consequent || !alternate) return null;
1400
+ return t3.conditionalExpression(expr.test, consequent, alternate);
1401
+ }
1402
+ if (t3.isIdentifier(expr) || t3.isMemberExpression(expr)) {
1403
+ const nestedSeen = new Set(seen);
1404
+ nestedSeen.delete(expr);
1405
+ if (isStyleArrayLikeExpression(expr, path, nestedSeen)) return expr;
1406
+ }
1407
+ return null;
1408
+ }
1409
+ function normalizeConditionalBranch(expr, path, seen) {
1410
+ if (isEmptyObjectExpression(expr)) {
1411
+ return t3.arrayExpression([]);
1412
+ }
1413
+ return normalizeStyleArrayExpression(expr, path, seen);
1414
+ }
1415
+ function isStyleArrayLikeExpression(expr, path, seen) {
1416
+ if (seen.has(expr)) return false;
1417
+ seen.add(expr);
1418
+ if (t3.isArrayExpression(expr)) return true;
1419
+ if (t3.isConditionalExpression(expr)) {
1420
+ return isStyleArrayLikeBranch(expr.consequent, path, seen) && isStyleArrayLikeBranch(expr.alternate, path, seen);
1421
+ }
1422
+ if (t3.isIdentifier(expr)) {
1423
+ const binding = path.scope.getBinding(expr.name);
1424
+ const bindingPath = binding?.path;
1425
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1426
+ const init = bindingPath.node.init;
1427
+ return !!(init && isStyleArrayLikeExpression(init, bindingPath, seen));
1428
+ }
1429
+ if (t3.isMemberExpression(expr) && !expr.computed && t3.isIdentifier(expr.property)) {
1430
+ const object = expr.object;
1431
+ if (!t3.isIdentifier(object)) return false;
1432
+ const binding = path.scope.getBinding(object.name);
1433
+ const bindingPath = binding?.path;
1434
+ if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
1435
+ const init = bindingPath.node.init;
1436
+ if (!init || !t3.isObjectExpression(init)) return false;
1437
+ const propertyName = expr.property.name;
1438
+ for (const prop of init.properties) {
1439
+ if (!t3.isObjectProperty(prop) || prop.computed) continue;
1440
+ if (!isMatchingPropertyName(prop.key, propertyName)) continue;
1441
+ const value = prop.value;
1442
+ return t3.isExpression(value) && isStyleArrayLikeExpression(value, bindingPath, seen);
1443
+ }
1444
+ }
1445
+ return false;
1446
+ }
1447
+ function isStyleArrayLikeBranch(expr, path, seen) {
1448
+ return isEmptyObjectExpression(expr) || isStyleArrayLikeExpression(expr, path, seen);
1449
+ }
1450
+ function isMatchingPropertyName(key, name) {
1451
+ return t3.isIdentifier(key) && key.name === name || t3.isStringLiteral(key) && key.value === name;
1452
+ }
1453
+ function isEmptyObjectExpression(expr) {
1454
+ return t3.isObjectExpression(expr) && expr.properties.length === 0;
1455
+ }
1237
1456
 
1238
1457
  // src/plugin/transform.ts
1239
1458
  var traverse2 = _traverse2.default ?? _traverse2;
@@ -1268,19 +1487,24 @@ function transformTruss(code, filename, mapping) {
1268
1487
  }
1269
1488
  });
1270
1489
  if (sites.length === 0) return null;
1271
- const { createEntries, needsMaybeInc } = collectCreateData(sites.map((s) => s.resolvedChain));
1490
+ const { createEntries, runtimeLookups, needsMaybeInc } = collectCreateData(sites.map((s) => s.resolvedChain));
1272
1491
  const usedTopLevelNames = collectTopLevelBindings(ast);
1273
1492
  const existingStylexNamespace = findStylexNamespaceImport(ast);
1274
1493
  const stylexNamespaceName = existingStylexNamespace ?? reservePreferredName(usedTopLevelNames, "stylex");
1275
1494
  const createVarName = reservePreferredName(usedTopLevelNames, "css", "css_");
1276
1495
  const maybeIncHelperName = needsMaybeInc ? reservePreferredName(usedTopLevelNames, "__maybeInc") : null;
1496
+ const runtimeLookupNames = /* @__PURE__ */ new Map();
1497
+ for (const [lookupKey] of runtimeLookups) {
1498
+ runtimeLookupNames.set(lookupKey, reservePreferredName(usedTopLevelNames, `__${lookupKey}`));
1499
+ }
1277
1500
  const createProperties = buildCreateProperties(createEntries, stylexNamespaceName);
1278
1501
  rewriteExpressionSites({
1279
1502
  ast,
1280
1503
  sites,
1281
1504
  createVarName,
1282
1505
  stylexNamespaceName,
1283
- maybeIncHelperName
1506
+ maybeIncHelperName,
1507
+ runtimeLookupNames
1284
1508
  });
1285
1509
  removeCssImport(ast, cssBindingName);
1286
1510
  if (!findStylexNamespaceImport(ast)) {
@@ -1295,6 +1519,11 @@ function transformTruss(code, filename, mapping) {
1295
1519
  declarationsToInsert.push(...hoistedMarkerDecls);
1296
1520
  if (createProperties.length > 0) {
1297
1521
  declarationsToInsert.push(buildCreateDeclaration(createVarName, stylexNamespaceName, createProperties));
1522
+ for (const [lookupKey, lookup] of runtimeLookups) {
1523
+ const lookupName = runtimeLookupNames.get(lookupKey);
1524
+ if (!lookupName) continue;
1525
+ declarationsToInsert.push(buildRuntimeLookupDeclaration(lookupName, createVarName, lookup));
1526
+ }
1298
1527
  }
1299
1528
  for (const { message, line } of errorMessages) {
1300
1529
  const location = line !== null ? `${filename}:${line}` : filename;
@@ -1450,6 +1679,9 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
1450
1679
  if (seg.dynamicProps && !seg.argResolved) {
1451
1680
  return { error: `dynamic value with variable argument is not supported in .css.ts files` };
1452
1681
  }
1682
+ if (seg.typographyLookup) {
1683
+ return { error: `typography() with a runtime key is not supported in .css.ts files` };
1684
+ }
1453
1685
  if (seg.mediaQuery) {
1454
1686
  return { error: `media query modifiers (ifSm, ifMd, etc.) are not supported in .css.ts files` };
1455
1687
  }