@velarscript/web 0.29.1 → 0.30.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.
Files changed (68) hide show
  1. package/dist/analysis/look-values.d.ts +38 -0
  2. package/dist/analysis/look-values.d.ts.map +1 -1
  3. package/dist/analysis/look-values.js +86 -0
  4. package/dist/analysis/look-values.js.map +1 -1
  5. package/dist/analysis/reactive-names.d.ts +50 -0
  6. package/dist/analysis/reactive-names.d.ts.map +1 -0
  7. package/dist/analysis/reactive-names.js +199 -0
  8. package/dist/analysis/reactive-names.js.map +1 -0
  9. package/dist/analysis/watch-cycles.d.ts +28 -0
  10. package/dist/analysis/watch-cycles.d.ts.map +1 -1
  11. package/dist/analysis/watch-cycles.js +47 -0
  12. package/dist/analysis/watch-cycles.js.map +1 -1
  13. package/dist/analyzer.d.ts +4 -0
  14. package/dist/analyzer.d.ts.map +1 -1
  15. package/dist/analyzer.js +36 -47
  16. package/dist/analyzer.js.map +1 -1
  17. package/dist/compiler.d.ts +1 -1
  18. package/dist/compiler.js +2 -2
  19. package/dist/compiler.js.map +1 -1
  20. package/dist/css-string.d.ts +4 -10
  21. package/dist/css-string.d.ts.map +1 -1
  22. package/dist/css-string.js +4 -27
  23. package/dist/css-string.js.map +1 -1
  24. package/dist/emitter.d.ts.map +1 -1
  25. package/dist/emitter.js +11 -2090
  26. package/dist/emitter.js.map +1 -1
  27. package/dist/lexer.js +63 -43
  28. package/dist/lexer.js.map +1 -1
  29. package/dist/look-static.d.ts.map +1 -1
  30. package/dist/look-static.js +20 -7
  31. package/dist/look-static.js.map +1 -1
  32. package/dist/look.d.ts +4 -4
  33. package/dist/look.d.ts.map +1 -1
  34. package/dist/look.js +4 -4
  35. package/dist/look.js.map +1 -1
  36. package/dist/runtime-sources.generated.d.ts +89 -0
  37. package/dist/runtime-sources.generated.d.ts.map +1 -0
  38. package/dist/runtime-sources.generated.js +96 -0
  39. package/dist/runtime-sources.generated.js.map +1 -0
  40. package/dist/runtime.d.ts +18 -0
  41. package/dist/runtime.d.ts.map +1 -1
  42. package/dist/runtime.js +31 -3742
  43. package/dist/runtime.js.map +1 -1
  44. package/package.json +3 -3
  45. package/dist/browser-host-runtime.d.ts +0 -23
  46. package/dist/browser-host-runtime.d.ts.map +0 -1
  47. package/dist/browser-host-runtime.js +0 -29
  48. package/dist/browser-host-runtime.js.map +0 -1
  49. package/dist/reactive-bridge-runtime.d.ts +0 -6
  50. package/dist/reactive-bridge-runtime.d.ts.map +0 -1
  51. package/dist/reactive-bridge-runtime.js +0 -114
  52. package/dist/reactive-bridge-runtime.js.map +0 -1
  53. package/dist/realtime-client-runtime.d.ts +0 -3
  54. package/dist/realtime-client-runtime.d.ts.map +0 -1
  55. package/dist/realtime-client-runtime.js +0 -382
  56. package/dist/realtime-client-runtime.js.map +0 -1
  57. package/dist/runtime-foundation.d.ts +0 -7
  58. package/dist/runtime-foundation.d.ts.map +0 -1
  59. package/dist/runtime-foundation.js +0 -1676
  60. package/dist/runtime-foundation.js.map +0 -1
  61. package/dist/websocket-runtime.d.ts +0 -2
  62. package/dist/websocket-runtime.d.ts.map +0 -1
  63. package/dist/websocket-runtime.js +0 -37
  64. package/dist/websocket-runtime.js.map +0 -1
  65. package/dist/worker-runtime.d.ts +0 -2
  66. package/dist/worker-runtime.d.ts.map +0 -1
  67. package/dist/worker-runtime.js +0 -83
  68. package/dist/worker-runtime.js.map +0 -1
