@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.
- package/build/index.js +39 -1
- package/build/index.js.map +1 -1
- package/build/plugin/index.d.ts +1 -0
- package/build/plugin/index.js +236 -4
- package/build/plugin/index.js.map +1 -1
- package/cli.js +6 -19
- package/package.json +3 -3
package/build/plugin/index.js
CHANGED
|
@@ -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
|
}
|