@velarscript/web 0.22.0 → 0.23.0
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/dist/analyzer.d.ts +61 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +216 -5
- package/dist/analyzer.js.map +1 -1
- package/dist/compiler.d.ts.map +1 -1
- package/dist/compiler.js +7 -0
- package/dist/compiler.js.map +1 -1
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +10 -0
- package/dist/emitter.js.map +1 -1
- package/dist/look-static.d.ts.map +1 -1
- package/dist/look-static.js +13 -1
- package/dist/look-static.js.map +1 -1
- package/dist/look.d.ts +33 -0
- package/dist/look.d.ts.map +1 -1
- package/dist/look.js +57 -0
- package/dist/look.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +15 -1
- package/dist/runtime.js.map +1 -1
- package/package.json +2 -2
package/dist/analyzer.d.ts
CHANGED
|
@@ -26,6 +26,13 @@ export declare class VelarWebAnalyzer extends Analyzer {
|
|
|
26
26
|
private readonly honoredJsxKeys;
|
|
27
27
|
private readonly reportedJsxKeys;
|
|
28
28
|
private lookBuilderNames;
|
|
29
|
+
/**
|
|
30
|
+
* D103: the module's `velar/look` import, so the migration off
|
|
31
|
+
* `color("var(--x)")` can write the `token` import it needs in the same
|
|
32
|
+
* rewrite. A migration that only changed the call would leave the module
|
|
33
|
+
* naming a builder it never imported, which is not a mechanical fix.
|
|
34
|
+
*/
|
|
35
|
+
private lookImport;
|
|
29
36
|
private lookDeclarations;
|
|
30
37
|
private lookLiteralDepth;
|
|
31
38
|
/**
|
|
@@ -270,6 +277,31 @@ export declare class VelarWebAnalyzer extends Analyzer {
|
|
|
270
277
|
* module compiles; dynamic arguments keep the runtime guard.
|
|
271
278
|
*/
|
|
272
279
|
private checkLookBuilderCall;
|
|
280
|
+
/**
|
|
281
|
+
* D103 rules 1 and 5 — what a checked token reference has to be at the site
|
|
282
|
+
* that writes it.
|
|
283
|
+
*
|
|
284
|
+
* The compiler cannot see a design system's values: no token stylesheet is an
|
|
285
|
+
* input to this compile, and the whole point of the contract is that the
|
|
286
|
+
* theme swaps values under the same names. So the reference itself is the
|
|
287
|
+
* only thing there is to check, and it is checked completely — a literal
|
|
288
|
+
* name, spelled as a CSS custom property identifier, and nothing else in the
|
|
289
|
+
* call. A computed name would leave a Look value with no checked part at all,
|
|
290
|
+
* which is the surface D50 rule 92 says is worse than none.
|
|
291
|
+
*/
|
|
292
|
+
private checkLookTokenCall;
|
|
293
|
+
/**
|
|
294
|
+
* D103 rule 4 — the migration off the one passthrough that used to work.
|
|
295
|
+
*
|
|
296
|
+
* The rewrite carries the `token` import when the module does not already
|
|
297
|
+
* have one, because a fix that leaves a module naming an unimported builder
|
|
298
|
+
* is not mechanical. A `var(--x, fallback)` reference has no rewrite: rule 5
|
|
299
|
+
* closed the contract against per-site fallbacks, so the message says that
|
|
300
|
+
* rather than offering a migration that would silently drop the fallback.
|
|
301
|
+
*/
|
|
302
|
+
private reportLookColorVarReference;
|
|
303
|
+
/** The one edit that gives this module a `token` import, in the shape D103's migration needs. */
|
|
304
|
+
private lookTokenImportEdit;
|
|
273
305
|
private checkAnimateBuilderCall;
|
|
274
306
|
private checkAnimationKeyword;
|
|
275
307
|
private analyzeKeyframes;
|
|
@@ -288,6 +320,35 @@ export declare class VelarWebAnalyzer extends Analyzer {
|
|
|
288
320
|
* property no longer co-reports a `stringType` fallback assignment error.
|
|
289
321
|
*/
|
|
290
322
|
private analyzeLookValue;
|
|
323
|
+
/** Whether this value is written as a call to the module's `token` builder, alias included. */
|
|
324
|
+
private isLookTokenCall;
|
|
325
|
+
/** How this module spells a call to the token builder, honouring an aliased import. */
|
|
326
|
+
private lookTokenCallText;
|
|
327
|
+
/**
|
|
328
|
+
* The edits that replace one written Look value with the checked token call,
|
|
329
|
+
* in the spelling of the site that wrote it.
|
|
330
|
+
*
|
|
331
|
+
* A `look:property="text"` directive is analyzed through a synthetic literal
|
|
332
|
+
* standing for the whole attribute, so the rewrite replaces the attribute:
|
|
333
|
+
* splicing a call between an attribute's quotes is not JSX, and a fix that
|
|
334
|
+
* produces a parse error is not a mechanical fix. The `look:property={...}`
|
|
335
|
+
* spelling carries a real expression whose span is the value alone, and is
|
|
336
|
+
* rewritten in place like a block entry.
|
|
337
|
+
*/
|
|
338
|
+
private lookTokenRewrite;
|
|
339
|
+
/**
|
|
340
|
+
* D103 rule 4, free-text half — an advisory rather than a refusal.
|
|
341
|
+
*
|
|
342
|
+
* `fontFamily`, `backdropFilter` and the other free-text kinds accept
|
|
343
|
+
* arbitrary CSS text by construction, and a `var()` inside a larger value is
|
|
344
|
+
* a legitimate spelling with no single token to rewrite it to: the tail of a
|
|
345
|
+
* font stack, one layer of a filter list. Refusing there would refuse real
|
|
346
|
+
* CSS. What is advised is the narrow case where the *whole* value is one
|
|
347
|
+
* reference, because there `token("--name")` is the checked spelling of the
|
|
348
|
+
* same thing and the rewrite is unambiguous — which is the bar the `A`
|
|
349
|
+
* roster is written to.
|
|
350
|
+
*/
|
|
351
|
+
private adviseLookTokenSpelling;
|
|
291
352
|
/**
|
|
292
353
|
* `subject` names the position in the message. It is the property itself
|
|
293
354
|
* everywhere except the `transition(...)` builder, whose first argument takes
|
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"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"}
|
package/dist/analyzer.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { semanticTypeIdentity } from "@velarscript/compiler";
|
|
1
|
+
import { mechanicalEdits, mechanicalFix, semanticTypeIdentity } from "@velarscript/compiler";
|
|
2
2
|
import { Analyzer, anyType, boolType, describeType, expressionContainsDirectAwait, invalidType, isInvalidType, isAssignable, isReadonlyView, nullType, nonOptional, numberType, optionalOf, spanIdentity, stringType, unknownType, } from "@velarscript/compiler/extension";
|
|
3
3
|
import { BROWSER_TEST_MODULE, BROWSER_TEST_SOURCE_SUFFIX, browserTestImportGuidance } from "./browser-test.js";
|
|
4
4
|
import { cssTokens } from "./css-tokens.js";
|
|
5
|
-
import { LOOK_ABSENT_MEDIA_SUBJECTS, LOOK_ARITHMETIC_HINT, LOOK_ANIMATION_DIRECTIONS, LOOK_ANIMATION_EASINGS, LOOK_ANIMATION_FILLS, LOOK_BORDER_STYLE_NAMES, LOOK_BUILDER_NUMERIC_RANGES, LOOK_BUILDER_SIGNATURES, LOOK_BUILDERS, LOOK_EXCLUDED_PROPERTIES, LOOK_HOOKS, LOOK_CSS_WIDE_KEYWORDS, LOOK_LARGE_KEYWORD_SETS, LOOK_LENGTH_BUILDERS, LOOK_MEDIA_LENGTH_UNITS, LOOK_MEDIA_SUBJECTS, LOOK_NUMERIC_TYPE_NAMES, LOOK_NON_ANIMATABLE_PROPERTIES, LOOK_PARTIAL_KEYWORD_PROPERTIES, LOOK_PROPERTIES, LOOK_PROPERTY_KEYWORDS, LOOK_PROPERTY_VALUE_KINDS, LOOK_TARGETS, LOOK_UNIT_TYPES, LOOK_UNITLESS_PROPERTIES, lookOwnKeywords, nearestLookName, } from "./look.js";
|
|
5
|
+
import { LOOK_ABSENT_MEDIA_SUBJECTS, LOOK_ARITHMETIC_HINT, LOOK_ANIMATION_DIRECTIONS, LOOK_ANIMATION_EASINGS, LOOK_ANIMATION_FILLS, LOOK_BORDER_STYLE_NAMES, LOOK_BUILDER_NUMERIC_RANGES, LOOK_BUILDER_SIGNATURES, LOOK_BUILDERS, LOOK_EXCLUDED_PROPERTIES, LOOK_HOOKS, LOOK_CSS_WIDE_KEYWORDS, LOOK_LARGE_KEYWORD_SETS, LOOK_LENGTH_BUILDERS, LOOK_MEDIA_LENGTH_UNITS, LOOK_MEDIA_SUBJECTS, LOOK_NUMERIC_TYPE_NAMES, LOOK_NON_ANIMATABLE_PROPERTIES, LOOK_PARTIAL_KEYWORD_PROPERTIES, LOOK_PROPERTIES, LOOK_PROPERTY_KEYWORDS, LOOK_PROPERTY_VALUE_KINDS, LOOK_TARGETS, LOOK_TOKEN_NAME_RULE, LOOK_TOKEN_NO_FALLBACK_GUIDANCE, LOOK_UNIT_TYPES, LOOK_UNITLESS_PROPERTIES, isLookTokenName, isLookVarReference, lookOwnKeywords, lookTokenReference, lookVarReferenceName, nearestLookName, } from "./look.js";
|
|
6
6
|
import { collectLookStaticValues, evaluateLookStaticExpression, isLookStaticValue, lookStaticCss } from "./look-static.js";
|
|
7
7
|
import { keyframeCssValue } from "./keyframes.js";
|
|
8
|
+
import { byCodeUnit } from "./stable-order.js";
|
|
8
9
|
import { dynamicChildLeaves, JSX_SCALAR_TEXT_HINT } from "./emitter.js";
|
|
9
10
|
import { isWebCustomElementName, WEB_ARIA_ATTRIBUTES, WEB_ARIA_ENUMERATED_VALUES, WEB_ARIA_ROLES, WEB_ARIA_ROLE_SYNONYMS, WEB_MISSPELLED_ATTRIBUTES, WEB_NATIVE_ELEMENTS, } from "./elements.js";
|
|
10
11
|
import { isWebExpression, isWebJsx, isWebKeyframes, isWebLook, isWebStatement, isWebUnit, } from "./ast.js";
|
|
@@ -55,7 +56,7 @@ const textualWebPrimitiveNames = new Set(["Length", "Percentage", "LengthPercent
|
|
|
55
56
|
// D72 rule 186: derived from the published table, not restated beside it.
|
|
56
57
|
const webEventTypeNames = WEB_EVENT_TYPE_NAMES;
|
|
57
58
|
const webEventDeadFields = new Set(["target", "currentTarget", "value", "checked"]);
|
|
58
|
-
const diagnostic = (code, message, sourceSpan) =>
|
|
59
|
+
const diagnostic = (code, message, sourceSpan, fix) => fix ? { code, message, span: sourceSpan, fix } : { code, message, span: sourceSpan };
|
|
59
60
|
const bindTargetGuidance = (directive) => `${directive} requires a writable reactive location: a state name, or a field or index path on one such as ${directive}={form.name} or ${directive}={items[0]}`;
|
|
60
61
|
const LOOK_CONDITION_TERM_LIMIT = 32;
|
|
61
62
|
const lookLength = { kind: "named", name: "Length" };
|
|
@@ -784,6 +785,27 @@ function lookContributions(look, looks, visited = new Set()) {
|
|
|
784
785
|
walk(look.entries, "");
|
|
785
786
|
return { properties, composed };
|
|
786
787
|
}
|
|
788
|
+
function collectLookImportSite(program) {
|
|
789
|
+
let lastImportEnd = null;
|
|
790
|
+
for (const statement of program.body) {
|
|
791
|
+
if (statement.kind !== "ImportDeclaration")
|
|
792
|
+
continue;
|
|
793
|
+
lastImportEnd = statement.span.end;
|
|
794
|
+
if (statement.source !== "velar/look" || statement.javascript || statement.specifiers.some((specifier) => specifier.namespace))
|
|
795
|
+
continue;
|
|
796
|
+
return {
|
|
797
|
+
declaration: {
|
|
798
|
+
span: statement.span,
|
|
799
|
+
specifiers: statement.specifiers.map((specifier) => ({ imported: specifier.imported, local: specifier.local })),
|
|
800
|
+
},
|
|
801
|
+
insertAt: statement.span.end,
|
|
802
|
+
leadingBlankLine: false,
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
return lastImportEnd === null
|
|
806
|
+
? { declaration: null, insertAt: program.body[0]?.span.start ?? 0, leadingBlankLine: true }
|
|
807
|
+
: { declaration: null, insertAt: lastImportEnd, leadingBlankLine: false };
|
|
808
|
+
}
|
|
787
809
|
/** Local names bound to a velar/look builder, including aliased imports. */
|
|
788
810
|
function collectLookBuilderNames(program) {
|
|
789
811
|
const names = new Map();
|
|
@@ -1407,6 +1429,13 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
1407
1429
|
honoredJsxKeys = new Set();
|
|
1408
1430
|
reportedJsxKeys = new Set();
|
|
1409
1431
|
lookBuilderNames = new Map();
|
|
1432
|
+
/**
|
|
1433
|
+
* D103: the module's `velar/look` import, so the migration off
|
|
1434
|
+
* `color("var(--x)")` can write the `token` import it needs in the same
|
|
1435
|
+
* rewrite. A migration that only changed the call would leave the module
|
|
1436
|
+
* naming a builder it never imported, which is not a mechanical fix.
|
|
1437
|
+
*/
|
|
1438
|
+
lookImport = null;
|
|
1410
1439
|
lookDeclarations = new Map();
|
|
1411
1440
|
lookLiteralDepth = 0;
|
|
1412
1441
|
/**
|
|
@@ -1441,6 +1470,7 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
1441
1470
|
analyze(program) {
|
|
1442
1471
|
this.lookStaticValues = collectLookStaticValues(program, this.importedLookStaticValues);
|
|
1443
1472
|
this.lookBuilderNames = collectLookBuilderNames(program);
|
|
1473
|
+
this.lookImport = collectLookImportSite(program);
|
|
1444
1474
|
this.lookDeclarations = collectLookDeclarations(program);
|
|
1445
1475
|
for (const name of collectDerivedReactiveNames(program))
|
|
1446
1476
|
this.derivedReactiveNames.add(name);
|
|
@@ -2731,6 +2761,10 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
2731
2761
|
this.checkAnimateBuilderCall(expression);
|
|
2732
2762
|
return;
|
|
2733
2763
|
}
|
|
2764
|
+
if (builder === "token") {
|
|
2765
|
+
this.checkLookTokenCall(expression);
|
|
2766
|
+
return;
|
|
2767
|
+
}
|
|
2734
2768
|
const ranges = LOOK_BUILDER_NUMERIC_RANGES.get(builder);
|
|
2735
2769
|
// A named argument fills the same slot its positional spelling does, so the
|
|
2736
2770
|
// position comes from the builder's own signature — the one table the
|
|
@@ -2772,11 +2806,104 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
2772
2806
|
if (builder === "transition" && position === 0) {
|
|
2773
2807
|
this.validateLookStringVocabulary("transitionProperty", argument, "The transition builder's property argument");
|
|
2774
2808
|
}
|
|
2809
|
+
// D103 rule 4: one spelling. `color(string)` used to be the only checked
|
|
2810
|
+
// Look value that let a design token through, and it let it through as
|
|
2811
|
+
// text nobody read — so the same reference was legal on `color` and
|
|
2812
|
+
// refused on `width`, and the accepting half checked nothing.
|
|
2813
|
+
if (builder === "color" && position === 0)
|
|
2814
|
+
this.reportLookColorVarReference(expression, argument);
|
|
2775
2815
|
}
|
|
2776
2816
|
if (builder === "tracks" && expression.arguments.length > 1024) {
|
|
2777
2817
|
this.diagnostics.push(diagnostic("VEL5042", "tracks cannot contain more than 1024 values", expression.span));
|
|
2778
2818
|
}
|
|
2779
2819
|
}
|
|
2820
|
+
/**
|
|
2821
|
+
* D103 rules 1 and 5 — what a checked token reference has to be at the site
|
|
2822
|
+
* that writes it.
|
|
2823
|
+
*
|
|
2824
|
+
* The compiler cannot see a design system's values: no token stylesheet is an
|
|
2825
|
+
* input to this compile, and the whole point of the contract is that the
|
|
2826
|
+
* theme swaps values under the same names. So the reference itself is the
|
|
2827
|
+
* only thing there is to check, and it is checked completely — a literal
|
|
2828
|
+
* name, spelled as a CSS custom property identifier, and nothing else in the
|
|
2829
|
+
* call. A computed name would leave a Look value with no checked part at all,
|
|
2830
|
+
* which is the surface D50 rule 92 says is worse than none.
|
|
2831
|
+
*/
|
|
2832
|
+
checkLookTokenCall(expression) {
|
|
2833
|
+
const names = expression.argumentNames;
|
|
2834
|
+
// `token(name="--x")` writes the same one argument the positional spelling
|
|
2835
|
+
// does, so the name position is read from the signature the way every other
|
|
2836
|
+
// builder's is rather than assumed to be index 0.
|
|
2837
|
+
const position = names?.findIndex((entry) => entry === "name") ?? -1;
|
|
2838
|
+
const nameIndex = position >= 0 ? position : names?.[0] === undefined || names[0] === null ? 0 : -1;
|
|
2839
|
+
for (const [index, argument] of expression.arguments.entries()) {
|
|
2840
|
+
if (index === nameIndex)
|
|
2841
|
+
continue;
|
|
2842
|
+
this.diagnostics.push(diagnostic("VEL5042", LOOK_TOKEN_NO_FALLBACK_GUIDANCE, argument.span));
|
|
2843
|
+
}
|
|
2844
|
+
const written = nameIndex >= 0 ? expression.arguments[nameIndex] : undefined;
|
|
2845
|
+
if (!written)
|
|
2846
|
+
return;
|
|
2847
|
+
if (written.kind !== "LiteralExpression" || typeof written.value !== "string") {
|
|
2848
|
+
this.diagnostics.push(diagnostic("VEL5042", `A design token reference names its custom property in the call: ${LOOK_TOKEN_NAME_RULE}. The compiler cannot see a design system's values, so the name is the whole of what it can check — a computed name, an interpolation, or a binding would leave the reference unchecked`, written.span));
|
|
2849
|
+
return;
|
|
2850
|
+
}
|
|
2851
|
+
if (isLookTokenName(written.value)) {
|
|
2852
|
+
// D103 rule 2: the reference is compile-time text, so it is folded here
|
|
2853
|
+
// rather than left as a call the browser makes on every module load. The
|
|
2854
|
+
// fold is stamped only on a call this check has just proved, which is
|
|
2855
|
+
// what keeps the emitter structurally unable to write a name nothing
|
|
2856
|
+
// validated. An aliased builder (`const t = token`) is not resolved here
|
|
2857
|
+
// and keeps the runtime implementation, exactly as every other builder
|
|
2858
|
+
// passed around as a value does.
|
|
2859
|
+
if (expression.arguments.length === 1) {
|
|
2860
|
+
this.extensionLiterals.set(spanIdentity(expression.span), lookTokenReference(written.value));
|
|
2861
|
+
}
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
// A name written without the `--` that makes it a custom property is the
|
|
2865
|
+
// one near miss with a single spelling behind it, so it is migrated rather
|
|
2866
|
+
// than only refused.
|
|
2867
|
+
const prefixed = `--${written.value}`;
|
|
2868
|
+
this.diagnostics.push(diagnostic("VEL5042", `Design token name '${written.value}' is not a CSS custom property identifier; ${LOOK_TOKEN_NAME_RULE}`, written.span, isLookTokenName(prefixed)
|
|
2869
|
+
? mechanicalFix(written.span, JSON.stringify(prefixed), `Use "${prefixed}"`)
|
|
2870
|
+
: undefined));
|
|
2871
|
+
}
|
|
2872
|
+
/**
|
|
2873
|
+
* D103 rule 4 — the migration off the one passthrough that used to work.
|
|
2874
|
+
*
|
|
2875
|
+
* The rewrite carries the `token` import when the module does not already
|
|
2876
|
+
* have one, because a fix that leaves a module naming an unimported builder
|
|
2877
|
+
* is not mechanical. A `var(--x, fallback)` reference has no rewrite: rule 5
|
|
2878
|
+
* closed the contract against per-site fallbacks, so the message says that
|
|
2879
|
+
* rather than offering a migration that would silently drop the fallback.
|
|
2880
|
+
*/
|
|
2881
|
+
reportLookColorVarReference(expression, argument) {
|
|
2882
|
+
if (argument.kind !== "LiteralExpression" || typeof argument.value !== "string")
|
|
2883
|
+
return;
|
|
2884
|
+
if (!isLookVarReference(argument.value))
|
|
2885
|
+
return;
|
|
2886
|
+
const referenced = lookVarReferenceName(argument.value);
|
|
2887
|
+
if (referenced === null) {
|
|
2888
|
+
this.diagnostics.push(diagnostic("VEL5042", `A design token reference is written token("--name"), and it carries no fallback: ${LOOK_TOKEN_NO_FALLBACK_GUIDANCE}`, argument.span));
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
const { call, imported } = this.lookTokenCallText(referenced);
|
|
2892
|
+
const rewrite = { span: expression.span, text: call };
|
|
2893
|
+
this.diagnostics.push(diagnostic("VEL5042", `Write a design token reference as ${call}; color("var(...)") passed the reference through as text nothing checked, and token() is the one checked spelling — legal in every Look property, not only the colour ones`, expression.span, mechanicalEdits(imported ? [rewrite] : [this.lookTokenImportEdit(), rewrite], `Use ${call}`)));
|
|
2894
|
+
}
|
|
2895
|
+
/** The one edit that gives this module a `token` import, in the shape D103's migration needs. */
|
|
2896
|
+
lookTokenImportEdit() {
|
|
2897
|
+
const site = this.lookImport ?? { declaration: null, insertAt: 0, leadingBlankLine: true };
|
|
2898
|
+
const specifiers = [...(site.declaration?.specifiers ?? []), { imported: "token", local: "token" }];
|
|
2899
|
+
const line = `import {${[...specifiers]
|
|
2900
|
+
.sort((left, right) => byCodeUnit(left.imported, right.imported))
|
|
2901
|
+
.map((specifier) => specifier.imported === specifier.local ? specifier.imported : `${specifier.imported} as ${specifier.local}`)
|
|
2902
|
+
.join(", ")}} from "velar/look"`;
|
|
2903
|
+
if (site.declaration)
|
|
2904
|
+
return { span: site.declaration.span, text: line };
|
|
2905
|
+
return { span: { start: site.insertAt, end: site.insertAt }, text: site.leadingBlankLine ? `${line}\n\n` : `\n${line}` };
|
|
2906
|
+
}
|
|
2780
2907
|
checkAnimateBuilderCall(expression) {
|
|
2781
2908
|
const argument = (name, position) => {
|
|
2782
2909
|
const named = expression.argumentNames?.findIndex((candidate) => candidate === name) ?? -1;
|
|
@@ -2900,6 +3027,20 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
2900
3027
|
this.inferExpression(value);
|
|
2901
3028
|
return false;
|
|
2902
3029
|
}
|
|
3030
|
+
// D103 rule 1 meets D49. `animation` is the one Look property whose value
|
|
3031
|
+
// names another rule rather than describing one: the `@keyframes` name in
|
|
3032
|
+
// an animation shorthand. Look owns those names — they are generated from
|
|
3033
|
+
// the `keyframes:` value that defines the motion — so a shorthand arriving
|
|
3034
|
+
// from outside this compile is a reference the compiler can check on
|
|
3035
|
+
// neither side, which is the surface D50 rule 92 calls worse than none. The
|
|
3036
|
+
// type refuses it either way; this replaces a bare union dump with the two
|
|
3037
|
+
// spellings that do work.
|
|
3038
|
+
if (name === "animation" && this.isLookTokenCall(value)) {
|
|
3039
|
+
this.diagnostics.push(diagnostic("VEL5038", "Look animation is the one property a design token cannot carry: an animation value names a '@keyframes' rule, and Look generates those names from the 'keyframes:' value that defines the motion, so a shorthand from a design system names a rule this compile never emitted. Declare a checked 'keyframes:' value and pass animate(frames, duration, ...); token() is legal in every other Look property, and a module-level 'import css unsafe \"./styles.css\" before look' carries a design system's own animation when that boundary is intentional", entrySpan));
|
|
3040
|
+
if (!inline)
|
|
3041
|
+
this.inferExpression(value);
|
|
3042
|
+
return false;
|
|
3043
|
+
}
|
|
2903
3044
|
if (!LOOK_PROPERTIES.has(name)) {
|
|
2904
3045
|
const nearest = nearestLookName(name, LOOK_PROPERTIES);
|
|
2905
3046
|
const exclusion = LOOK_EXCLUDED_PROPERTIES.get(name);
|
|
@@ -2917,10 +3058,67 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
2917
3058
|
this.diagnostics.push(diagnostic("VEL5038", shorthandGuidance, value.span));
|
|
2918
3059
|
return false;
|
|
2919
3060
|
}
|
|
2920
|
-
|
|
3061
|
+
const site = { property: name, entrySpan, directive };
|
|
3062
|
+
this.adviseLookTokenSpelling(name, value, site);
|
|
3063
|
+
if (!this.validateLookStringVocabulary(name, value, undefined, site))
|
|
2921
3064
|
return false;
|
|
2922
3065
|
return true;
|
|
2923
3066
|
}
|
|
3067
|
+
/** Whether this value is written as a call to the module's `token` builder, alias included. */
|
|
3068
|
+
isLookTokenCall(value) {
|
|
3069
|
+
return value.kind === "CallExpression" && value.callee.kind === "IdentifierExpression"
|
|
3070
|
+
&& this.lookBuilderNames.get(value.callee.name) === "token";
|
|
3071
|
+
}
|
|
3072
|
+
/** How this module spells a call to the token builder, honouring an aliased import. */
|
|
3073
|
+
lookTokenCallText(referenced) {
|
|
3074
|
+
const local = [...this.lookBuilderNames].find(([, builder]) => builder === "token")?.[0] ?? null;
|
|
3075
|
+
return { call: `${local ?? "token"}(${JSON.stringify(referenced)})`, imported: local !== null };
|
|
3076
|
+
}
|
|
3077
|
+
/**
|
|
3078
|
+
* The edits that replace one written Look value with the checked token call,
|
|
3079
|
+
* in the spelling of the site that wrote it.
|
|
3080
|
+
*
|
|
3081
|
+
* A `look:property="text"` directive is analyzed through a synthetic literal
|
|
3082
|
+
* standing for the whole attribute, so the rewrite replaces the attribute:
|
|
3083
|
+
* splicing a call between an attribute's quotes is not JSX, and a fix that
|
|
3084
|
+
* produces a parse error is not a mechanical fix. The `look:property={...}`
|
|
3085
|
+
* spelling carries a real expression whose span is the value alone, and is
|
|
3086
|
+
* rewritten in place like a block entry.
|
|
3087
|
+
*/
|
|
3088
|
+
lookTokenRewrite(value, referenced, site) {
|
|
3089
|
+
const { call, imported } = this.lookTokenCallText(referenced);
|
|
3090
|
+
const wholeAttribute = site.directive !== null
|
|
3091
|
+
&& value.span.start === site.entrySpan.start && value.span.end === site.entrySpan.end;
|
|
3092
|
+
const edit = {
|
|
3093
|
+
span: value.span,
|
|
3094
|
+
text: wholeAttribute ? `${site.directive}:${site.property}={${call}}` : call,
|
|
3095
|
+
};
|
|
3096
|
+
return imported ? [edit] : [this.lookTokenImportEdit(), edit];
|
|
3097
|
+
}
|
|
3098
|
+
/**
|
|
3099
|
+
* D103 rule 4, free-text half — an advisory rather than a refusal.
|
|
3100
|
+
*
|
|
3101
|
+
* `fontFamily`, `backdropFilter` and the other free-text kinds accept
|
|
3102
|
+
* arbitrary CSS text by construction, and a `var()` inside a larger value is
|
|
3103
|
+
* a legitimate spelling with no single token to rewrite it to: the tail of a
|
|
3104
|
+
* font stack, one layer of a filter list. Refusing there would refuse real
|
|
3105
|
+
* CSS. What is advised is the narrow case where the *whole* value is one
|
|
3106
|
+
* reference, because there `token("--name")` is the checked spelling of the
|
|
3107
|
+
* same thing and the rewrite is unambiguous — which is the bar the `A`
|
|
3108
|
+
* roster is written to.
|
|
3109
|
+
*/
|
|
3110
|
+
adviseLookTokenSpelling(name, value, site) {
|
|
3111
|
+
const kind = LOOK_PROPERTY_VALUE_KINDS.get(name);
|
|
3112
|
+
if (kind !== "text" && kind !== "filter" && kind !== "transform")
|
|
3113
|
+
return;
|
|
3114
|
+
if (value.kind !== "LiteralExpression" || typeof value.value !== "string")
|
|
3115
|
+
return;
|
|
3116
|
+
const referenced = lookVarReferenceName(value.value);
|
|
3117
|
+
if (referenced === null)
|
|
3118
|
+
return;
|
|
3119
|
+
const { call } = this.lookTokenCallText(referenced);
|
|
3120
|
+
this.advise("A12", `Look property '${name}' accepts free text, so this design token reference compiles — as text nothing checks. ${call} is the checked spelling of the same reference, and it is legal in every Look property. A var() inside a larger value, such as a font stack's fallback, has no single token to stand for it and is not advised`, value.span, mechanicalEdits(this.lookTokenRewrite(value, referenced, site), `Use ${call}`));
|
|
3121
|
+
}
|
|
2924
3122
|
/**
|
|
2925
3123
|
* `subject` names the position in the message. It is the property itself
|
|
2926
3124
|
* everywhere except the `transition(...)` builder, whose first argument takes
|
|
@@ -2928,7 +3126,7 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
2928
3126
|
* refusal that named the longhand there would name a spelling the author
|
|
2929
3127
|
* never wrote.
|
|
2930
3128
|
*/
|
|
2931
|
-
validateLookStringVocabulary(name, value, subject = `Look property '${name}'
|
|
3129
|
+
validateLookStringVocabulary(name, value, subject = `Look property '${name}'`, site) {
|
|
2932
3130
|
const kind = LOOK_PROPERTY_VALUE_KINDS.get(name);
|
|
2933
3131
|
if (!kind || kind === "text" || kind === "filter" || kind === "transform" || kind === "animation")
|
|
2934
3132
|
return true;
|
|
@@ -2937,6 +3135,19 @@ export class VelarWebAnalyzer extends Analyzer {
|
|
|
2937
3135
|
return true;
|
|
2938
3136
|
for (const text of values) {
|
|
2939
3137
|
const normalized = text.trim();
|
|
3138
|
+
// D103 rule 4: this is the refusal the shell wave met — every metric,
|
|
3139
|
+
// shadow and transition property turned a design token reference away and
|
|
3140
|
+
// named `import css unsafe` as the only way out, which is how a product's
|
|
3141
|
+
// whole visual layer ended up outside Look. The reference is still
|
|
3142
|
+
// refused as free text, because free text is what it was; what changed is
|
|
3143
|
+
// that the refusal now names the checked spelling of the same reference
|
|
3144
|
+
// and rewrites to it.
|
|
3145
|
+
const referenced = site && lookVarReferenceName(normalized);
|
|
3146
|
+
if (referenced) {
|
|
3147
|
+
const { call } = this.lookTokenCallText(referenced);
|
|
3148
|
+
this.diagnostics.push(diagnostic("VEL5038", `${subject} does not accept the text '${normalized}'; write the design token reference as ${call}, which is checked and legal in every Look property`, value.span, mechanicalEdits(this.lookTokenRewrite(value, referenced, site), `Use ${call}`)));
|
|
3149
|
+
return false;
|
|
3150
|
+
}
|
|
2940
3151
|
if (kind === "metric" && /^[+-]?(?:\d+(?:\.\d+)?|\.\d+)(?:px|rem|em|vw|vh|vmin|vmax|%|fr|ms|s|deg|turn)$/u.test(normalized)) {
|
|
2941
3152
|
this.diagnostics.push(diagnostic("VEL5038", `Use the unit literal ${normalized}; quoted unit values are not part of Look`, value.span));
|
|
2942
3153
|
return false;
|