@homebound/truss 2.29.6 → 2.29.8
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.js +266 -141
- package/build/plugin/index.js.map +1 -1
- package/build/runtime.d.ts +51 -17
- package/build/runtime.js +142 -26
- package/build/runtime.js.map +1 -1
- package/package.json +3 -1
- package/tsconfig.tsbuildinfo +1 -1
package/build/plugin/index.js
CHANGED
|
@@ -56,6 +56,7 @@ function removeCssImport(ast, cssBinding) {
|
|
|
56
56
|
ast.program.body.splice(i, 1);
|
|
57
57
|
} else {
|
|
58
58
|
node.specifiers.splice(cssSpecIndex, 1);
|
|
59
|
+
hoistTypeOnlyImportKind(node);
|
|
59
60
|
}
|
|
60
61
|
return;
|
|
61
62
|
}
|
|
@@ -76,10 +77,10 @@ function insertAfterLeadingImports(ast, statements) {
|
|
|
76
77
|
}
|
|
77
78
|
function findNamedImportBinding(ast, importedName, source) {
|
|
78
79
|
for (const node of ast.program.body) {
|
|
79
|
-
if (!t.isImportDeclaration(node)) continue;
|
|
80
|
+
if (!t.isImportDeclaration(node) || node.importKind === "type") continue;
|
|
80
81
|
if (source !== void 0 && node.source.value !== source) continue;
|
|
81
82
|
for (const spec of node.specifiers) {
|
|
82
|
-
if (t.isImportSpecifier(spec) && t.isIdentifier(spec.imported, { name: importedName })) {
|
|
83
|
+
if (t.isImportSpecifier(spec) && spec.importKind !== "type" && t.isIdentifier(spec.imported, { name: importedName })) {
|
|
83
84
|
return spec.local.name;
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -109,7 +110,9 @@ function replaceCssImportWithNamedImports(ast, cssBinding, source, imports) {
|
|
|
109
110
|
}
|
|
110
111
|
function upsertNamedImports(ast, source, imports) {
|
|
111
112
|
if (imports.length === 0) return;
|
|
112
|
-
const existing =
|
|
113
|
+
const existing = ast.program.body.find(
|
|
114
|
+
(node) => t.isImportDeclaration(node) && node.source.value === source && node.importKind !== "type" && !node.specifiers.some((spec) => t.isImportNamespaceSpecifier(spec))
|
|
115
|
+
);
|
|
113
116
|
if (!existing) {
|
|
114
117
|
const importDecl = t.importDeclaration(imports.map(toImportSpecifier), t.stringLiteral(source));
|
|
115
118
|
ast.program.body.splice(findLastImportIndex(ast) + 1, 0, importDecl);
|
|
@@ -117,7 +120,7 @@ function upsertNamedImports(ast, source, imports) {
|
|
|
117
120
|
}
|
|
118
121
|
for (const entry of imports) {
|
|
119
122
|
const exists = existing.specifiers.some((spec) => {
|
|
120
|
-
return t.isImportSpecifier(spec) && t.isIdentifier(spec.imported, { name: entry.importedName });
|
|
123
|
+
return t.isImportSpecifier(spec) && spec.importKind !== "type" && t.isIdentifier(spec.imported, { name: entry.importedName });
|
|
121
124
|
});
|
|
122
125
|
if (!exists) existing.specifiers.push(toImportSpecifier(entry));
|
|
123
126
|
}
|
|
@@ -183,6 +186,15 @@ function memberPropertyName(node) {
|
|
|
183
186
|
if (node.computed && t.isStringLiteral(node.property)) return node.property.value;
|
|
184
187
|
return null;
|
|
185
188
|
}
|
|
189
|
+
function hoistTypeOnlyImportKind(node) {
|
|
190
|
+
if (node.importKind === "type") return;
|
|
191
|
+
const typeOnly = node.specifiers.every((spec) => t.isImportSpecifier(spec) && spec.importKind === "type");
|
|
192
|
+
if (!typeOnly) return;
|
|
193
|
+
node.importKind = "type";
|
|
194
|
+
for (const spec of node.specifiers) {
|
|
195
|
+
if (t.isImportSpecifier(spec)) spec.importKind = null;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
186
198
|
function toImportSpecifier(entry) {
|
|
187
199
|
return t.importSpecifier(t.identifier(entry.localName), t.identifier(entry.importedName));
|
|
188
200
|
}
|
|
@@ -1874,6 +1886,55 @@ function flattenWhenObjectParts(resolved) {
|
|
|
1874
1886
|
return segments;
|
|
1875
1887
|
}
|
|
1876
1888
|
|
|
1889
|
+
// src/css-order.ts
|
|
1890
|
+
function ruleSortKey(priority, className, atRulePrelude2) {
|
|
1891
|
+
const widthInterval = atRulePrelude2 === void 0 ? null : parseWidthInterval(atRulePrelude2);
|
|
1892
|
+
return { priority, className, widthInterval };
|
|
1893
|
+
}
|
|
1894
|
+
function compareRuleSortKeys(a, b) {
|
|
1895
|
+
return a.priority - b.priority || compareWidthIntervals(a.widthInterval, b.widthInterval) || compareClassNames(a.className, b.className);
|
|
1896
|
+
}
|
|
1897
|
+
function compareClassNames(a, b) {
|
|
1898
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
1899
|
+
}
|
|
1900
|
+
function atRulePrelude(cssText) {
|
|
1901
|
+
if (!cssText.startsWith("@")) return void 0;
|
|
1902
|
+
const brace = cssText.indexOf("{");
|
|
1903
|
+
return brace === -1 ? void 0 : cssText.slice(0, brace).trim();
|
|
1904
|
+
}
|
|
1905
|
+
function parseWidthInterval(prelude) {
|
|
1906
|
+
if (/,|\bnot\b|\bor\b|[<>]/.test(prelude)) return null;
|
|
1907
|
+
const terms = Array.from(prelude.matchAll(/\((min|max)-width:\s*([^)]*)\)/g));
|
|
1908
|
+
if (terms.length === 0) return null;
|
|
1909
|
+
let lo = 0;
|
|
1910
|
+
let hi = Infinity;
|
|
1911
|
+
for (const term of terms) {
|
|
1912
|
+
const px = parsePxLength(term[2]);
|
|
1913
|
+
if (px === null) return null;
|
|
1914
|
+
if (term[1] === "min") {
|
|
1915
|
+
lo = Math.max(lo, px);
|
|
1916
|
+
} else {
|
|
1917
|
+
hi = Math.min(hi, px);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
return { lo, hi };
|
|
1921
|
+
}
|
|
1922
|
+
function parsePxLength(value) {
|
|
1923
|
+
const match = value.trim().match(/^(\d+(?:\.\d+)?)(px)?$/);
|
|
1924
|
+
if (!match) return null;
|
|
1925
|
+
if (match[2] === void 0 && Number(match[1]) !== 0) return null;
|
|
1926
|
+
return Number(match[1]);
|
|
1927
|
+
}
|
|
1928
|
+
function compareWidthIntervals(a, b) {
|
|
1929
|
+
if (a === null || b === null) {
|
|
1930
|
+
return (a === null ? 1 : 0) - (b === null ? 1 : 0);
|
|
1931
|
+
}
|
|
1932
|
+
const widthA = a.hi - a.lo;
|
|
1933
|
+
const widthB = b.hi - b.lo;
|
|
1934
|
+
if (widthA !== widthB) return widthA > widthB ? -1 : 1;
|
|
1935
|
+
return a.lo - b.lo;
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1877
1938
|
// src/plugin/property-priorities.ts
|
|
1878
1939
|
var longHandPhysical = /* @__PURE__ */ new Set();
|
|
1879
1940
|
var longHandLogical = /* @__PURE__ */ new Set();
|
|
@@ -2428,13 +2489,6 @@ function computeRulePriority(rule) {
|
|
|
2428
2489
|
function isVariableRule(rule) {
|
|
2429
2490
|
return rule.declarations.some((d) => d.cssVarName !== void 0);
|
|
2430
2491
|
}
|
|
2431
|
-
function ruleSortKey(priority, className, atRulePrelude2) {
|
|
2432
|
-
const widthInterval = atRulePrelude2 === void 0 ? null : parseWidthInterval(atRulePrelude2);
|
|
2433
|
-
return { priority, className, widthInterval };
|
|
2434
|
-
}
|
|
2435
|
-
function compareRuleSortKeys(a, b) {
|
|
2436
|
-
return a.priority - b.priority || compareWidthIntervals(a.widthInterval, b.widthInterval) || compareClassNames(a.className, b.className);
|
|
2437
|
-
}
|
|
2438
2492
|
function sortRulesByPriority(rules) {
|
|
2439
2493
|
const decorated = Array.from(rules, (rule) => {
|
|
2440
2494
|
const priority = computeRulePriority(rule);
|
|
@@ -2443,40 +2497,83 @@ function sortRulesByPriority(rules) {
|
|
|
2443
2497
|
decorated.sort((a, b) => compareRuleSortKeys(a.key, b.key));
|
|
2444
2498
|
return decorated.map((d) => ({ rule: d.rule, priority: d.priority }));
|
|
2445
2499
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2500
|
+
|
|
2501
|
+
// src/plugin/truss-css.ts
|
|
2502
|
+
var RULE_ANNOTATION_RE = /^\/\* @truss p:([\d.]+) c:(\S+) \*\/$/;
|
|
2503
|
+
var PROPERTY_ANNOTATION_RE = /^\/\* @truss @property \*\/$/;
|
|
2504
|
+
var ARBITRARY_START_RE = /^\/\* @truss arbitrary:start \*\/$/;
|
|
2505
|
+
var ARBITRARY_END_RE = /^\/\* @truss arbitrary:end \*\/$/;
|
|
2506
|
+
var PROPERTY_VAR_RE = /^@property\s+(--\S+)/;
|
|
2507
|
+
function parseTrussCss(cssText) {
|
|
2508
|
+
const lines = cssText.split("\n");
|
|
2509
|
+
const rules = [];
|
|
2510
|
+
const properties = [];
|
|
2511
|
+
const arbitraryCssBlocks = [];
|
|
2512
|
+
let i = 0;
|
|
2513
|
+
function takeAnnotatedLine() {
|
|
2514
|
+
i++;
|
|
2515
|
+
while (i < lines.length && lines[i].trim() === "") i++;
|
|
2516
|
+
return i < lines.length ? lines[i].trim() : null;
|
|
2517
|
+
}
|
|
2518
|
+
while (i < lines.length) {
|
|
2519
|
+
const line = lines[i].trim();
|
|
2520
|
+
const ruleMatch = RULE_ANNOTATION_RE.exec(line);
|
|
2521
|
+
if (ruleMatch) {
|
|
2522
|
+
const cssText2 = takeAnnotatedLine();
|
|
2523
|
+
if (cssText2 !== null) {
|
|
2524
|
+
rules.push({ priority: parseFloat(ruleMatch[1]), className: ruleMatch[2], cssText: cssText2 });
|
|
2525
|
+
}
|
|
2526
|
+
i++;
|
|
2527
|
+
continue;
|
|
2462
2528
|
}
|
|
2529
|
+
if (PROPERTY_ANNOTATION_RE.test(line)) {
|
|
2530
|
+
const propLine = takeAnnotatedLine();
|
|
2531
|
+
const varMatch = propLine === null ? null : PROPERTY_VAR_RE.exec(propLine);
|
|
2532
|
+
if (propLine !== null && varMatch) {
|
|
2533
|
+
properties.push({ cssText: propLine, varName: varMatch[1] });
|
|
2534
|
+
}
|
|
2535
|
+
i++;
|
|
2536
|
+
continue;
|
|
2537
|
+
}
|
|
2538
|
+
if (ARBITRARY_START_RE.test(line)) {
|
|
2539
|
+
i++;
|
|
2540
|
+
const blockLines = [];
|
|
2541
|
+
while (i < lines.length && !ARBITRARY_END_RE.test(lines[i].trim())) {
|
|
2542
|
+
blockLines.push(lines[i]);
|
|
2543
|
+
i++;
|
|
2544
|
+
}
|
|
2545
|
+
const blockText = blockLines.join("\n").trim();
|
|
2546
|
+
if (blockText.length > 0) {
|
|
2547
|
+
arbitraryCssBlocks.push({ cssText: blockText });
|
|
2548
|
+
}
|
|
2549
|
+
if (i < lines.length && ARBITRARY_END_RE.test(lines[i].trim())) {
|
|
2550
|
+
i++;
|
|
2551
|
+
}
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
i++;
|
|
2463
2555
|
}
|
|
2464
|
-
return {
|
|
2556
|
+
return { rules, properties, arbitraryCssBlocks };
|
|
2465
2557
|
}
|
|
2466
|
-
function
|
|
2467
|
-
const
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2558
|
+
function serializeTrussCss(css) {
|
|
2559
|
+
const lines = [];
|
|
2560
|
+
for (const rule of css.rules) {
|
|
2561
|
+
lines.push(`/* @truss p:${rule.priority} c:${rule.className} */`, rule.cssText);
|
|
2562
|
+
}
|
|
2563
|
+
for (const prop of css.properties) {
|
|
2564
|
+
lines.push(`/* @truss @property */`, prop.cssText);
|
|
2565
|
+
}
|
|
2566
|
+
for (const block of css.arbitraryCssBlocks) {
|
|
2567
|
+
lines.push(annotateArbitraryCssBlock(block.cssText));
|
|
2568
|
+
}
|
|
2569
|
+
return lines.join("\n");
|
|
2471
2570
|
}
|
|
2472
|
-
function
|
|
2473
|
-
|
|
2474
|
-
|
|
2571
|
+
function annotateArbitraryCssBlock(cssText) {
|
|
2572
|
+
const trimmed = cssText.trim();
|
|
2573
|
+
if (trimmed.length === 0) {
|
|
2574
|
+
return "";
|
|
2475
2575
|
}
|
|
2476
|
-
|
|
2477
|
-
const widthB = b.hi - b.lo;
|
|
2478
|
-
if (widthA !== widthB) return widthA > widthB ? -1 : 1;
|
|
2479
|
-
return a.lo - b.lo;
|
|
2576
|
+
return ["/* @truss arbitrary:start */", trimmed, "/* @truss arbitrary:end */"].join("\n");
|
|
2480
2577
|
}
|
|
2481
2578
|
|
|
2482
2579
|
// src/plugin/emit-css.ts
|
|
@@ -2538,22 +2635,28 @@ function whenSelectorFor(whenPseudo) {
|
|
|
2538
2635
|
pseudo: whenPseudo.pseudo
|
|
2539
2636
|
};
|
|
2540
2637
|
}
|
|
2541
|
-
function
|
|
2638
|
+
function generateCssData(rules) {
|
|
2542
2639
|
const sorted = sortRulesByPriority(rules.values());
|
|
2543
|
-
const
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2640
|
+
const css = {
|
|
2641
|
+
rules: sorted.map((entry) => ({
|
|
2642
|
+
priority: entry.priority,
|
|
2643
|
+
className: entry.rule.className,
|
|
2644
|
+
cssText: formatRule(entry.rule)
|
|
2645
|
+
})),
|
|
2646
|
+
properties: [],
|
|
2647
|
+
arbitraryCssBlocks: []
|
|
2648
|
+
};
|
|
2548
2649
|
for (const { rule } of sorted) {
|
|
2549
2650
|
for (const declaration of rule.declarations) {
|
|
2550
2651
|
if (declaration.cssVarName) {
|
|
2551
|
-
|
|
2552
|
-
|
|
2652
|
+
css.properties.push({
|
|
2653
|
+
varName: declaration.cssVarName,
|
|
2654
|
+
cssText: `@property ${declaration.cssVarName} { syntax: "*"; inherits: false; }`
|
|
2655
|
+
});
|
|
2553
2656
|
}
|
|
2554
2657
|
}
|
|
2555
2658
|
}
|
|
2556
|
-
return
|
|
2659
|
+
return css;
|
|
2557
2660
|
}
|
|
2558
2661
|
function formatRule(rule) {
|
|
2559
2662
|
const duplicateClassName = !!rule.mediaQuery;
|
|
@@ -2798,6 +2901,38 @@ ${body}
|
|
|
2798
2901
|
import * as t15 from "@babel/types";
|
|
2799
2902
|
import { basename } from "path";
|
|
2800
2903
|
|
|
2904
|
+
// src/plugin/test-css.ts
|
|
2905
|
+
import { parse as parse2 } from "css-tree";
|
|
2906
|
+
function createTestCssPayload(css) {
|
|
2907
|
+
const payload = {};
|
|
2908
|
+
if (css.rules.length > 0) {
|
|
2909
|
+
payload.rules = css.rules.map((rule) => {
|
|
2910
|
+
const atRule = atRulePrelude(rule.cssText);
|
|
2911
|
+
return { ...rule, ...atRule === void 0 ? {} : { atRule } };
|
|
2912
|
+
});
|
|
2913
|
+
}
|
|
2914
|
+
if (css.properties.length > 0) payload.properties = css.properties;
|
|
2915
|
+
const arbitraryRules = css.arbitraryCssBlocks.flatMap((block) => splitArbitraryCss(block.cssText));
|
|
2916
|
+
if (arbitraryRules.length > 0) payload.arbitraryRules = arbitraryRules;
|
|
2917
|
+
return payload;
|
|
2918
|
+
}
|
|
2919
|
+
function splitArbitraryCss(cssText) {
|
|
2920
|
+
const root = parse2(cssText, {
|
|
2921
|
+
context: "stylesheet",
|
|
2922
|
+
positions: true,
|
|
2923
|
+
parseRulePrelude: false,
|
|
2924
|
+
parseAtrulePrelude: false,
|
|
2925
|
+
parseValue: false
|
|
2926
|
+
});
|
|
2927
|
+
const rules = [];
|
|
2928
|
+
root.children.forEach((node) => {
|
|
2929
|
+
if (node.type === "Rule" || node.type === "Atrule") {
|
|
2930
|
+
rules.push(cssText.slice(node.loc.start.offset, node.loc.end.offset));
|
|
2931
|
+
}
|
|
2932
|
+
});
|
|
2933
|
+
return rules;
|
|
2934
|
+
}
|
|
2935
|
+
|
|
2801
2936
|
// src/plugin/emit-style-hash.ts
|
|
2802
2937
|
import * as t13 from "@babel/types";
|
|
2803
2938
|
function buildStyleHashProperties(segments, mapping, maybeIncHelperName, maybeCssVarHelperName) {
|
|
@@ -3264,7 +3399,8 @@ function transformTruss(code, filename, mapping, options = {}) {
|
|
|
3264
3399
|
if (sites.length === 0 && !hasCssPropsCall && !hasBuildtimeJsxCssAttribute) return null;
|
|
3265
3400
|
const chains = sites.map((s) => s.resolvedChain);
|
|
3266
3401
|
const { rules, needsMaybeInc, needsMaybeCssVar } = collectAtomicRules(chains, mapping);
|
|
3267
|
-
const
|
|
3402
|
+
const cssData = generateCssData(rules);
|
|
3403
|
+
const cssText = serializeTrussCss(cssData);
|
|
3268
3404
|
const runtime = createRuntimeHelpers(ast, usedTopLevelNames);
|
|
3269
3405
|
const maybeIncHelperName = needsMaybeInc ? reservePreferredName(usedTopLevelNames, "__maybeInc") : null;
|
|
3270
3406
|
const maybeCssVarHelperName = needsMaybeCssVar ? runtime.use("maybeCssVar") : null;
|
|
@@ -3310,7 +3446,9 @@ function transformTruss(code, filename, mapping, options = {}) {
|
|
|
3310
3446
|
}
|
|
3311
3447
|
if (options.injectCss && cssText.length > 0) {
|
|
3312
3448
|
declarationsToInsert.push(
|
|
3313
|
-
t15.expressionStatement(
|
|
3449
|
+
t15.expressionStatement(
|
|
3450
|
+
t15.callExpression(t15.identifier("__injectTrussCSS"), [t15.valueToNode(createTestCssPayload(cssData))])
|
|
3451
|
+
)
|
|
3314
3452
|
);
|
|
3315
3453
|
}
|
|
3316
3454
|
for (const { message, line } of errorMessages) {
|
|
@@ -3429,74 +3567,11 @@ function findLastImportLine(lines) {
|
|
|
3429
3567
|
|
|
3430
3568
|
// src/plugin/merge-css.ts
|
|
3431
3569
|
import { readFileSync as readFileSync2 } from "fs";
|
|
3432
|
-
var RULE_ANNOTATION_RE = /^\/\* @truss p:([\d.]+) c:(\S+) \*\/$/;
|
|
3433
|
-
var PROPERTY_ANNOTATION_RE = /^\/\* @truss @property \*\/$/;
|
|
3434
|
-
var ARBITRARY_START_RE = /^\/\* @truss arbitrary:start \*\/$/;
|
|
3435
|
-
var ARBITRARY_END_RE = /^\/\* @truss arbitrary:end \*\/$/;
|
|
3436
|
-
var PROPERTY_VAR_RE = /^@property\s+(--\S+)/;
|
|
3437
|
-
function parseTrussCss(cssText) {
|
|
3438
|
-
const lines = cssText.split("\n");
|
|
3439
|
-
const rules = [];
|
|
3440
|
-
const properties = [];
|
|
3441
|
-
const arbitraryCssBlocks = [];
|
|
3442
|
-
let i = 0;
|
|
3443
|
-
function takeAnnotatedLine() {
|
|
3444
|
-
i++;
|
|
3445
|
-
while (i < lines.length && lines[i].trim() === "") i++;
|
|
3446
|
-
return i < lines.length ? lines[i].trim() : null;
|
|
3447
|
-
}
|
|
3448
|
-
while (i < lines.length) {
|
|
3449
|
-
const line = lines[i].trim();
|
|
3450
|
-
const ruleMatch = RULE_ANNOTATION_RE.exec(line);
|
|
3451
|
-
if (ruleMatch) {
|
|
3452
|
-
const cssText2 = takeAnnotatedLine();
|
|
3453
|
-
if (cssText2 !== null) {
|
|
3454
|
-
rules.push({ priority: parseFloat(ruleMatch[1]), className: ruleMatch[2], cssText: cssText2 });
|
|
3455
|
-
}
|
|
3456
|
-
i++;
|
|
3457
|
-
continue;
|
|
3458
|
-
}
|
|
3459
|
-
if (PROPERTY_ANNOTATION_RE.test(line)) {
|
|
3460
|
-
const propLine = takeAnnotatedLine();
|
|
3461
|
-
const varMatch = propLine === null ? null : PROPERTY_VAR_RE.exec(propLine);
|
|
3462
|
-
if (propLine !== null && varMatch) {
|
|
3463
|
-
properties.push({ cssText: propLine, varName: varMatch[1] });
|
|
3464
|
-
}
|
|
3465
|
-
i++;
|
|
3466
|
-
continue;
|
|
3467
|
-
}
|
|
3468
|
-
if (ARBITRARY_START_RE.test(line)) {
|
|
3469
|
-
i++;
|
|
3470
|
-
const blockLines = [];
|
|
3471
|
-
while (i < lines.length && !ARBITRARY_END_RE.test(lines[i].trim())) {
|
|
3472
|
-
blockLines.push(lines[i]);
|
|
3473
|
-
i++;
|
|
3474
|
-
}
|
|
3475
|
-
const blockText = blockLines.join("\n").trim();
|
|
3476
|
-
if (blockText.length > 0) {
|
|
3477
|
-
arbitraryCssBlocks.push({ cssText: blockText });
|
|
3478
|
-
}
|
|
3479
|
-
if (i < lines.length && ARBITRARY_END_RE.test(lines[i].trim())) {
|
|
3480
|
-
i++;
|
|
3481
|
-
}
|
|
3482
|
-
continue;
|
|
3483
|
-
}
|
|
3484
|
-
i++;
|
|
3485
|
-
}
|
|
3486
|
-
return { rules, properties, arbitraryCssBlocks };
|
|
3487
|
-
}
|
|
3488
3570
|
function readTrussCss(filePath) {
|
|
3489
3571
|
const content = readFileSync2(filePath, "utf8");
|
|
3490
3572
|
return parseTrussCss(content);
|
|
3491
3573
|
}
|
|
3492
|
-
function
|
|
3493
|
-
const trimmed = cssText.trim();
|
|
3494
|
-
if (trimmed.length === 0) {
|
|
3495
|
-
return "";
|
|
3496
|
-
}
|
|
3497
|
-
return ["/* @truss arbitrary:start */", trimmed, "/* @truss arbitrary:end */"].join("\n");
|
|
3498
|
-
}
|
|
3499
|
-
function mergeTrussCss(sources) {
|
|
3574
|
+
function mergeTrussCssData(sources) {
|
|
3500
3575
|
const seenClasses = /* @__PURE__ */ new Set();
|
|
3501
3576
|
const allRules = [];
|
|
3502
3577
|
const seenProperties = /* @__PURE__ */ new Set();
|
|
@@ -3521,24 +3596,11 @@ function mergeTrussCss(sources) {
|
|
|
3521
3596
|
return { rule, key: ruleSortKey(rule.priority, rule.className, atRulePrelude(rule.cssText)) };
|
|
3522
3597
|
});
|
|
3523
3598
|
decorated.sort((a, b) => compareRuleSortKeys(a.key, b.key));
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
}
|
|
3529
|
-
for (const prop of allProperties) {
|
|
3530
|
-
lines.push(`/* @truss @property */`);
|
|
3531
|
-
lines.push(prop.cssText);
|
|
3532
|
-
}
|
|
3533
|
-
for (const block of allArbitraryCssBlocks) {
|
|
3534
|
-
lines.push(annotateArbitraryCssBlock(block.cssText));
|
|
3535
|
-
}
|
|
3536
|
-
return lines.join("\n");
|
|
3537
|
-
}
|
|
3538
|
-
function atRulePrelude(cssText) {
|
|
3539
|
-
if (!cssText.startsWith("@")) return void 0;
|
|
3540
|
-
const brace = cssText.indexOf("{");
|
|
3541
|
-
return brace === -1 ? void 0 : cssText.slice(0, brace).trim();
|
|
3599
|
+
return {
|
|
3600
|
+
rules: decorated.map((entry) => entry.rule),
|
|
3601
|
+
properties: allProperties,
|
|
3602
|
+
arbitraryCssBlocks: allArbitraryCssBlocks
|
|
3603
|
+
};
|
|
3542
3604
|
}
|
|
3543
3605
|
|
|
3544
3606
|
// src/plugin/transform-session.ts
|
|
@@ -3569,6 +3631,7 @@ function createTrussTransformSession(options) {
|
|
|
3569
3631
|
libraryCache = null;
|
|
3570
3632
|
}
|
|
3571
3633
|
function updateArbitraryCssRegistry(sourcePath, sourceCode) {
|
|
3634
|
+
sourcePath = resolve2(sourcePath).replace(/\\/g, "/");
|
|
3572
3635
|
const css = transformCssTs(sourceCode, sourcePath, ensureMapping()).trim();
|
|
3573
3636
|
if (css.length > 0) {
|
|
3574
3637
|
const prev = arbitraryCssRegistry.get(sourcePath);
|
|
@@ -3597,12 +3660,11 @@ function createTrussTransformSession(options) {
|
|
|
3597
3660
|
}
|
|
3598
3661
|
function collectCss() {
|
|
3599
3662
|
const mapping2 = ensureMapping();
|
|
3600
|
-
const
|
|
3601
|
-
const allArbitrary = Array.from(arbitraryCssRegistry.
|
|
3602
|
-
|
|
3603
|
-
const appCss = appCssParts.filter((part) => part.length > 0).join("\n");
|
|
3663
|
+
const appCss = generateCssData(cssRegistry);
|
|
3664
|
+
const allArbitrary = Array.from(arbitraryCssRegistry.entries()).sort((a, b) => compareClassNames(a[0], b[0])).map((entry) => entry[1]).join("\n\n");
|
|
3665
|
+
if (allArbitrary.length > 0) appCss.arbitraryCssBlocks.push({ cssText: allArbitrary });
|
|
3604
3666
|
const libs = loadLibraries();
|
|
3605
|
-
const body = libs.length === 0 ? appCss :
|
|
3667
|
+
const body = serializeTrussCss(libs.length === 0 ? appCss : mergeTrussCssData([...libs, appCss]));
|
|
3606
3668
|
if (body.length === 0) return "";
|
|
3607
3669
|
return `${rootSpacingPreludeCss(mapping2.increment)}
|
|
3608
3670
|
${body}`;
|
|
@@ -3610,8 +3672,16 @@ ${body}`;
|
|
|
3610
3672
|
function hasCss() {
|
|
3611
3673
|
return cssRegistry.size > 0 || arbitraryCssRegistry.size > 0 || libraryPaths.length > 0;
|
|
3612
3674
|
}
|
|
3675
|
+
function collectTestCss() {
|
|
3676
|
+
return createTestCssPayload(mergeTrussCssData(loadLibraries()));
|
|
3677
|
+
}
|
|
3678
|
+
function getArbitraryCss(sourcePath) {
|
|
3679
|
+
return arbitraryCssRegistry.get(resolve2(sourcePath).replace(/\\/g, "/")) ?? "";
|
|
3680
|
+
}
|
|
3613
3681
|
return {
|
|
3614
3682
|
collectCss,
|
|
3683
|
+
collectTestCss,
|
|
3684
|
+
getArbitraryCss,
|
|
3615
3685
|
ensureMapping,
|
|
3616
3686
|
hasCss,
|
|
3617
3687
|
reset,
|
|
@@ -3620,6 +3690,9 @@ ${body}`;
|
|
|
3620
3690
|
};
|
|
3621
3691
|
}
|
|
3622
3692
|
|
|
3693
|
+
// src/plugin/index.ts
|
|
3694
|
+
import * as t16 from "@babel/types";
|
|
3695
|
+
|
|
3623
3696
|
// src/plugin/esbuild-plugin.ts
|
|
3624
3697
|
import { readFileSync as readFileSync3, writeFileSync, mkdirSync } from "fs";
|
|
3625
3698
|
import { resolve as resolve3, join } from "path";
|
|
@@ -3667,7 +3740,10 @@ function loaderForPath(filePath) {
|
|
|
3667
3740
|
|
|
3668
3741
|
// src/plugin/index.ts
|
|
3669
3742
|
var VIRTUAL_CSS_PREFIX = "\0truss-css:";
|
|
3743
|
+
var VIRTUAL_TEST_CSS_PREFIX = "\0truss-test-css:";
|
|
3670
3744
|
var CSS_TS_QUERY = "?truss-css";
|
|
3745
|
+
var RUNTIME_MODULE2 = "@homebound/truss/runtime";
|
|
3746
|
+
var INJECT_CSS_HELPER = "__injectTrussCSS";
|
|
3671
3747
|
var TRUSS_CSS_PLACEHOLDER = "__TRUSS_CSS_HASH__";
|
|
3672
3748
|
var VIRTUAL_CSS_ENDPOINT = "/virtual:truss.css";
|
|
3673
3749
|
var VIRTUAL_RUNTIME_ID = "virtual:truss:runtime";
|
|
@@ -3756,6 +3832,7 @@ function trussPlugin(opts) {
|
|
|
3756
3832
|
if (!source.endsWith(CSS_TS_QUERY)) return null;
|
|
3757
3833
|
const absolutePath = resolveImportPath(source.slice(0, -CSS_TS_QUERY.length), importer, projectRoot);
|
|
3758
3834
|
if (!existsSync2(absolutePath)) return null;
|
|
3835
|
+
if (isTest) return VIRTUAL_TEST_CSS_PREFIX + absolutePath;
|
|
3759
3836
|
return VIRTUAL_CSS_PREFIX + absolutePath.slice(0, -3);
|
|
3760
3837
|
},
|
|
3761
3838
|
load(id) {
|
|
@@ -3789,11 +3866,30 @@ function trussPlugin(opts) {
|
|
|
3789
3866
|
`;
|
|
3790
3867
|
}
|
|
3791
3868
|
if (id === RESOLVED_VIRTUAL_TEST_CSS_ID) {
|
|
3792
|
-
const
|
|
3869
|
+
const payload = {
|
|
3870
|
+
...session.collectTestCss(),
|
|
3871
|
+
source: "libraries",
|
|
3872
|
+
order: 0,
|
|
3873
|
+
prelude: rootSpacingPreludeCss(session.ensureMapping().increment)
|
|
3874
|
+
};
|
|
3793
3875
|
return `
|
|
3794
3876
|
import { __injectTrussCSS } from "@homebound/truss/runtime";
|
|
3795
3877
|
|
|
3796
|
-
__injectTrussCSS(${JSON.stringify(
|
|
3878
|
+
__injectTrussCSS(${JSON.stringify(payload)});
|
|
3879
|
+
`;
|
|
3880
|
+
}
|
|
3881
|
+
if (id.startsWith(VIRTUAL_TEST_CSS_PREFIX)) {
|
|
3882
|
+
const sourcePath2 = canonicalSourcePath(id.slice(VIRTUAL_TEST_CSS_PREFIX.length));
|
|
3883
|
+
session.updateArbitraryCssRegistry(sourcePath2, readFileSync4(sourcePath2, "utf8"));
|
|
3884
|
+
const payload = {
|
|
3885
|
+
arbitraryRules: splitArbitraryCss(session.getArbitraryCss(sourcePath2)),
|
|
3886
|
+
source: sourcePath2
|
|
3887
|
+
};
|
|
3888
|
+
return `
|
|
3889
|
+
import "${VIRTUAL_TEST_CSS_ID}";
|
|
3890
|
+
import { __injectTrussCSS } from "@homebound/truss/runtime";
|
|
3891
|
+
|
|
3892
|
+
__injectTrussCSS(${JSON.stringify(payload)});
|
|
3797
3893
|
`;
|
|
3798
3894
|
}
|
|
3799
3895
|
if (!id.startsWith(VIRTUAL_CSS_PREFIX)) return null;
|
|
@@ -3803,16 +3899,21 @@ __injectTrussCSS(${JSON.stringify(css)});
|
|
|
3803
3899
|
return `/* [truss] ${sourcePath} \u2014 included via truss.css */`;
|
|
3804
3900
|
},
|
|
3805
3901
|
transform(code, id) {
|
|
3902
|
+
if (id.startsWith(VIRTUAL_TEST_CSS_PREFIX)) return null;
|
|
3806
3903
|
if (!/\.[cm]?[jt]sx?(\?|$)/.test(id)) return null;
|
|
3807
3904
|
const fileId = stripQueryAndHash(id);
|
|
3808
3905
|
if (isNodeModulesFile(fileId)) return null;
|
|
3809
3906
|
const rewrittenImports = rewriteCssTsImports(code, id);
|
|
3810
|
-
const shouldBootstrapTestCss = isTest
|
|
3907
|
+
const shouldBootstrapTestCss = isTest;
|
|
3811
3908
|
const transformedCode = shouldBootstrapTestCss ? `${rewrittenImports.code}
|
|
3812
3909
|
import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
|
|
3813
3910
|
const importsOnlyResult = rewrittenImports.changed || shouldBootstrapTestCss ? { code: transformedCode, map: null } : null;
|
|
3814
3911
|
if (fileId.endsWith(".css.ts")) {
|
|
3815
3912
|
session.updateArbitraryCssRegistry(fileId, code);
|
|
3913
|
+
if (isTest) {
|
|
3914
|
+
const css = session.getArbitraryCss(fileId);
|
|
3915
|
+
return { code: appendTestCssInjection(transformedCode, fileId, css), map: null };
|
|
3916
|
+
}
|
|
3816
3917
|
return importsOnlyResult;
|
|
3817
3918
|
}
|
|
3818
3919
|
const hasCssDsl = rewrittenImports.code.includes("Css") || rewrittenImports.code.includes("css=");
|
|
@@ -3873,6 +3974,30 @@ function stripQueryAndHash(id) {
|
|
|
3873
3974
|
function isNodeModulesFile(filePath) {
|
|
3874
3975
|
return filePath.replace(/\\/g, "/").includes("/node_modules/");
|
|
3875
3976
|
}
|
|
3977
|
+
function canonicalSourcePath(filePath) {
|
|
3978
|
+
return resolve4(filePath).replace(/\\/g, "/");
|
|
3979
|
+
}
|
|
3980
|
+
function appendTestCssInjection(code, fileId, css) {
|
|
3981
|
+
const ast = parseModule(code, fileId);
|
|
3982
|
+
let usedTopLevelNames = /* @__PURE__ */ new Set();
|
|
3983
|
+
traverse(ast, {
|
|
3984
|
+
Program(path) {
|
|
3985
|
+
usedTopLevelNames = new Set(Object.keys(path.scope.bindings));
|
|
3986
|
+
path.stop();
|
|
3987
|
+
}
|
|
3988
|
+
});
|
|
3989
|
+
const existing = findNamedImportBinding(ast, INJECT_CSS_HELPER, RUNTIME_MODULE2);
|
|
3990
|
+
const localName = existing ?? reservePreferredName(usedTopLevelNames, INJECT_CSS_HELPER);
|
|
3991
|
+
if (!existing) upsertNamedImports(ast, RUNTIME_MODULE2, [{ importedName: INJECT_CSS_HELPER, localName }]);
|
|
3992
|
+
ast.program.body.push(
|
|
3993
|
+
t16.expressionStatement(
|
|
3994
|
+
t16.callExpression(t16.identifier(localName), [
|
|
3995
|
+
t16.valueToNode({ arbitraryRules: splitArbitraryCss(css), source: canonicalSourcePath(fileId) })
|
|
3996
|
+
])
|
|
3997
|
+
)
|
|
3998
|
+
);
|
|
3999
|
+
return generate(ast, { sourceFileName: fileId }).code;
|
|
4000
|
+
}
|
|
3876
4001
|
export {
|
|
3877
4002
|
loadMapping,
|
|
3878
4003
|
trussEsbuildPlugin,
|