@homebound/truss 2.0.0-next.5 → 2.0.0-next.9
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/plugin/index.d.ts +3 -3
- package/build/plugin/index.js +329 -109
- package/build/plugin/index.js.map +1 -1
- package/package.json +1 -1
package/build/plugin/index.d.ts
CHANGED
|
@@ -58,9 +58,9 @@ interface TrussVitePlugin {
|
|
|
58
58
|
* Vite plugin that transforms `Css.*.$` expressions from truss's CssBuilder DSL
|
|
59
59
|
* into file-local `stylex.create()` + `stylex.props()` calls.
|
|
60
60
|
*
|
|
61
|
-
* Also supports `.css.ts` files: a `.css.ts` file with
|
|
62
|
-
*
|
|
63
|
-
*
|
|
61
|
+
* Also supports `.css.ts` files: a `.css.ts` file with
|
|
62
|
+
* `export const css = { ".selector": Css.blue.$ }` can keep other runtime exports,
|
|
63
|
+
* while imports are supplemented with a virtual CSS side-effect module.
|
|
64
64
|
*
|
|
65
65
|
* Must be placed BEFORE the StyleX unplugin in the plugins array so that
|
|
66
66
|
* StyleX's babel plugin can process the generated `stylex.create()` calls.
|
package/build/plugin/index.js
CHANGED
|
@@ -1130,6 +1130,44 @@ function buildMaybeIncDeclaration(helperName, increment) {
|
|
|
1130
1130
|
t2.variableDeclarator(t2.identifier(helperName), t2.arrowFunctionExpression([incParam], body))
|
|
1131
1131
|
]);
|
|
1132
1132
|
}
|
|
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
|
+
}
|
|
1133
1171
|
function buildCreateDeclaration(createVarName, stylexNamespaceName, createProperties) {
|
|
1134
1172
|
const createCall = t2.callExpression(t2.memberExpression(t2.identifier(stylexNamespaceName), t2.identifier("create")), [
|
|
1135
1173
|
t2.objectExpression(createProperties)
|
|
@@ -1191,54 +1229,22 @@ function rewriteExpressionSites(options) {
|
|
|
1191
1229
|
t3.memberExpression(t3.identifier(options.stylexNamespaceName), t3.identifier("props")),
|
|
1192
1230
|
propsArgs
|
|
1193
1231
|
);
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
const attr = attrs[i];
|
|
1200
|
-
if (t3.isJSXAttribute(attr) && t3.isJSXIdentifier(attr.name, { name: "className" })) {
|
|
1201
|
-
if (t3.isStringLiteral(attr.value)) {
|
|
1202
|
-
existingClassNameExpr = attr.value;
|
|
1203
|
-
} else if (t3.isJSXExpressionContainer(attr.value) && t3.isExpression(attr.value.expression)) {
|
|
1204
|
-
existingClassNameExpr = attr.value.expression;
|
|
1205
|
-
}
|
|
1206
|
-
attrs.splice(i, 1);
|
|
1207
|
-
break;
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
}
|
|
1211
|
-
let spreadExpr;
|
|
1212
|
-
if (existingClassNameExpr) {
|
|
1213
|
-
const rId = t3.identifier("__r");
|
|
1214
|
-
const mergedClassName = t3.callExpression(
|
|
1215
|
-
t3.memberExpression(
|
|
1216
|
-
t3.binaryExpression(
|
|
1217
|
-
"+",
|
|
1218
|
-
t3.binaryExpression("+", existingClassNameExpr, t3.stringLiteral(" ")),
|
|
1219
|
-
t3.logicalExpression("||", t3.memberExpression(rId, t3.identifier("className")), t3.stringLiteral(""))
|
|
1220
|
-
),
|
|
1221
|
-
t3.identifier("trim")
|
|
1222
|
-
),
|
|
1223
|
-
[]
|
|
1224
|
-
);
|
|
1225
|
-
spreadExpr = t3.callExpression(
|
|
1226
|
-
t3.arrowFunctionExpression(
|
|
1227
|
-
[rId],
|
|
1228
|
-
t3.objectExpression([t3.spreadElement(rId), t3.objectProperty(t3.identifier("className"), mergedClassName)])
|
|
1229
|
-
),
|
|
1230
|
-
[propsCall]
|
|
1231
|
-
);
|
|
1232
|
-
} else {
|
|
1233
|
-
spreadExpr = propsCall;
|
|
1234
|
-
}
|
|
1235
|
-
cssAttrPath.replaceWith(t3.jsxSpreadAttribute(spreadExpr));
|
|
1232
|
+
cssAttrPath.replaceWith(
|
|
1233
|
+
t3.jsxSpreadAttribute(
|
|
1234
|
+
buildCssSpreadExpression(cssAttrPath, propsCall, options.mergePropsHelperName, options.needsMergePropsHelper)
|
|
1235
|
+
)
|
|
1236
|
+
);
|
|
1236
1237
|
continue;
|
|
1237
1238
|
}
|
|
1238
1239
|
site.path.replaceWith(t3.arrayExpression(propsArgs));
|
|
1239
1240
|
}
|
|
1240
1241
|
rewriteStyleObjectExpressions(options.ast);
|
|
1241
|
-
|
|
1242
|
+
rewriteCssAttributeExpressions(
|
|
1243
|
+
options.ast,
|
|
1244
|
+
options.stylexNamespaceName,
|
|
1245
|
+
options.mergePropsHelperName,
|
|
1246
|
+
options.needsMergePropsHelper
|
|
1247
|
+
);
|
|
1242
1248
|
}
|
|
1243
1249
|
function getCssAttributePath(path) {
|
|
1244
1250
|
const parentPath = path.parentPath;
|
|
@@ -1315,42 +1321,107 @@ function buildPropsArgs(segments, options) {
|
|
|
1315
1321
|
}
|
|
1316
1322
|
return args;
|
|
1317
1323
|
}
|
|
1318
|
-
function
|
|
1324
|
+
function rewriteCssAttributeExpressions(ast, stylexNamespaceName, mergePropsHelperName, needsMergePropsHelper) {
|
|
1319
1325
|
traverse(ast, {
|
|
1320
1326
|
JSXAttribute(path) {
|
|
1321
1327
|
if (!t3.isJSXIdentifier(path.node.name, { name: "css" })) return;
|
|
1322
1328
|
const value = path.node.value;
|
|
1323
1329
|
if (!t3.isJSXExpressionContainer(value)) return;
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
for (const el of expr.elements) {
|
|
1328
|
-
if (t3.isSpreadElement(el)) {
|
|
1329
|
-
const arg = el.argument;
|
|
1330
|
-
if (t3.isArrayExpression(arg)) {
|
|
1331
|
-
for (const inner of arg.elements) {
|
|
1332
|
-
if (!inner) continue;
|
|
1333
|
-
if (t3.isSpreadElement(inner)) {
|
|
1334
|
-
propsArgs.push(t3.spreadElement(inner.argument));
|
|
1335
|
-
} else {
|
|
1336
|
-
propsArgs.push(inner);
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
} else {
|
|
1340
|
-
propsArgs.push(t3.spreadElement(arg));
|
|
1341
|
-
}
|
|
1342
|
-
} else if (el) {
|
|
1343
|
-
propsArgs.push(el);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1330
|
+
if (!t3.isExpression(value.expression)) return;
|
|
1331
|
+
const propsArgs = buildStyleObjectPropsArgs(value.expression, path) ?? buildStyleArrayLikePropsArgsFromExpression(value.expression, path);
|
|
1332
|
+
if (!propsArgs) return;
|
|
1346
1333
|
const propsCall = t3.callExpression(
|
|
1347
1334
|
t3.memberExpression(t3.identifier(stylexNamespaceName), t3.identifier("props")),
|
|
1348
1335
|
propsArgs
|
|
1349
1336
|
);
|
|
1350
|
-
path.replaceWith(
|
|
1337
|
+
path.replaceWith(
|
|
1338
|
+
t3.jsxSpreadAttribute(buildCssSpreadExpression(path, propsCall, mergePropsHelperName, needsMergePropsHelper))
|
|
1339
|
+
);
|
|
1351
1340
|
}
|
|
1352
1341
|
});
|
|
1353
1342
|
}
|
|
1343
|
+
function buildCssSpreadExpression(path, propsCall, mergePropsHelperName, needsMergePropsHelper) {
|
|
1344
|
+
const existingClassNameExpr = removeExistingClassNameAttribute(path);
|
|
1345
|
+
if (!existingClassNameExpr) return propsCall;
|
|
1346
|
+
needsMergePropsHelper.current = true;
|
|
1347
|
+
return t3.callExpression(t3.identifier(mergePropsHelperName), [existingClassNameExpr, ...propsCall.arguments]);
|
|
1348
|
+
}
|
|
1349
|
+
function removeExistingClassNameAttribute(path) {
|
|
1350
|
+
const openingElement = path.parentPath;
|
|
1351
|
+
if (!openingElement || !openingElement.isJSXOpeningElement()) return null;
|
|
1352
|
+
const attrs = openingElement.node.attributes;
|
|
1353
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
1354
|
+
const attr = attrs[i];
|
|
1355
|
+
if (!t3.isJSXAttribute(attr) || !t3.isJSXIdentifier(attr.name, { name: "className" })) continue;
|
|
1356
|
+
let classNameExpr = null;
|
|
1357
|
+
if (t3.isStringLiteral(attr.value)) {
|
|
1358
|
+
classNameExpr = attr.value;
|
|
1359
|
+
} else if (t3.isJSXExpressionContainer(attr.value) && t3.isExpression(attr.value.expression)) {
|
|
1360
|
+
classNameExpr = attr.value.expression;
|
|
1361
|
+
}
|
|
1362
|
+
attrs.splice(i, 1);
|
|
1363
|
+
return classNameExpr;
|
|
1364
|
+
}
|
|
1365
|
+
return null;
|
|
1366
|
+
}
|
|
1367
|
+
function buildStyleObjectPropsArgs(expr, path) {
|
|
1368
|
+
if (!t3.isObjectExpression(expr) || expr.properties.length === 0) return null;
|
|
1369
|
+
let sawStyleArray = false;
|
|
1370
|
+
const propsArgs = [];
|
|
1371
|
+
for (const prop of expr.properties) {
|
|
1372
|
+
if (!t3.isSpreadElement(prop)) return null;
|
|
1373
|
+
const normalizedArg = normalizeStyleArrayLikeExpression(prop.argument, path, /* @__PURE__ */ new Set());
|
|
1374
|
+
if (!normalizedArg) {
|
|
1375
|
+
propsArgs.push(t3.spreadElement(prop.argument));
|
|
1376
|
+
continue;
|
|
1377
|
+
}
|
|
1378
|
+
if (isStyleArrayLike(normalizedArg, path, /* @__PURE__ */ new Set())) {
|
|
1379
|
+
sawStyleArray = true;
|
|
1380
|
+
}
|
|
1381
|
+
const nestedArgs = buildStyleArrayLikePropsArgs(normalizedArg, path, /* @__PURE__ */ new Set());
|
|
1382
|
+
if (nestedArgs && t3.isArrayExpression(normalizedArg)) {
|
|
1383
|
+
propsArgs.push(...nestedArgs);
|
|
1384
|
+
} else {
|
|
1385
|
+
propsArgs.push(t3.spreadElement(normalizedArg));
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
return sawStyleArray ? propsArgs : null;
|
|
1389
|
+
}
|
|
1390
|
+
function buildStyleArrayLikePropsArgsFromExpression(expr, path) {
|
|
1391
|
+
const normalizedExpr = normalizeStyleArrayLikeExpression(expr, path, /* @__PURE__ */ new Set());
|
|
1392
|
+
if (!normalizedExpr) return null;
|
|
1393
|
+
return buildStyleArrayLikePropsArgs(normalizedExpr, path, /* @__PURE__ */ new Set());
|
|
1394
|
+
}
|
|
1395
|
+
function buildStyleArrayLikePropsArgs(expr, path, seen) {
|
|
1396
|
+
if (seen.has(expr)) return null;
|
|
1397
|
+
seen.add(expr);
|
|
1398
|
+
if (t3.isArrayExpression(expr)) {
|
|
1399
|
+
const propsArgs = [];
|
|
1400
|
+
for (const el of expr.elements) {
|
|
1401
|
+
if (!el) continue;
|
|
1402
|
+
if (t3.isSpreadElement(el)) {
|
|
1403
|
+
const normalizedArg = normalizeStyleArrayLikeExpression(el.argument, path, /* @__PURE__ */ new Set());
|
|
1404
|
+
if (!normalizedArg) {
|
|
1405
|
+
propsArgs.push(t3.spreadElement(el.argument));
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
const nestedArgs = buildStyleArrayLikePropsArgs(normalizedArg, path, seen);
|
|
1409
|
+
if (nestedArgs && t3.isArrayExpression(normalizedArg)) {
|
|
1410
|
+
propsArgs.push(...nestedArgs);
|
|
1411
|
+
} else {
|
|
1412
|
+
propsArgs.push(t3.spreadElement(normalizedArg));
|
|
1413
|
+
}
|
|
1414
|
+
continue;
|
|
1415
|
+
}
|
|
1416
|
+
propsArgs.push(el);
|
|
1417
|
+
}
|
|
1418
|
+
return propsArgs;
|
|
1419
|
+
}
|
|
1420
|
+
if (t3.isIdentifier(expr) || t3.isMemberExpression(expr) || t3.isConditionalExpression(expr)) {
|
|
1421
|
+
return [t3.spreadElement(expr)];
|
|
1422
|
+
}
|
|
1423
|
+
return null;
|
|
1424
|
+
}
|
|
1354
1425
|
function rewriteStyleObjectExpressions(ast) {
|
|
1355
1426
|
traverse(ast, {
|
|
1356
1427
|
ObjectExpression(path) {
|
|
@@ -1368,7 +1439,7 @@ function tryBuildStyleArrayFromObject(path) {
|
|
|
1368
1439
|
if (!t3.isSpreadElement(prop)) {
|
|
1369
1440
|
return null;
|
|
1370
1441
|
}
|
|
1371
|
-
const normalizedArg =
|
|
1442
|
+
const normalizedArg = normalizeStyleArrayLikeExpression(
|
|
1372
1443
|
prop.argument,
|
|
1373
1444
|
path,
|
|
1374
1445
|
/* @__PURE__ */ new Set()
|
|
@@ -1377,7 +1448,7 @@ function tryBuildStyleArrayFromObject(path) {
|
|
|
1377
1448
|
if (!normalizedArg) {
|
|
1378
1449
|
return null;
|
|
1379
1450
|
}
|
|
1380
|
-
if (
|
|
1451
|
+
if (isStyleArrayLike(normalizedArg, path, /* @__PURE__ */ new Set())) {
|
|
1381
1452
|
sawStyleArray = true;
|
|
1382
1453
|
}
|
|
1383
1454
|
if (t3.isArrayExpression(normalizedArg)) {
|
|
@@ -1389,30 +1460,30 @@ function tryBuildStyleArrayFromObject(path) {
|
|
|
1389
1460
|
if (!sawStyleArray) return null;
|
|
1390
1461
|
return t3.arrayExpression(elements);
|
|
1391
1462
|
}
|
|
1392
|
-
function
|
|
1463
|
+
function normalizeStyleArrayLikeExpression(expr, path, seen) {
|
|
1393
1464
|
if (seen.has(expr)) return null;
|
|
1394
1465
|
seen.add(expr);
|
|
1395
1466
|
if (t3.isArrayExpression(expr)) return expr;
|
|
1396
1467
|
if (t3.isConditionalExpression(expr)) {
|
|
1397
|
-
const consequent =
|
|
1398
|
-
const alternate =
|
|
1468
|
+
const consequent = normalizeStyleArrayLikeBranch(expr.consequent, path, seen);
|
|
1469
|
+
const alternate = normalizeStyleArrayLikeBranch(expr.alternate, path, seen);
|
|
1399
1470
|
if (!consequent || !alternate) return null;
|
|
1400
1471
|
return t3.conditionalExpression(expr.test, consequent, alternate);
|
|
1401
1472
|
}
|
|
1402
1473
|
if (t3.isIdentifier(expr) || t3.isMemberExpression(expr)) {
|
|
1403
1474
|
const nestedSeen = new Set(seen);
|
|
1404
1475
|
nestedSeen.delete(expr);
|
|
1405
|
-
if (
|
|
1476
|
+
if (isStyleArrayLike(expr, path, nestedSeen)) return expr;
|
|
1406
1477
|
}
|
|
1407
1478
|
return null;
|
|
1408
1479
|
}
|
|
1409
|
-
function
|
|
1480
|
+
function normalizeStyleArrayLikeBranch(expr, path, seen) {
|
|
1410
1481
|
if (isEmptyObjectExpression(expr)) {
|
|
1411
1482
|
return t3.arrayExpression([]);
|
|
1412
1483
|
}
|
|
1413
|
-
return
|
|
1484
|
+
return normalizeStyleArrayLikeExpression(expr, path, seen);
|
|
1414
1485
|
}
|
|
1415
|
-
function
|
|
1486
|
+
function isStyleArrayLike(expr, path, seen) {
|
|
1416
1487
|
if (seen.has(expr)) return false;
|
|
1417
1488
|
seen.add(expr);
|
|
1418
1489
|
if (t3.isArrayExpression(expr)) return true;
|
|
@@ -1424,7 +1495,7 @@ function isStyleArrayLikeExpression(expr, path, seen) {
|
|
|
1424
1495
|
const bindingPath = binding?.path;
|
|
1425
1496
|
if (!bindingPath || !bindingPath.isVariableDeclarator()) return false;
|
|
1426
1497
|
const init = bindingPath.node.init;
|
|
1427
|
-
return !!(init &&
|
|
1498
|
+
return !!(init && isStyleArrayLike(init, bindingPath, seen));
|
|
1428
1499
|
}
|
|
1429
1500
|
if (t3.isMemberExpression(expr) && !expr.computed && t3.isIdentifier(expr.property)) {
|
|
1430
1501
|
const object = expr.object;
|
|
@@ -1439,13 +1510,13 @@ function isStyleArrayLikeExpression(expr, path, seen) {
|
|
|
1439
1510
|
if (!t3.isObjectProperty(prop) || prop.computed) continue;
|
|
1440
1511
|
if (!isMatchingPropertyName(prop.key, propertyName)) continue;
|
|
1441
1512
|
const value = prop.value;
|
|
1442
|
-
return t3.isExpression(value) &&
|
|
1513
|
+
return t3.isExpression(value) && isStyleArrayLike(value, bindingPath, seen);
|
|
1443
1514
|
}
|
|
1444
1515
|
}
|
|
1445
1516
|
return false;
|
|
1446
1517
|
}
|
|
1447
1518
|
function isStyleArrayLikeBranch(expr, path, seen) {
|
|
1448
|
-
return isEmptyObjectExpression(expr) ||
|
|
1519
|
+
return isEmptyObjectExpression(expr) || isStyleArrayLike(expr, path, seen);
|
|
1449
1520
|
}
|
|
1450
1521
|
function isMatchingPropertyName(key, name) {
|
|
1451
1522
|
return t3.isIdentifier(key) && key.name === name || t3.isStringLiteral(key) && key.value === name;
|
|
@@ -1493,6 +1564,8 @@ function transformTruss(code, filename, mapping) {
|
|
|
1493
1564
|
const stylexNamespaceName = existingStylexNamespace ?? reservePreferredName(usedTopLevelNames, "stylex");
|
|
1494
1565
|
const createVarName = reservePreferredName(usedTopLevelNames, "css", "css_");
|
|
1495
1566
|
const maybeIncHelperName = needsMaybeInc ? reservePreferredName(usedTopLevelNames, "__maybeInc") : null;
|
|
1567
|
+
const mergePropsHelperName = reservePreferredName(usedTopLevelNames, "__mergeProps");
|
|
1568
|
+
const needsMergePropsHelper = { current: false };
|
|
1496
1569
|
const runtimeLookupNames = /* @__PURE__ */ new Map();
|
|
1497
1570
|
for (const [lookupKey] of runtimeLookups) {
|
|
1498
1571
|
runtimeLookupNames.set(lookupKey, reservePreferredName(usedTopLevelNames, `__${lookupKey}`));
|
|
@@ -1504,6 +1577,8 @@ function transformTruss(code, filename, mapping) {
|
|
|
1504
1577
|
createVarName,
|
|
1505
1578
|
stylexNamespaceName,
|
|
1506
1579
|
maybeIncHelperName,
|
|
1580
|
+
mergePropsHelperName,
|
|
1581
|
+
needsMergePropsHelper,
|
|
1507
1582
|
runtimeLookupNames
|
|
1508
1583
|
});
|
|
1509
1584
|
removeCssImport(ast, cssBindingName);
|
|
@@ -1516,6 +1591,9 @@ function transformTruss(code, filename, mapping) {
|
|
|
1516
1591
|
if (maybeIncHelperName) {
|
|
1517
1592
|
declarationsToInsert.push(buildMaybeIncDeclaration(maybeIncHelperName, mapping.increment));
|
|
1518
1593
|
}
|
|
1594
|
+
if (needsMergePropsHelper.current) {
|
|
1595
|
+
declarationsToInsert.push(buildMergePropsDeclaration(mergePropsHelperName, stylexNamespaceName));
|
|
1596
|
+
}
|
|
1519
1597
|
declarationsToInsert.push(...hoistedMarkerDecls);
|
|
1520
1598
|
if (createProperties.length > 0) {
|
|
1521
1599
|
declarationsToInsert.push(buildCreateDeclaration(createVarName, stylexNamespaceName, createProperties));
|
|
@@ -1587,7 +1665,72 @@ function hoistMarkerDeclarations(ast, names) {
|
|
|
1587
1665
|
|
|
1588
1666
|
// src/plugin/transform-css.ts
|
|
1589
1667
|
import { parse as parse2 } from "@babel/parser";
|
|
1668
|
+
import * as t6 from "@babel/types";
|
|
1669
|
+
|
|
1670
|
+
// src/plugin/css-ts-utils.ts
|
|
1590
1671
|
import * as t5 from "@babel/types";
|
|
1672
|
+
function collectStaticStringBindings(ast) {
|
|
1673
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
1674
|
+
let changed = true;
|
|
1675
|
+
while (changed) {
|
|
1676
|
+
changed = false;
|
|
1677
|
+
for (const node of ast.program.body) {
|
|
1678
|
+
const declaration = getTopLevelVariableDeclaration(node);
|
|
1679
|
+
if (!declaration) continue;
|
|
1680
|
+
for (const declarator of declaration.declarations) {
|
|
1681
|
+
if (!t5.isIdentifier(declarator.id) || !declarator.init) continue;
|
|
1682
|
+
if (bindings.has(declarator.id.name)) continue;
|
|
1683
|
+
const value = resolveStaticString(declarator.init, bindings);
|
|
1684
|
+
if (value === null) continue;
|
|
1685
|
+
bindings.set(declarator.id.name, value);
|
|
1686
|
+
changed = true;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
return bindings;
|
|
1691
|
+
}
|
|
1692
|
+
function resolveStaticString(node, bindings) {
|
|
1693
|
+
if (!node) return null;
|
|
1694
|
+
if (t5.isStringLiteral(node)) return node.value;
|
|
1695
|
+
if (t5.isTemplateLiteral(node)) {
|
|
1696
|
+
let value = "";
|
|
1697
|
+
for (let i = 0; i < node.quasis.length; i++) {
|
|
1698
|
+
value += node.quasis[i].value.cooked ?? "";
|
|
1699
|
+
if (i >= node.expressions.length) continue;
|
|
1700
|
+
const expressionValue = resolveStaticString(node.expressions[i], bindings);
|
|
1701
|
+
if (expressionValue === null) return null;
|
|
1702
|
+
value += expressionValue;
|
|
1703
|
+
}
|
|
1704
|
+
return value;
|
|
1705
|
+
}
|
|
1706
|
+
if (t5.isIdentifier(node)) {
|
|
1707
|
+
return bindings.get(node.name) ?? null;
|
|
1708
|
+
}
|
|
1709
|
+
if (t5.isTSAsExpression(node) || t5.isTSSatisfiesExpression(node) || t5.isTSNonNullExpression(node)) {
|
|
1710
|
+
return resolveStaticString(node.expression, bindings);
|
|
1711
|
+
}
|
|
1712
|
+
if (t5.isParenthesizedExpression(node)) {
|
|
1713
|
+
return resolveStaticString(node.expression, bindings);
|
|
1714
|
+
}
|
|
1715
|
+
if (t5.isBinaryExpression(node, { operator: "+" })) {
|
|
1716
|
+
const left = resolveStaticString(node.left, bindings);
|
|
1717
|
+
const right = resolveStaticString(node.right, bindings);
|
|
1718
|
+
if (left === null || right === null) return null;
|
|
1719
|
+
return left + right;
|
|
1720
|
+
}
|
|
1721
|
+
return null;
|
|
1722
|
+
}
|
|
1723
|
+
function getTopLevelVariableDeclaration(node) {
|
|
1724
|
+
if (t5.isVariableDeclaration(node)) {
|
|
1725
|
+
return node;
|
|
1726
|
+
}
|
|
1727
|
+
if (t5.isExportNamedDeclaration(node) && node.declaration && t5.isVariableDeclaration(node.declaration)) {
|
|
1728
|
+
return node.declaration;
|
|
1729
|
+
}
|
|
1730
|
+
return null;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
// src/plugin/transform-css.ts
|
|
1591
1734
|
function transformCssTs(code, filename, mapping) {
|
|
1592
1735
|
const ast = parse2(code, {
|
|
1593
1736
|
sourceType: "module",
|
|
@@ -1599,28 +1742,29 @@ function transformCssTs(code, filename, mapping) {
|
|
|
1599
1742
|
return `/* [truss] ${filename}: no Css import found */
|
|
1600
1743
|
`;
|
|
1601
1744
|
}
|
|
1602
|
-
const
|
|
1603
|
-
if (!
|
|
1604
|
-
return `/* [truss] ${filename}: expected \`export
|
|
1745
|
+
const cssExport = findNamedCssExportObject(ast);
|
|
1746
|
+
if (!cssExport) {
|
|
1747
|
+
return `/* [truss] ${filename}: expected \`export const css = { ... }\` with an object literal */
|
|
1605
1748
|
`;
|
|
1606
1749
|
}
|
|
1607
1750
|
const rules = [];
|
|
1608
|
-
|
|
1609
|
-
|
|
1751
|
+
const stringBindings = collectStaticStringBindings(ast);
|
|
1752
|
+
for (const prop of cssExport.properties) {
|
|
1753
|
+
if (t6.isSpreadElement(prop)) {
|
|
1610
1754
|
rules.push(`/* [truss] unsupported: spread elements in css.ts export */`);
|
|
1611
1755
|
continue;
|
|
1612
1756
|
}
|
|
1613
|
-
if (!
|
|
1757
|
+
if (!t6.isObjectProperty(prop)) {
|
|
1614
1758
|
rules.push(`/* [truss] unsupported: non-property in css.ts export */`);
|
|
1615
1759
|
continue;
|
|
1616
1760
|
}
|
|
1617
|
-
const selector = objectPropertyStringKey(prop);
|
|
1761
|
+
const selector = objectPropertyStringKey(prop, stringBindings);
|
|
1618
1762
|
if (selector === null) {
|
|
1619
1763
|
rules.push(`/* [truss] unsupported: non-string-literal key in css.ts export */`);
|
|
1620
1764
|
continue;
|
|
1621
1765
|
}
|
|
1622
1766
|
const valueNode = prop.value;
|
|
1623
|
-
if (!
|
|
1767
|
+
if (!t6.isExpression(valueNode)) {
|
|
1624
1768
|
rules.push(`/* [truss] unsupported: "${selector}" value is not an expression */`);
|
|
1625
1769
|
continue;
|
|
1626
1770
|
}
|
|
@@ -1633,23 +1777,32 @@ function transformCssTs(code, filename, mapping) {
|
|
|
1633
1777
|
}
|
|
1634
1778
|
return rules.join("\n\n") + "\n";
|
|
1635
1779
|
}
|
|
1636
|
-
function
|
|
1780
|
+
function findNamedCssExportObject(ast) {
|
|
1637
1781
|
for (const node of ast.program.body) {
|
|
1638
|
-
if (!
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1782
|
+
if (!t6.isExportNamedDeclaration(node) || !node.declaration) continue;
|
|
1783
|
+
if (!t6.isVariableDeclaration(node.declaration)) continue;
|
|
1784
|
+
for (const declarator of node.declaration.declarations) {
|
|
1785
|
+
if (!t6.isIdentifier(declarator.id, { name: "css" })) continue;
|
|
1786
|
+
const value = unwrapObjectExpression(declarator.init);
|
|
1787
|
+
if (value) return value;
|
|
1788
|
+
}
|
|
1643
1789
|
}
|
|
1644
1790
|
return null;
|
|
1645
1791
|
}
|
|
1646
|
-
function
|
|
1647
|
-
if (
|
|
1648
|
-
if (
|
|
1792
|
+
function unwrapObjectExpression(node) {
|
|
1793
|
+
if (!node) return null;
|
|
1794
|
+
if (t6.isObjectExpression(node)) return node;
|
|
1795
|
+
if (t6.isTSAsExpression(node) || t6.isTSSatisfiesExpression(node)) return unwrapObjectExpression(node.expression);
|
|
1796
|
+
return null;
|
|
1797
|
+
}
|
|
1798
|
+
function objectPropertyStringKey(prop, stringBindings) {
|
|
1799
|
+
if (t6.isStringLiteral(prop.key)) return prop.key.value;
|
|
1800
|
+
if (t6.isIdentifier(prop.key) && !prop.computed) return prop.key.name;
|
|
1801
|
+
if (prop.computed) return resolveStaticString(prop.key, stringBindings);
|
|
1649
1802
|
return null;
|
|
1650
1803
|
}
|
|
1651
1804
|
function resolveCssExpression(node, cssBindingName, mapping, filename) {
|
|
1652
|
-
if (!
|
|
1805
|
+
if (!t6.isMemberExpression(node) || node.computed || !t6.isIdentifier(node.property, { name: "$" })) {
|
|
1653
1806
|
return { error: "value must be a Css.*.$ expression" };
|
|
1654
1807
|
}
|
|
1655
1808
|
const chain = extractChain(node.object, cssBindingName);
|
|
@@ -1718,8 +1871,61 @@ ${body}
|
|
|
1718
1871
|
}`;
|
|
1719
1872
|
}
|
|
1720
1873
|
|
|
1874
|
+
// src/plugin/rewrite-css-ts-imports.ts
|
|
1875
|
+
import { parse as parse3 } from "@babel/parser";
|
|
1876
|
+
import _generate2 from "@babel/generator";
|
|
1877
|
+
import * as t7 from "@babel/types";
|
|
1878
|
+
var generate2 = _generate2.default ?? _generate2;
|
|
1879
|
+
function rewriteCssTsImports(code, filename) {
|
|
1880
|
+
if (!code.includes(".css.ts")) {
|
|
1881
|
+
return { code, changed: false };
|
|
1882
|
+
}
|
|
1883
|
+
const ast = parse3(code, {
|
|
1884
|
+
sourceType: "module",
|
|
1885
|
+
plugins: ["typescript", "jsx"],
|
|
1886
|
+
sourceFilename: filename
|
|
1887
|
+
});
|
|
1888
|
+
const existingCssSideEffects = /* @__PURE__ */ new Set();
|
|
1889
|
+
const neededCssSideEffects = /* @__PURE__ */ new Set();
|
|
1890
|
+
let changed = false;
|
|
1891
|
+
for (const node of ast.program.body) {
|
|
1892
|
+
if (!t7.isImportDeclaration(node)) continue;
|
|
1893
|
+
if (typeof node.source.value !== "string") continue;
|
|
1894
|
+
if (!node.source.value.endsWith(".css.ts")) continue;
|
|
1895
|
+
if (node.specifiers.length === 0) {
|
|
1896
|
+
node.source = t7.stringLiteral(toVirtualCssSpecifier(node.source.value));
|
|
1897
|
+
existingCssSideEffects.add(node.source.value);
|
|
1898
|
+
changed = true;
|
|
1899
|
+
continue;
|
|
1900
|
+
}
|
|
1901
|
+
neededCssSideEffects.add(toVirtualCssSpecifier(node.source.value));
|
|
1902
|
+
}
|
|
1903
|
+
const sideEffectImports = [];
|
|
1904
|
+
for (const source of neededCssSideEffects) {
|
|
1905
|
+
if (existingCssSideEffects.has(source)) continue;
|
|
1906
|
+
sideEffectImports.push(t7.importDeclaration([], t7.stringLiteral(source)));
|
|
1907
|
+
changed = true;
|
|
1908
|
+
}
|
|
1909
|
+
if (!changed) {
|
|
1910
|
+
return { code, changed: false };
|
|
1911
|
+
}
|
|
1912
|
+
if (sideEffectImports.length > 0) {
|
|
1913
|
+
const insertIndex = findLastImportIndex(ast) + 1;
|
|
1914
|
+
ast.program.body.splice(insertIndex, 0, ...sideEffectImports);
|
|
1915
|
+
}
|
|
1916
|
+
const output = generate2(ast, {
|
|
1917
|
+
sourceFileName: filename,
|
|
1918
|
+
retainLines: false
|
|
1919
|
+
});
|
|
1920
|
+
return { code: output.code, changed: true };
|
|
1921
|
+
}
|
|
1922
|
+
function toVirtualCssSpecifier(source) {
|
|
1923
|
+
return `${source}?truss-css`;
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1721
1926
|
// src/plugin/index.ts
|
|
1722
1927
|
var VIRTUAL_CSS_PREFIX = "\0truss-css:";
|
|
1928
|
+
var CSS_TS_QUERY = "?truss-css";
|
|
1723
1929
|
function trussPlugin(opts) {
|
|
1724
1930
|
let mapping = null;
|
|
1725
1931
|
let projectRoot;
|
|
@@ -1743,15 +1949,8 @@ function trussPlugin(opts) {
|
|
|
1743
1949
|
ensureMapping();
|
|
1744
1950
|
},
|
|
1745
1951
|
resolveId(source, importer) {
|
|
1746
|
-
if (!source.endsWith(
|
|
1747
|
-
|
|
1748
|
-
if (isAbsolute(source)) {
|
|
1749
|
-
absolutePath = source;
|
|
1750
|
-
} else if (importer) {
|
|
1751
|
-
absolutePath = resolve(dirname(importer), source);
|
|
1752
|
-
} else {
|
|
1753
|
-
absolutePath = resolve(projectRoot || process.cwd(), source);
|
|
1754
|
-
}
|
|
1952
|
+
if (!source.endsWith(CSS_TS_QUERY)) return null;
|
|
1953
|
+
const absolutePath = resolveImportPath(source.slice(0, -CSS_TS_QUERY.length), importer, projectRoot);
|
|
1755
1954
|
if (!existsSync(absolutePath)) return null;
|
|
1756
1955
|
return VIRTUAL_CSS_PREFIX + absolutePath.slice(0, -3);
|
|
1757
1956
|
},
|
|
@@ -1763,17 +1962,38 @@ function trussPlugin(opts) {
|
|
|
1763
1962
|
},
|
|
1764
1963
|
transform(code, id) {
|
|
1765
1964
|
if (!/\.[cm]?[jt]sx?(\?|$)/.test(id)) return null;
|
|
1766
|
-
|
|
1965
|
+
const rewrittenImports = rewriteCssTsImports(code, id);
|
|
1966
|
+
const rewrittenCode = rewrittenImports.code;
|
|
1967
|
+
const hasCssDsl = rewrittenCode.includes("Css");
|
|
1968
|
+
if (!hasCssDsl && !rewrittenImports.changed) return null;
|
|
1767
1969
|
const fileId = stripQueryAndHash(id);
|
|
1768
1970
|
if (isNodeModulesFile(fileId) && !isWhitelistedExternalPackageFile(fileId, externalPackages)) {
|
|
1769
1971
|
return null;
|
|
1770
1972
|
}
|
|
1771
|
-
|
|
1772
|
-
|
|
1973
|
+
if (fileId.endsWith(".css.ts")) {
|
|
1974
|
+
return rewrittenImports.changed ? { code: rewrittenCode, map: null } : null;
|
|
1975
|
+
}
|
|
1976
|
+
if (!hasCssDsl) {
|
|
1977
|
+
return { code: rewrittenCode, map: null };
|
|
1978
|
+
}
|
|
1979
|
+
const result = transformTruss(rewrittenCode, id, ensureMapping());
|
|
1980
|
+
if (!result) {
|
|
1981
|
+
if (!rewrittenImports.changed) return null;
|
|
1982
|
+
return { code: rewrittenCode, map: null };
|
|
1983
|
+
}
|
|
1773
1984
|
return { code: result.code, map: result.map };
|
|
1774
1985
|
}
|
|
1775
1986
|
};
|
|
1776
1987
|
}
|
|
1988
|
+
function resolveImportPath(source, importer, projectRoot) {
|
|
1989
|
+
if (isAbsolute(source)) {
|
|
1990
|
+
return source;
|
|
1991
|
+
}
|
|
1992
|
+
if (importer) {
|
|
1993
|
+
return resolve(dirname(importer), source);
|
|
1994
|
+
}
|
|
1995
|
+
return resolve(projectRoot || process.cwd(), source);
|
|
1996
|
+
}
|
|
1777
1997
|
function stripQueryAndHash(id) {
|
|
1778
1998
|
const queryIndex = id.indexOf("?");
|
|
1779
1999
|
const hashIndex = id.indexOf("#");
|