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