@homebound/truss 2.29.9 → 2.29.11
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 +7 -2
- package/build/plugin/index.js +114 -27
- package/build/plugin/index.js.map +1 -1
- package/package.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
package/build/plugin/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PluginContext } from 'rollup';
|
|
2
|
+
|
|
1
3
|
/** The shape of the Css.json mapping file consumed by the Vite plugin. */
|
|
2
4
|
interface TrussMapping {
|
|
3
5
|
increment: number;
|
|
@@ -91,15 +93,18 @@ interface TrussPluginOptions {
|
|
|
91
93
|
mapping: string;
|
|
92
94
|
/** Paths to pre-compiled truss.css files from libraries to merge into the app's CSS. */
|
|
93
95
|
libraries?: string[];
|
|
96
|
+
/** Unsupported patterns fail builds by default; dev transforms warn and keep serving the page. */
|
|
97
|
+
unsupportedPattern?: "error" | "warn";
|
|
94
98
|
}
|
|
99
|
+
type DiagnosticContext = Pick<PluginContext, "warn">;
|
|
95
100
|
interface TrussVitePlugin {
|
|
96
101
|
name: string;
|
|
97
102
|
enforce?: "pre" | "post";
|
|
98
103
|
configResolved?: (config: any) => void;
|
|
99
104
|
buildStart?: () => void;
|
|
100
105
|
resolveId?: (source: string, importer: string | undefined) => string | null;
|
|
101
|
-
load?: (id: string) => string | null;
|
|
102
|
-
transform?: (code: string, id: string) => {
|
|
106
|
+
load?: (this: DiagnosticContext, id: string) => string | null;
|
|
107
|
+
transform?: (this: DiagnosticContext, code: string, id: string) => {
|
|
103
108
|
code: string;
|
|
104
109
|
map: any;
|
|
105
110
|
} | null;
|
package/build/plugin/index.js
CHANGED
|
@@ -330,11 +330,44 @@ function resetConditionContext(context) {
|
|
|
330
330
|
Object.assign(context, emptyConditionContext());
|
|
331
331
|
}
|
|
332
332
|
|
|
333
|
+
// src/plugin/unknown-abbreviation.ts
|
|
334
|
+
var UnknownAbbreviationError = class extends UnsupportedPatternError {
|
|
335
|
+
constructor(abbreviation, candidates) {
|
|
336
|
+
const suggestion = closestAbbreviation(abbreviation, candidates);
|
|
337
|
+
super(`Unknown abbreviation "${abbreviation}"${suggestion ? `. Did you mean "${suggestion}"?` : ""}`);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
function closestAbbreviation(abbreviation, candidates) {
|
|
341
|
+
let closest;
|
|
342
|
+
let bestDistance = 3;
|
|
343
|
+
for (const candidate of candidates) {
|
|
344
|
+
if (Math.abs(candidate.length - abbreviation.length) >= bestDistance) continue;
|
|
345
|
+
let previous = Array.from({ length: candidate.length + 1 }, (_, index) => index);
|
|
346
|
+
for (let i = 1; i <= abbreviation.length; i++) {
|
|
347
|
+
const current = [i];
|
|
348
|
+
for (let j = 1; j <= candidate.length; j++) {
|
|
349
|
+
current[j] = Math.min(
|
|
350
|
+
current[j - 1] + 1,
|
|
351
|
+
previous[j] + 1,
|
|
352
|
+
previous[j - 1] + (abbreviation[i - 1] === candidate[j - 1] ? 0 : 1)
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
previous = current;
|
|
356
|
+
}
|
|
357
|
+
const distance = previous[candidate.length];
|
|
358
|
+
if (distance < bestDistance) {
|
|
359
|
+
closest = candidate;
|
|
360
|
+
bestDistance = distance;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return closest;
|
|
364
|
+
}
|
|
365
|
+
|
|
333
366
|
// src/plugin/resolve-entry.ts
|
|
334
367
|
function requireEntry(mapping, abbr) {
|
|
335
368
|
const entry = mapping.abbreviations[abbr];
|
|
336
369
|
if (!entry) {
|
|
337
|
-
throw new
|
|
370
|
+
throw new UnknownAbbreviationError(abbr, Object.keys(mapping.abbreviations));
|
|
338
371
|
}
|
|
339
372
|
return entry;
|
|
340
373
|
}
|
|
@@ -2553,16 +2586,18 @@ function parseTrussCss(cssText) {
|
|
|
2553
2586
|
}
|
|
2554
2587
|
return { rules, properties, arbitraryCssBlocks };
|
|
2555
2588
|
}
|
|
2556
|
-
function serializeTrussCss(css) {
|
|
2589
|
+
function serializeTrussCss(css, annotate = true) {
|
|
2557
2590
|
const lines = [];
|
|
2558
2591
|
for (const rule of css.rules) {
|
|
2559
|
-
lines.push(`/* @truss p:${rule.priority} c:${rule.className}
|
|
2592
|
+
if (annotate) lines.push(`/* @truss p:${rule.priority} c:${rule.className} */`);
|
|
2593
|
+
lines.push(rule.cssText);
|
|
2560
2594
|
}
|
|
2561
2595
|
for (const prop of css.properties) {
|
|
2562
|
-
lines.push(`/* @truss @property
|
|
2596
|
+
if (annotate) lines.push(`/* @truss @property */`);
|
|
2597
|
+
lines.push(prop.cssText);
|
|
2563
2598
|
}
|
|
2564
2599
|
for (const block of css.arbitraryCssBlocks) {
|
|
2565
|
-
lines.push(annotateArbitraryCssBlock(block.cssText));
|
|
2600
|
+
lines.push(annotate ? annotateArbitraryCssBlock(block.cssText) : block.cssText.trim());
|
|
2566
2601
|
}
|
|
2567
2602
|
return lines.join("\n");
|
|
2568
2603
|
}
|
|
@@ -2733,12 +2768,28 @@ function getTopLevelVariableDeclaration(node) {
|
|
|
2733
2768
|
return null;
|
|
2734
2769
|
}
|
|
2735
2770
|
|
|
2771
|
+
// src/plugin/diagnostic.ts
|
|
2772
|
+
var Diagnostic = class extends Error {
|
|
2773
|
+
id;
|
|
2774
|
+
loc;
|
|
2775
|
+
constructor(message, filename, node) {
|
|
2776
|
+
const start = node.loc?.start;
|
|
2777
|
+
const location = start ? `${filename}:${start.line}:${start.column + 1}` : filename;
|
|
2778
|
+
super(`${location}: ${message}`);
|
|
2779
|
+
this.id = filename;
|
|
2780
|
+
this.loc = start ? { file: filename, line: start.line, column: start.column } : void 0;
|
|
2781
|
+
}
|
|
2782
|
+
};
|
|
2783
|
+
|
|
2736
2784
|
// src/plugin/transform-css.ts
|
|
2737
|
-
function transformCssTs(code, filename, mapping) {
|
|
2785
|
+
function transformCssTs(code, filename, mapping, options = {}) {
|
|
2738
2786
|
const ast = parseModule(code, filename);
|
|
2739
2787
|
const cssBindingName = findCssImportBinding(ast);
|
|
2740
2788
|
const cssExport = findNamedCssExportObject(ast);
|
|
2741
2789
|
if (!cssExport) {
|
|
2790
|
+
options.onDiagnostic?.(
|
|
2791
|
+
new Diagnostic("expected `export const css = { ... }` with an object literal", filename, ast.program)
|
|
2792
|
+
);
|
|
2742
2793
|
return `/* [truss] ${filename}: expected \`export const css = { ... }\` with an object literal */
|
|
2743
2794
|
`;
|
|
2744
2795
|
}
|
|
@@ -2746,16 +2797,16 @@ function transformCssTs(code, filename, mapping) {
|
|
|
2746
2797
|
const stringBindings = collectStaticStringBindings(ast);
|
|
2747
2798
|
for (const prop of cssExport.properties) {
|
|
2748
2799
|
if (t12.isSpreadElement(prop)) {
|
|
2749
|
-
rules.push(
|
|
2800
|
+
rules.push(unsupported("spread elements in css.ts export", prop));
|
|
2750
2801
|
continue;
|
|
2751
2802
|
}
|
|
2752
2803
|
if (!t12.isObjectProperty(prop)) {
|
|
2753
|
-
rules.push(
|
|
2804
|
+
rules.push(unsupported("non-property in css.ts export", prop));
|
|
2754
2805
|
continue;
|
|
2755
2806
|
}
|
|
2756
2807
|
const selector = objectPropertyStringKey(prop, stringBindings);
|
|
2757
2808
|
if (selector === null) {
|
|
2758
|
-
rules.push(
|
|
2809
|
+
rules.push(unsupported("non-string-literal key in css.ts export", prop));
|
|
2759
2810
|
continue;
|
|
2760
2811
|
}
|
|
2761
2812
|
const valueNode = prop.value;
|
|
@@ -2765,21 +2816,25 @@ function transformCssTs(code, filename, mapping) {
|
|
|
2765
2816
|
continue;
|
|
2766
2817
|
}
|
|
2767
2818
|
if (!t12.isExpression(valueNode)) {
|
|
2768
|
-
rules.push(
|
|
2819
|
+
rules.push(unsupported(`"${selector}" value is not an expression`, valueNode));
|
|
2769
2820
|
continue;
|
|
2770
2821
|
}
|
|
2771
2822
|
if (!cssBindingName) {
|
|
2772
|
-
rules.push(
|
|
2823
|
+
rules.push(unsupported(`"${selector}" \u2014 Css.*.$ chain requires a Css import`, valueNode));
|
|
2773
2824
|
continue;
|
|
2774
2825
|
}
|
|
2775
|
-
const cssResult = resolveCssExpression(valueNode, cssBindingName, mapping
|
|
2826
|
+
const cssResult = resolveCssExpression(valueNode, cssBindingName, mapping);
|
|
2776
2827
|
if ("error" in cssResult) {
|
|
2777
|
-
rules.push(
|
|
2828
|
+
rules.push(unsupported(`"${selector}" \u2014 ${cssResult.error}`, valueNode));
|
|
2778
2829
|
continue;
|
|
2779
2830
|
}
|
|
2780
2831
|
rules.push(formatCssRule(selector, cssResult.declarations));
|
|
2781
2832
|
}
|
|
2782
2833
|
return rules.join("\n\n") + "\n";
|
|
2834
|
+
function unsupported(message, node) {
|
|
2835
|
+
options.onDiagnostic?.(new Diagnostic(message, filename, node));
|
|
2836
|
+
return `/* [truss] unsupported: ${message} */`;
|
|
2837
|
+
}
|
|
2783
2838
|
}
|
|
2784
2839
|
function findNamedCssExportObject(ast) {
|
|
2785
2840
|
for (const node of ast.program.body) {
|
|
@@ -2809,7 +2864,7 @@ function extractStaticStringValue(node, cssBindingName) {
|
|
|
2809
2864
|
}
|
|
2810
2865
|
return null;
|
|
2811
2866
|
}
|
|
2812
|
-
function resolveCssExpression(node, cssBindingName, mapping
|
|
2867
|
+
function resolveCssExpression(node, cssBindingName, mapping) {
|
|
2813
2868
|
const chain = extractDollarChain(node, cssBindingName);
|
|
2814
2869
|
if (!chain) {
|
|
2815
2870
|
return { error: "value must be a Css.*.$ expression" };
|
|
@@ -3375,10 +3430,18 @@ function transformTruss(code, filename, mapping, options = {}) {
|
|
|
3375
3430
|
return;
|
|
3376
3431
|
}
|
|
3377
3432
|
const resolveCssChainReference2 = buildCssChainReferenceResolver(path, cssBindingName);
|
|
3378
|
-
const resolvedChain = resolveFullChain(
|
|
3433
|
+
const resolvedChain = resolveFullChain(
|
|
3434
|
+
{
|
|
3435
|
+
mapping,
|
|
3436
|
+
cssBindingName,
|
|
3437
|
+
resolveCssChainReference: resolveCssChainReference2
|
|
3438
|
+
},
|
|
3439
|
+
chain
|
|
3440
|
+
);
|
|
3379
3441
|
sites.push({ path, resolvedChain });
|
|
3380
3442
|
const line = path.node.loc?.start.line ?? null;
|
|
3381
3443
|
for (const err of resolvedChain.errors) {
|
|
3444
|
+
options.onDiagnostic?.(new Diagnostic(err, filename, path.node));
|
|
3382
3445
|
errorMessages.push({ message: err, line });
|
|
3383
3446
|
}
|
|
3384
3447
|
},
|
|
@@ -3628,9 +3691,9 @@ function createTrussTransformSession(options) {
|
|
|
3628
3691
|
arbitraryCssRegistry.clear();
|
|
3629
3692
|
libraryCache = null;
|
|
3630
3693
|
}
|
|
3631
|
-
function updateArbitraryCssRegistry(sourcePath, sourceCode) {
|
|
3694
|
+
function updateArbitraryCssRegistry(sourcePath, sourceCode, diagnostics = {}) {
|
|
3632
3695
|
sourcePath = resolve2(sourcePath).replace(/\\/g, "/");
|
|
3633
|
-
const css = transformCssTs(sourceCode, sourcePath, ensureMapping()).trim();
|
|
3696
|
+
const css = transformCssTs(sourceCode, sourcePath, ensureMapping(), diagnostics).trim();
|
|
3634
3697
|
if (css.length > 0) {
|
|
3635
3698
|
const prev = arbitraryCssRegistry.get(sourcePath);
|
|
3636
3699
|
arbitraryCssRegistry.set(sourcePath, css);
|
|
@@ -3656,13 +3719,13 @@ function createTrussTransformSession(options) {
|
|
|
3656
3719
|
}
|
|
3657
3720
|
return result;
|
|
3658
3721
|
}
|
|
3659
|
-
function collectCss() {
|
|
3722
|
+
function collectCss(annotate = true) {
|
|
3660
3723
|
const mapping2 = ensureMapping();
|
|
3661
3724
|
const appCss = generateCssData(cssRegistry);
|
|
3662
3725
|
const allArbitrary = Array.from(arbitraryCssRegistry.entries()).sort((a, b) => compareClassNames(a[0], b[0])).map((entry) => entry[1]).join("\n\n");
|
|
3663
3726
|
if (allArbitrary.length > 0) appCss.arbitraryCssBlocks.push({ cssText: allArbitrary });
|
|
3664
3727
|
const libs = loadLibraries();
|
|
3665
|
-
const body = serializeTrussCss(libs.length === 0 ? appCss : mergeTrussCssData([...libs, appCss]));
|
|
3728
|
+
const body = serializeTrussCss(libs.length === 0 ? appCss : mergeTrussCssData([...libs, appCss]), annotate);
|
|
3666
3729
|
if (body.length === 0) return "";
|
|
3667
3730
|
return `${rootSpacingPreludeCss(mapping2.increment)}
|
|
3668
3731
|
${body}`;
|
|
@@ -3695,6 +3758,11 @@ import * as t16 from "@babel/types";
|
|
|
3695
3758
|
import { readFileSync as readFileSync3, writeFileSync, mkdirSync } from "fs";
|
|
3696
3759
|
import { resolve as resolve3, join } from "path";
|
|
3697
3760
|
function trussEsbuildPlugin(opts) {
|
|
3761
|
+
const diagnostics = {
|
|
3762
|
+
onDiagnostic(error) {
|
|
3763
|
+
throw error;
|
|
3764
|
+
}
|
|
3765
|
+
};
|
|
3698
3766
|
const session = createTrussTransformSession({
|
|
3699
3767
|
mappingPath() {
|
|
3700
3768
|
return resolve3(process.cwd(), opts.mapping);
|
|
@@ -3710,11 +3778,11 @@ function trussEsbuildPlugin(opts) {
|
|
|
3710
3778
|
build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, (args) => {
|
|
3711
3779
|
const code = readFileSync3(args.path, "utf8");
|
|
3712
3780
|
if (args.path.endsWith(".css.ts")) {
|
|
3713
|
-
session.updateArbitraryCssRegistry(args.path, code);
|
|
3781
|
+
session.updateArbitraryCssRegistry(args.path, code, diagnostics);
|
|
3714
3782
|
return { contents: code, loader: loaderForPath(args.path) };
|
|
3715
3783
|
}
|
|
3716
3784
|
if (!code.includes("Css") && !code.includes("css=")) return void 0;
|
|
3717
|
-
const result = session.transformCode(code, args.path);
|
|
3785
|
+
const result = session.transformCode(code, args.path, diagnostics);
|
|
3718
3786
|
if (!result) return void 0;
|
|
3719
3787
|
return { contents: result.code, loader: loaderForPath(args.path) };
|
|
3720
3788
|
});
|
|
@@ -3753,6 +3821,8 @@ function trussPlugin(opts) {
|
|
|
3753
3821
|
let debug = false;
|
|
3754
3822
|
let isTest = false;
|
|
3755
3823
|
let isBuild = false;
|
|
3824
|
+
let annotate = true;
|
|
3825
|
+
let devSocket;
|
|
3756
3826
|
const libraryPaths = opts.libraries ?? [];
|
|
3757
3827
|
let emittedCssFileName = null;
|
|
3758
3828
|
let cssVersion = 0;
|
|
@@ -3760,6 +3830,11 @@ function trussPlugin(opts) {
|
|
|
3760
3830
|
function mappingPath() {
|
|
3761
3831
|
return resolve4(projectRoot || process.cwd(), opts.mapping);
|
|
3762
3832
|
}
|
|
3833
|
+
function diagnostics(context) {
|
|
3834
|
+
return {
|
|
3835
|
+
onDiagnostic: (error) => reportDiagnostic(context, error)
|
|
3836
|
+
};
|
|
3837
|
+
}
|
|
3763
3838
|
const session = createTrussTransformSession({
|
|
3764
3839
|
mappingPath,
|
|
3765
3840
|
projectRoot: () => projectRoot || process.cwd(),
|
|
@@ -3776,6 +3851,7 @@ function trussPlugin(opts) {
|
|
|
3776
3851
|
debug = config.command === "serve" || config.mode === "development" || config.mode === "test";
|
|
3777
3852
|
isTest = config.mode === "test";
|
|
3778
3853
|
isBuild = config.command === "build";
|
|
3854
|
+
annotate = !isBuild || Boolean(config.build?.lib);
|
|
3779
3855
|
},
|
|
3780
3856
|
buildStart() {
|
|
3781
3857
|
session.ensureMapping();
|
|
@@ -3786,9 +3862,10 @@ function trussPlugin(opts) {
|
|
|
3786
3862
|
// -- Dev mode HMR --
|
|
3787
3863
|
configureServer(server) {
|
|
3788
3864
|
if (isTest) return;
|
|
3865
|
+
devSocket = server.ws;
|
|
3789
3866
|
server.middlewares.use((req, res, next) => {
|
|
3790
3867
|
if (req.url !== VIRTUAL_CSS_ENDPOINT) return next();
|
|
3791
|
-
const css = session.collectCss();
|
|
3868
|
+
const css = session.collectCss(annotate);
|
|
3792
3869
|
res.setHeader("Content-Type", "text/css");
|
|
3793
3870
|
res.setHeader("Cache-Control", "no-store");
|
|
3794
3871
|
res.end(css);
|
|
@@ -3878,7 +3955,7 @@ __injectTrussCSS(${JSON.stringify(payload)});
|
|
|
3878
3955
|
}
|
|
3879
3956
|
if (id.startsWith(VIRTUAL_TEST_CSS_PREFIX)) {
|
|
3880
3957
|
const sourcePath2 = canonicalSourcePath(id.slice(VIRTUAL_TEST_CSS_PREFIX.length));
|
|
3881
|
-
session.updateArbitraryCssRegistry(sourcePath2, readFileSync4(sourcePath2, "utf8"));
|
|
3958
|
+
session.updateArbitraryCssRegistry(sourcePath2, readFileSync4(sourcePath2, "utf8"), diagnostics(this));
|
|
3882
3959
|
const payload = {
|
|
3883
3960
|
arbitraryRules: splitArbitraryCss(session.getArbitraryCss(sourcePath2)),
|
|
3884
3961
|
source: sourcePath2
|
|
@@ -3893,7 +3970,7 @@ __injectTrussCSS(${JSON.stringify(payload)});
|
|
|
3893
3970
|
if (!id.startsWith(VIRTUAL_CSS_PREFIX)) return null;
|
|
3894
3971
|
const sourcePath = id.slice(VIRTUAL_CSS_PREFIX.length) + ".ts";
|
|
3895
3972
|
const sourceCode = readFileSync4(sourcePath, "utf8");
|
|
3896
|
-
session.updateArbitraryCssRegistry(sourcePath, sourceCode);
|
|
3973
|
+
session.updateArbitraryCssRegistry(sourcePath, sourceCode, diagnostics(this));
|
|
3897
3974
|
return `/* [truss] ${sourcePath} \u2014 included via truss.css */`;
|
|
3898
3975
|
},
|
|
3899
3976
|
transform(code, id) {
|
|
@@ -3907,7 +3984,7 @@ __injectTrussCSS(${JSON.stringify(payload)});
|
|
|
3907
3984
|
import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
|
|
3908
3985
|
const importsOnlyResult = rewrittenImports.changed || shouldBootstrapTestCss ? { code: transformedCode, map: null } : null;
|
|
3909
3986
|
if (fileId.endsWith(".css.ts")) {
|
|
3910
|
-
session.updateArbitraryCssRegistry(fileId, code);
|
|
3987
|
+
session.updateArbitraryCssRegistry(fileId, code, diagnostics(this));
|
|
3911
3988
|
if (isTest) {
|
|
3912
3989
|
const css = session.getArbitraryCss(fileId);
|
|
3913
3990
|
return { code: appendTestCssInjection(transformedCode, fileId, css), map: null };
|
|
@@ -3916,13 +3993,13 @@ import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
|
|
|
3916
3993
|
}
|
|
3917
3994
|
const hasCssDsl = rewrittenImports.code.includes("Css") || rewrittenImports.code.includes("css=");
|
|
3918
3995
|
if (!hasCssDsl) return importsOnlyResult;
|
|
3919
|
-
const result = session.transformCode(transformedCode, fileId, { debug, injectCss: isTest });
|
|
3996
|
+
const result = session.transformCode(transformedCode, fileId, { debug, injectCss: isTest, ...diagnostics(this) });
|
|
3920
3997
|
return result ? { code: result.code, map: result.map } : importsOnlyResult;
|
|
3921
3998
|
},
|
|
3922
3999
|
// -- Production CSS emission --
|
|
3923
4000
|
generateBundle(_options, _bundle) {
|
|
3924
4001
|
if (!isBuild) return;
|
|
3925
|
-
const css = session.collectCss();
|
|
4002
|
+
const css = session.collectCss(annotate);
|
|
3926
4003
|
if (!css) return;
|
|
3927
4004
|
const hash = createHash("sha256").update(css).digest("hex").slice(0, 8);
|
|
3928
4005
|
const fileName = `assets/truss-${hash}.css`;
|
|
@@ -3947,6 +4024,16 @@ import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
|
|
|
3947
4024
|
}
|
|
3948
4025
|
}
|
|
3949
4026
|
};
|
|
4027
|
+
function reportDiagnostic(context, error) {
|
|
4028
|
+
if ((opts.unsupportedPattern ?? (isBuild ? "error" : "warn")) === "error") {
|
|
4029
|
+
throw error;
|
|
4030
|
+
}
|
|
4031
|
+
context.warn(error);
|
|
4032
|
+
devSocket?.send({
|
|
4033
|
+
type: "error",
|
|
4034
|
+
err: { message: error.message, stack: "", id: error.id, loc: error.loc, plugin: "truss" }
|
|
4035
|
+
});
|
|
4036
|
+
}
|
|
3950
4037
|
}
|
|
3951
4038
|
function resolveImportPath(source, importer, projectRoot) {
|
|
3952
4039
|
if (isAbsolute(source)) {
|