@homebound/truss 2.29.9 → 2.29.10
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 +102 -19
- 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
|
}
|
|
@@ -2733,12 +2766,28 @@ function getTopLevelVariableDeclaration(node) {
|
|
|
2733
2766
|
return null;
|
|
2734
2767
|
}
|
|
2735
2768
|
|
|
2769
|
+
// src/plugin/diagnostic.ts
|
|
2770
|
+
var Diagnostic = class extends Error {
|
|
2771
|
+
id;
|
|
2772
|
+
loc;
|
|
2773
|
+
constructor(message, filename, node) {
|
|
2774
|
+
const start = node.loc?.start;
|
|
2775
|
+
const location = start ? `${filename}:${start.line}:${start.column + 1}` : filename;
|
|
2776
|
+
super(`${location}: ${message}`);
|
|
2777
|
+
this.id = filename;
|
|
2778
|
+
this.loc = start ? { file: filename, line: start.line, column: start.column } : void 0;
|
|
2779
|
+
}
|
|
2780
|
+
};
|
|
2781
|
+
|
|
2736
2782
|
// src/plugin/transform-css.ts
|
|
2737
|
-
function transformCssTs(code, filename, mapping) {
|
|
2783
|
+
function transformCssTs(code, filename, mapping, options = {}) {
|
|
2738
2784
|
const ast = parseModule(code, filename);
|
|
2739
2785
|
const cssBindingName = findCssImportBinding(ast);
|
|
2740
2786
|
const cssExport = findNamedCssExportObject(ast);
|
|
2741
2787
|
if (!cssExport) {
|
|
2788
|
+
options.onDiagnostic?.(
|
|
2789
|
+
new Diagnostic("expected `export const css = { ... }` with an object literal", filename, ast.program)
|
|
2790
|
+
);
|
|
2742
2791
|
return `/* [truss] ${filename}: expected \`export const css = { ... }\` with an object literal */
|
|
2743
2792
|
`;
|
|
2744
2793
|
}
|
|
@@ -2746,16 +2795,16 @@ function transformCssTs(code, filename, mapping) {
|
|
|
2746
2795
|
const stringBindings = collectStaticStringBindings(ast);
|
|
2747
2796
|
for (const prop of cssExport.properties) {
|
|
2748
2797
|
if (t12.isSpreadElement(prop)) {
|
|
2749
|
-
rules.push(
|
|
2798
|
+
rules.push(unsupported("spread elements in css.ts export", prop));
|
|
2750
2799
|
continue;
|
|
2751
2800
|
}
|
|
2752
2801
|
if (!t12.isObjectProperty(prop)) {
|
|
2753
|
-
rules.push(
|
|
2802
|
+
rules.push(unsupported("non-property in css.ts export", prop));
|
|
2754
2803
|
continue;
|
|
2755
2804
|
}
|
|
2756
2805
|
const selector = objectPropertyStringKey(prop, stringBindings);
|
|
2757
2806
|
if (selector === null) {
|
|
2758
|
-
rules.push(
|
|
2807
|
+
rules.push(unsupported("non-string-literal key in css.ts export", prop));
|
|
2759
2808
|
continue;
|
|
2760
2809
|
}
|
|
2761
2810
|
const valueNode = prop.value;
|
|
@@ -2765,21 +2814,25 @@ function transformCssTs(code, filename, mapping) {
|
|
|
2765
2814
|
continue;
|
|
2766
2815
|
}
|
|
2767
2816
|
if (!t12.isExpression(valueNode)) {
|
|
2768
|
-
rules.push(
|
|
2817
|
+
rules.push(unsupported(`"${selector}" value is not an expression`, valueNode));
|
|
2769
2818
|
continue;
|
|
2770
2819
|
}
|
|
2771
2820
|
if (!cssBindingName) {
|
|
2772
|
-
rules.push(
|
|
2821
|
+
rules.push(unsupported(`"${selector}" \u2014 Css.*.$ chain requires a Css import`, valueNode));
|
|
2773
2822
|
continue;
|
|
2774
2823
|
}
|
|
2775
|
-
const cssResult = resolveCssExpression(valueNode, cssBindingName, mapping
|
|
2824
|
+
const cssResult = resolveCssExpression(valueNode, cssBindingName, mapping);
|
|
2776
2825
|
if ("error" in cssResult) {
|
|
2777
|
-
rules.push(
|
|
2826
|
+
rules.push(unsupported(`"${selector}" \u2014 ${cssResult.error}`, valueNode));
|
|
2778
2827
|
continue;
|
|
2779
2828
|
}
|
|
2780
2829
|
rules.push(formatCssRule(selector, cssResult.declarations));
|
|
2781
2830
|
}
|
|
2782
2831
|
return rules.join("\n\n") + "\n";
|
|
2832
|
+
function unsupported(message, node) {
|
|
2833
|
+
options.onDiagnostic?.(new Diagnostic(message, filename, node));
|
|
2834
|
+
return `/* [truss] unsupported: ${message} */`;
|
|
2835
|
+
}
|
|
2783
2836
|
}
|
|
2784
2837
|
function findNamedCssExportObject(ast) {
|
|
2785
2838
|
for (const node of ast.program.body) {
|
|
@@ -2809,7 +2862,7 @@ function extractStaticStringValue(node, cssBindingName) {
|
|
|
2809
2862
|
}
|
|
2810
2863
|
return null;
|
|
2811
2864
|
}
|
|
2812
|
-
function resolveCssExpression(node, cssBindingName, mapping
|
|
2865
|
+
function resolveCssExpression(node, cssBindingName, mapping) {
|
|
2813
2866
|
const chain = extractDollarChain(node, cssBindingName);
|
|
2814
2867
|
if (!chain) {
|
|
2815
2868
|
return { error: "value must be a Css.*.$ expression" };
|
|
@@ -3375,10 +3428,18 @@ function transformTruss(code, filename, mapping, options = {}) {
|
|
|
3375
3428
|
return;
|
|
3376
3429
|
}
|
|
3377
3430
|
const resolveCssChainReference2 = buildCssChainReferenceResolver(path, cssBindingName);
|
|
3378
|
-
const resolvedChain = resolveFullChain(
|
|
3431
|
+
const resolvedChain = resolveFullChain(
|
|
3432
|
+
{
|
|
3433
|
+
mapping,
|
|
3434
|
+
cssBindingName,
|
|
3435
|
+
resolveCssChainReference: resolveCssChainReference2
|
|
3436
|
+
},
|
|
3437
|
+
chain
|
|
3438
|
+
);
|
|
3379
3439
|
sites.push({ path, resolvedChain });
|
|
3380
3440
|
const line = path.node.loc?.start.line ?? null;
|
|
3381
3441
|
for (const err of resolvedChain.errors) {
|
|
3442
|
+
options.onDiagnostic?.(new Diagnostic(err, filename, path.node));
|
|
3382
3443
|
errorMessages.push({ message: err, line });
|
|
3383
3444
|
}
|
|
3384
3445
|
},
|
|
@@ -3628,9 +3689,9 @@ function createTrussTransformSession(options) {
|
|
|
3628
3689
|
arbitraryCssRegistry.clear();
|
|
3629
3690
|
libraryCache = null;
|
|
3630
3691
|
}
|
|
3631
|
-
function updateArbitraryCssRegistry(sourcePath, sourceCode) {
|
|
3692
|
+
function updateArbitraryCssRegistry(sourcePath, sourceCode, diagnostics = {}) {
|
|
3632
3693
|
sourcePath = resolve2(sourcePath).replace(/\\/g, "/");
|
|
3633
|
-
const css = transformCssTs(sourceCode, sourcePath, ensureMapping()).trim();
|
|
3694
|
+
const css = transformCssTs(sourceCode, sourcePath, ensureMapping(), diagnostics).trim();
|
|
3634
3695
|
if (css.length > 0) {
|
|
3635
3696
|
const prev = arbitraryCssRegistry.get(sourcePath);
|
|
3636
3697
|
arbitraryCssRegistry.set(sourcePath, css);
|
|
@@ -3695,6 +3756,11 @@ import * as t16 from "@babel/types";
|
|
|
3695
3756
|
import { readFileSync as readFileSync3, writeFileSync, mkdirSync } from "fs";
|
|
3696
3757
|
import { resolve as resolve3, join } from "path";
|
|
3697
3758
|
function trussEsbuildPlugin(opts) {
|
|
3759
|
+
const diagnostics = {
|
|
3760
|
+
onDiagnostic(error) {
|
|
3761
|
+
throw error;
|
|
3762
|
+
}
|
|
3763
|
+
};
|
|
3698
3764
|
const session = createTrussTransformSession({
|
|
3699
3765
|
mappingPath() {
|
|
3700
3766
|
return resolve3(process.cwd(), opts.mapping);
|
|
@@ -3710,11 +3776,11 @@ function trussEsbuildPlugin(opts) {
|
|
|
3710
3776
|
build.onLoad({ filter: /\.[cm]?[jt]sx?$/ }, (args) => {
|
|
3711
3777
|
const code = readFileSync3(args.path, "utf8");
|
|
3712
3778
|
if (args.path.endsWith(".css.ts")) {
|
|
3713
|
-
session.updateArbitraryCssRegistry(args.path, code);
|
|
3779
|
+
session.updateArbitraryCssRegistry(args.path, code, diagnostics);
|
|
3714
3780
|
return { contents: code, loader: loaderForPath(args.path) };
|
|
3715
3781
|
}
|
|
3716
3782
|
if (!code.includes("Css") && !code.includes("css=")) return void 0;
|
|
3717
|
-
const result = session.transformCode(code, args.path);
|
|
3783
|
+
const result = session.transformCode(code, args.path, diagnostics);
|
|
3718
3784
|
if (!result) return void 0;
|
|
3719
3785
|
return { contents: result.code, loader: loaderForPath(args.path) };
|
|
3720
3786
|
});
|
|
@@ -3753,6 +3819,7 @@ function trussPlugin(opts) {
|
|
|
3753
3819
|
let debug = false;
|
|
3754
3820
|
let isTest = false;
|
|
3755
3821
|
let isBuild = false;
|
|
3822
|
+
let devSocket;
|
|
3756
3823
|
const libraryPaths = opts.libraries ?? [];
|
|
3757
3824
|
let emittedCssFileName = null;
|
|
3758
3825
|
let cssVersion = 0;
|
|
@@ -3760,6 +3827,11 @@ function trussPlugin(opts) {
|
|
|
3760
3827
|
function mappingPath() {
|
|
3761
3828
|
return resolve4(projectRoot || process.cwd(), opts.mapping);
|
|
3762
3829
|
}
|
|
3830
|
+
function diagnostics(context) {
|
|
3831
|
+
return {
|
|
3832
|
+
onDiagnostic: (error) => reportDiagnostic(context, error)
|
|
3833
|
+
};
|
|
3834
|
+
}
|
|
3763
3835
|
const session = createTrussTransformSession({
|
|
3764
3836
|
mappingPath,
|
|
3765
3837
|
projectRoot: () => projectRoot || process.cwd(),
|
|
@@ -3786,6 +3858,7 @@ function trussPlugin(opts) {
|
|
|
3786
3858
|
// -- Dev mode HMR --
|
|
3787
3859
|
configureServer(server) {
|
|
3788
3860
|
if (isTest) return;
|
|
3861
|
+
devSocket = server.ws;
|
|
3789
3862
|
server.middlewares.use((req, res, next) => {
|
|
3790
3863
|
if (req.url !== VIRTUAL_CSS_ENDPOINT) return next();
|
|
3791
3864
|
const css = session.collectCss();
|
|
@@ -3878,7 +3951,7 @@ __injectTrussCSS(${JSON.stringify(payload)});
|
|
|
3878
3951
|
}
|
|
3879
3952
|
if (id.startsWith(VIRTUAL_TEST_CSS_PREFIX)) {
|
|
3880
3953
|
const sourcePath2 = canonicalSourcePath(id.slice(VIRTUAL_TEST_CSS_PREFIX.length));
|
|
3881
|
-
session.updateArbitraryCssRegistry(sourcePath2, readFileSync4(sourcePath2, "utf8"));
|
|
3954
|
+
session.updateArbitraryCssRegistry(sourcePath2, readFileSync4(sourcePath2, "utf8"), diagnostics(this));
|
|
3882
3955
|
const payload = {
|
|
3883
3956
|
arbitraryRules: splitArbitraryCss(session.getArbitraryCss(sourcePath2)),
|
|
3884
3957
|
source: sourcePath2
|
|
@@ -3893,7 +3966,7 @@ __injectTrussCSS(${JSON.stringify(payload)});
|
|
|
3893
3966
|
if (!id.startsWith(VIRTUAL_CSS_PREFIX)) return null;
|
|
3894
3967
|
const sourcePath = id.slice(VIRTUAL_CSS_PREFIX.length) + ".ts";
|
|
3895
3968
|
const sourceCode = readFileSync4(sourcePath, "utf8");
|
|
3896
|
-
session.updateArbitraryCssRegistry(sourcePath, sourceCode);
|
|
3969
|
+
session.updateArbitraryCssRegistry(sourcePath, sourceCode, diagnostics(this));
|
|
3897
3970
|
return `/* [truss] ${sourcePath} \u2014 included via truss.css */`;
|
|
3898
3971
|
},
|
|
3899
3972
|
transform(code, id) {
|
|
@@ -3907,7 +3980,7 @@ __injectTrussCSS(${JSON.stringify(payload)});
|
|
|
3907
3980
|
import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
|
|
3908
3981
|
const importsOnlyResult = rewrittenImports.changed || shouldBootstrapTestCss ? { code: transformedCode, map: null } : null;
|
|
3909
3982
|
if (fileId.endsWith(".css.ts")) {
|
|
3910
|
-
session.updateArbitraryCssRegistry(fileId, code);
|
|
3983
|
+
session.updateArbitraryCssRegistry(fileId, code, diagnostics(this));
|
|
3911
3984
|
if (isTest) {
|
|
3912
3985
|
const css = session.getArbitraryCss(fileId);
|
|
3913
3986
|
return { code: appendTestCssInjection(transformedCode, fileId, css), map: null };
|
|
@@ -3916,7 +3989,7 @@ import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
|
|
|
3916
3989
|
}
|
|
3917
3990
|
const hasCssDsl = rewrittenImports.code.includes("Css") || rewrittenImports.code.includes("css=");
|
|
3918
3991
|
if (!hasCssDsl) return importsOnlyResult;
|
|
3919
|
-
const result = session.transformCode(transformedCode, fileId, { debug, injectCss: isTest });
|
|
3992
|
+
const result = session.transformCode(transformedCode, fileId, { debug, injectCss: isTest, ...diagnostics(this) });
|
|
3920
3993
|
return result ? { code: result.code, map: result.map } : importsOnlyResult;
|
|
3921
3994
|
},
|
|
3922
3995
|
// -- Production CSS emission --
|
|
@@ -3947,6 +4020,16 @@ import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
|
|
|
3947
4020
|
}
|
|
3948
4021
|
}
|
|
3949
4022
|
};
|
|
4023
|
+
function reportDiagnostic(context, error) {
|
|
4024
|
+
if ((opts.unsupportedPattern ?? (isBuild ? "error" : "warn")) === "error") {
|
|
4025
|
+
throw error;
|
|
4026
|
+
}
|
|
4027
|
+
context.warn(error);
|
|
4028
|
+
devSocket?.send({
|
|
4029
|
+
type: "error",
|
|
4030
|
+
err: { message: error.message, stack: "", id: error.id, loc: error.loc, plugin: "truss" }
|
|
4031
|
+
});
|
|
4032
|
+
}
|
|
3950
4033
|
}
|
|
3951
4034
|
function resolveImportPath(source, importer, projectRoot) {
|
|
3952
4035
|
if (isAbsolute(source)) {
|