package/dist/analyzer.js CHANGED
@@ -6,14 +6,15 @@ import { LOOK_ABSENT_MEDIA_SUBJECTS, LOOK_ARITHMETIC_HINT, LOOK_ANIMATION_DIRECT
6
6
  import { collectLookStaticValues, evaluateLookStaticExpression, isLookStaticValue, lookStaticCss } from "./look-static.js";
7
7
  import { keyframeCssValue } from "./keyframes.js";
8
8
  import { componentCallRefusal, componentRefHandleRefusal, componentSectionCountDiagnostics, watchedResourceSurfaceRefusal } from "./analysis/component-guidance.js";
9
- import { foldedLengthPercentage, isLookNumericType, lookAdditiveType } from "./analysis/look-values.js";
9
+ import { foldedLengthPercentage, isLookNumericType, lookAdditiveType, teachLookLengthSlot, teachLookPercentageSlot } from "./analysis/look-values.js";
10
10
  import { collectPublicConfigNames, declaredPublicConfig, publicConfigDiagnostic } from "./analysis/public-config.js";
11
+ import { collectDerivedReactiveNames, collectReactiveDerivations, collectReactiveStateNames, isRetiredAccessorName } from "./analysis/reactive-names.js";
11
12
  import { byCodeUnit } from "./stable-order.js";
12
13
  import { dynamicChildLeaves, JSX_SCALAR_TEXT_HINT } from "./emitter.js";
13
14
  import { isWebCustomElementName, WEB_ARIA_ATTRIBUTES, WEB_ARIA_ENUMERATED_VALUES, WEB_ARIA_ROLES, WEB_ARIA_ROLE_SYNONYMS, WEB_BOOL_PRESENCE_HTML_ATTRIBUTES, WEB_HTML_ELEMENTS, WEB_MISSPELLED_ATTRIBUTES, WEB_NATIVE_ELEMENTS, } from "./elements.js";
14
15
  import { isWebExpression, isWebJsx, isWebKeyframes, isWebLook, isWebStatement, isWebUnit, } from "./ast.js";
15
16
  import { isWebComponentConstructor, isWebComponentType, isWebComputedExport, isWebNodeType, normalizeWebComponentType, webComponentConstructor, webComponentHandle, webComponentIntrinsic, webComponentName, webNodeType, WEB_EVENT_TYPE_NAMES, WEB_OWNED_TYPE_NAMES, } from "./types.js";
16
- import { collectionMutators, collectReactiveWriters, finallySelfWrite, reactivePathOf, reactivePathRoot, statementBindsName, topLevelCall, watchSelfWrite, writerWritesPath, } from "./analysis/watch-cycles.js";
17
+ import { collectionMutators, collectReactiveWriters, finallySelfWrite, reactivePathOf, reactivePathRoot, watchDerivedSourceWrite, statementBindsName, topLevelCall, watchSelfWrite, writerWritesPath, } from "./analysis/watch-cycles.js";
17
18
  // The canonical nominal identity of the Web RouteContext record. Route checks
18
19
  // probe with this identity so they succeed in modules that use route() without
19
20
  // importing RouteContext by name.
@@ -842,38 +843,6 @@ function lookConditionTerms(expression, negated, staticValues) {
842
843
  const atom = lookConditionAtom(expression, negated, staticValues);
843
844
  return [[atom ?? `rt:${negated ? "!" : ""}${lookRuntimeSignature(expression)}`]];
844
845
  }
845
- /**
846
- * Every name in a module or component body whose read is reactive but whose
847
- * binding is not itself a state/prop reference: a computed accessor, a resource
848
- * handle, an action handle. A Look literal that reads one of these freezes it
849
- * exactly as it freezes a state read (LOK-D1).
850
- *
851
- * The retired `computed(...)` accessor stays in the callee test even though it
852
- * is not a global any more (D71 replaced it with the declaration): analysis
853
- * after a migration diagnostic still has to stay coherent, so the name a
854
- * retired declaration binds is treated as derived exactly as it was before.
855
- */
856
- function collectDerivedReactiveNames(program) {
857
- const names = new Set();
858
- const record = (statements) => {
859
- for (const statement of statements) {
860
- if (statement.kind === "VariableDeclaration" && statement.pattern.kind === "NameBindingPattern"
861
- && statement.initializer.kind === "CallExpression" && statement.initializer.callee.kind === "IdentifierExpression"
862
- && isRetiredAccessorName(statement.initializer.callee.name)) {
863
- names.add(statement.pattern.name);
864
- continue;
865
- }
866
- if (!isWebStatement(statement))
867
- continue;
868
- if (statement.kind === "ExtensionStatement:web:resource" || statement.kind === "ExtensionStatement:web:action")
869
- names.add(statement.name);
870
- if (statement.kind === "ExtensionStatement:web:component")
871
- record(statement.body);
872
- }
873
- };
874
- record(program.body);
875
- return names;
876
- }
877
846
  /**
878
847
  * Every `const name = look:` in the module, wherever it is written. A name
879
848
  * declared twice maps to null: two different Look literals under one name make
@@ -1140,10 +1109,6 @@ const RETIRED_ACCESSOR_TYPE = Object.freeze({
1140
1109
  requiredParameters: 1,
1141
1110
  result: retiredAccessorReaderType,
1142
1111
  });
1143
- /** The function spelling of a derived value; `computed` the declaration is what it becomes. */
1144
- function isRetiredAccessorName(name) {
1145
- return name === "computed";
1146
- }
1147
1112
  /**
1148
1113
  * D90 R15(a): a watch subject names a place in the reactive graph — the name of
1149
1114
  * a `state` or a `computed`, or a read path out of one — and never computes a
@@ -1782,6 +1747,10 @@ export class VelarWebAnalyzer extends Analyzer {
1782
1747
  resourceBindingSpans = new Set();
1783
1748
  /** D114 W A2(b): this module's `action` and `async def` bodies by name. */
1784
1749
  reactiveWriters = new Map();
1750
+ /** D114 P6 item 6 (ST-U2): each same-module `computed` and the names it reads on every evaluation. */
1751
+ reactiveDerivations = new Map();
1752
+ /** Each `state` this module declares, false where the name is declared twice. */
1753
+ reactiveStateNames = new Map();
1785
1754
  /** Local names bound to an imported `export computed`, from the Web interface. */
1786
1755
  importedComputedNames;
1787
1756
  /** The resolved spans of those imports, so a local shadow of the name is not one. */
@@ -1828,6 +1797,8 @@ export class VelarWebAnalyzer extends Analyzer {
1828
1797
  this.keyedListRebuilds.length = 0;
1829
1798
  this.moduleFunctions = collectModuleFunctions(program);
1830
1799
  this.reactiveWriters = collectReactiveWriters(program);
1800
+ this.reactiveDerivations = collectReactiveDerivations(program);
1801
+ this.reactiveStateNames = collectReactiveStateNames(program);
1831
1802
  super.analyze(program);
1832
1803
  this.reportStaticJsxKeys();
1833
1804
  this.reportRetiredComputedFunction();
@@ -2543,6 +2514,11 @@ export class VelarWebAnalyzer extends Analyzer {
2543
2514
  this.diagnostics.push(diagnostic("VEL5077", inFinally.message, inFinally.span));
2544
2515
  return;
2545
2516
  }
2517
+ const hop = watchDerivedSourceWrite(subject, statement, this.reactiveDerivations, this.reactiveStateNames, (name) => this.reactiveBindingKind(name) === "state");
2518
+ if (hop !== null) {
2519
+ this.diagnostics.push(diagnostic("VEL5077", hop, statement.span));
2520
+ return;
2521
+ }
2546
2522
  const call = topLevelCall(statement);
2547
2523
  if (call === null)
2548
2524
  continue;
@@ -3394,6 +3370,7 @@ export class VelarWebAnalyzer extends Analyzer {
3394
3370
  // was refused.
3395
3371
  const parameters = LOOK_BUILDER_SIGNATURES.get(builder)?.parameters;
3396
3372
  const before = this.diagnostics.length;
3373
+ let taught = false;
3397
3374
  for (const [index, argument] of expression.arguments.entries()) {
3398
3375
  const named = expression.argumentNames?.[index] ?? null;
3399
3376
  const position = named === null ? index : parameters?.indexOf(named) ?? -1;
@@ -3405,20 +3382,32 @@ export class VelarWebAnalyzer extends Analyzer {
3405
3382
  // charter names does not exist.
3406
3383
  const folded = evaluateLookStaticExpression(argument, this.lookStaticValues);
3407
3384
  const literal = folded?.kind === "number" ? folded.value : null;
3408
- if (range && literal !== null && (literal < range[1] || literal > range[2])) {
3409
- this.diagnostics.push(diagnostic("VEL5042", `${range[0]} must be from ${range[1]} through ${range[2]}; ${builder} received ${literal}`, argument.span));
3385
+ // D114 P6 item 2 (LK-I3): a slot whose domain carries a unit states its
3386
+ // bound in that unit and reads the value out of the unit's own number, so
3387
+ // `hsl(200, 120%, 50%)` is refused in the same VEL5042 wording that
3388
+ // refused `hsl(200, 120, 50)` before the slot became a `Percentage`.
3389
+ const rangeUnit = range?.[3];
3390
+ const ranged = rangeUnit === undefined ? literal
3391
+ : folded?.kind === "unit" && folded.unit === rangeUnit ? folded.value : null;
3392
+ if (range && ranged !== null && (ranged < range[1] || ranged > range[2])) {
3393
+ const unit = rangeUnit ?? "";
3394
+ this.diagnostics.push(diagnostic("VEL5042", `${range[0]} must be from ${range[1]}${unit} through ${range[2]}${unit}; ${builder} received ${ranged}${unit}`, argument.span));
3410
3395
  }
3396
+ if (rangeUnit === "%" && literal !== null && teachLookPercentageSlot(this.diagnostics, range[0], argument, literal))
3397
+ taught = true;
3411
3398
  const nonNegativeBlur = (builder === "blur" && position === 0) || (builder === "dropShadow" && position === 2);
3412
3399
  if (nonNegativeBlur && folded?.kind === "unit" && folded.value < 0) {
3413
3400
  this.diagnostics.push(diagnostic("VEL5042", `${builder} blur cannot be negative`, argument.span));
3414
3401
  }
3415
3402
  // LOK-D3, builder half: a unitless number in a length position is dead
3416
- // CSS exactly as it is on a property. Zero is the one unitless length.
3417
- if (LOOK_LENGTH_BUILDERS.has(builder) && literal !== null && literal !== 0
3403
+ // CSS exactly as it is on a property except in a slot whose own type
3404
+ // takes only a length or a percentage, where core's refusal is already
3405
+ // the report and gains the remedy rather than a neighbour.
3406
+ if (LOOK_LENGTH_BUILDERS.has(builder) && literal !== null
3418
3407
  && !(builder === "border" && position !== 0) && !(builder === "shadow" && position === 5)
3419
- && !(builder === "dropShadow" && position === 3)) {
3420
- this.diagnostics.push(diagnostic("VEL5042", `${builder} composes CSS lengths, so ${literal} requires a unit; write a unit value such as ${literal}px or ${literal}rem (only 0 is unitless)`, argument.span));
3421
- }
3408
+ && !(builder === "dropShadow" && position === 3)
3409
+ && teachLookLengthSlot(this.diagnostics, builder, position, argument, literal))
3410
+ taught = true;
3422
3411
  if (builder === "border" && position === 2 && argument.kind === "LiteralExpression" && typeof argument.value === "string"
3423
3412
  && !LOOK_BORDER_STYLE_NAMES.has(argument.value)) {
3424
3413
  this.diagnostics.push(diagnostic("VEL5042", `Border style '${argument.value}' is not a CSS border style; use one of ${[...LOOK_BORDER_STYLE_NAMES].join(", ")}`, argument.span));
@@ -3444,8 +3433,8 @@ export class VelarWebAnalyzer extends Analyzer {
3444
3433
  if (builder === "filters" && expression.arguments.length > 64) {
3445
3434
  this.diagnostics.push(diagnostic("VEL5042", "filters cannot compose more than 64 values", expression.span));
3446
3435
  }
3447
- // D114 0.29.0 LK-I2: a call its own argument check refused is recorded so a `keyframes:` stop can drop the consequence. The record is the call rather than the code, because a bad token name and a stop's one-declaration rule are two facts about one value (D103-2) and both are still reported.
3448
- if (this.diagnostics.length > before)
3436
+ // D114 0.29.0 LK-I2: a call its own argument check refused is recorded so a `keyframes:` stop can drop the consequence. The record is the call rather than the code, because a bad token name and a stop's one-declaration rule are two facts about one value (D103-2) and both are still reported. A slot lesson written *into* core's refusal grows no diagnostic, so it says so itself.
3437
+ if (taught || this.diagnostics.length > before)
3449
3438
  this.refusedBuilderCalls.push(expression.span);
3450
3439
  }
3451
3440
  /**