@llui/compiler 0.3.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/LICENSE +21 -0
- package/dist/accessor-resolver.d.ts +58 -0
- package/dist/accessor-resolver.d.ts.map +1 -0
- package/dist/accessor-resolver.js +119 -0
- package/dist/accessor-resolver.js.map +1 -0
- package/dist/binding-descriptors.d.ts +105 -0
- package/dist/binding-descriptors.d.ts.map +1 -0
- package/dist/binding-descriptors.js +340 -0
- package/dist/binding-descriptors.js.map +1 -0
- package/dist/collect-deps.d.ts +49 -0
- package/dist/collect-deps.d.ts.map +1 -0
- package/dist/collect-deps.js +444 -0
- package/dist/collect-deps.js.map +1 -0
- package/dist/compiler-cache.d.ts +20 -0
- package/dist/compiler-cache.d.ts.map +1 -0
- package/dist/compiler-cache.js +20 -0
- package/dist/compiler-cache.js.map +1 -0
- package/dist/cross-file-resolver.d.ts +109 -0
- package/dist/cross-file-resolver.d.ts.map +1 -0
- package/dist/cross-file-resolver.js +530 -0
- package/dist/cross-file-resolver.js.map +1 -0
- package/dist/cross-file-walker.d.ts +63 -0
- package/dist/cross-file-walker.d.ts.map +1 -0
- package/dist/cross-file-walker.js +516 -0
- package/dist/cross-file-walker.js.map +1 -0
- package/dist/diagnostic.d.ts +76 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +59 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/introspection-factory.d.ts +54 -0
- package/dist/introspection-factory.d.ts.map +1 -0
- package/dist/introspection-factory.js +46 -0
- package/dist/introspection-factory.js.map +1 -0
- package/dist/manifest.d.ts +144 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +209 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +222 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +256 -0
- package/dist/module.js.map +1 -0
- package/dist/modules/_element-helpers.d.ts +4 -0
- package/dist/modules/_element-helpers.d.ts.map +1 -0
- package/dist/modules/_element-helpers.js +138 -0
- package/dist/modules/_element-helpers.js.map +1 -0
- package/dist/modules/_msg-variants.d.ts +10 -0
- package/dist/modules/_msg-variants.d.ts.map +1 -0
- package/dist/modules/_msg-variants.js +97 -0
- package/dist/modules/_msg-variants.js.map +1 -0
- package/dist/modules/_shared.d.ts +16 -0
- package/dist/modules/_shared.d.ts.map +1 -0
- package/dist/modules/_shared.js +30 -0
- package/dist/modules/_shared.js.map +1 -0
- package/dist/modules/accessibility.d.ts +3 -0
- package/dist/modules/accessibility.d.ts.map +1 -0
- package/dist/modules/accessibility.js +82 -0
- package/dist/modules/accessibility.js.map +1 -0
- package/dist/modules/accessor-side-effect.d.ts +3 -0
- package/dist/modules/accessor-side-effect.d.ts.map +1 -0
- package/dist/modules/accessor-side-effect.js +113 -0
- package/dist/modules/accessor-side-effect.js.map +1 -0
- package/dist/modules/agent-emits-drift.d.ts +3 -0
- package/dist/modules/agent-emits-drift.d.ts.map +1 -0
- package/dist/modules/agent-emits-drift.js +158 -0
- package/dist/modules/agent-emits-drift.js.map +1 -0
- package/dist/modules/agent-example-on-payload.d.ts +3 -0
- package/dist/modules/agent-example-on-payload.d.ts.map +1 -0
- package/dist/modules/agent-example-on-payload.js +53 -0
- package/dist/modules/agent-example-on-payload.js.map +1 -0
- package/dist/modules/agent-exclusive-annotations.d.ts +3 -0
- package/dist/modules/agent-exclusive-annotations.d.ts.map +1 -0
- package/dist/modules/agent-exclusive-annotations.js +68 -0
- package/dist/modules/agent-exclusive-annotations.js.map +1 -0
- package/dist/modules/agent-missing-intent.d.ts +3 -0
- package/dist/modules/agent-missing-intent.d.ts.map +1 -0
- package/dist/modules/agent-missing-intent.js +47 -0
- package/dist/modules/agent-missing-intent.js.map +1 -0
- package/dist/modules/agent-msg-resolvable.d.ts +3 -0
- package/dist/modules/agent-msg-resolvable.d.ts.map +1 -0
- package/dist/modules/agent-msg-resolvable.js +161 -0
- package/dist/modules/agent-msg-resolvable.js.map +1 -0
- package/dist/modules/agent-nonextractable-handler.d.ts +3 -0
- package/dist/modules/agent-nonextractable-handler.d.ts.map +1 -0
- package/dist/modules/agent-nonextractable-handler.js +127 -0
- package/dist/modules/agent-nonextractable-handler.js.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts +3 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.js +67 -0
- package/dist/modules/agent-optional-field-undocumented.js.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts +3 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.js +58 -0
- package/dist/modules/agent-tagsend-translator-missing.js.map +1 -0
- package/dist/modules/agent-warning-on-confirm.d.ts +3 -0
- package/dist/modules/agent-warning-on-confirm.d.ts.map +1 -0
- package/dist/modules/agent-warning-on-confirm.js +46 -0
- package/dist/modules/agent-warning-on-confirm.js.map +1 -0
- package/dist/modules/async-update.d.ts +3 -0
- package/dist/modules/async-update.d.ts.map +1 -0
- package/dist/modules/async-update.js +86 -0
- package/dist/modules/async-update.js.map +1 -0
- package/dist/modules/binding-descriptors.d.ts +4 -0
- package/dist/modules/binding-descriptors.d.ts.map +1 -0
- package/dist/modules/binding-descriptors.js +48 -0
- package/dist/modules/binding-descriptors.js.map +1 -0
- package/dist/modules/bitmask-overflow.d.ts +3 -0
- package/dist/modules/bitmask-overflow.d.ts.map +1 -0
- package/dist/modules/bitmask-overflow.js +152 -0
- package/dist/modules/bitmask-overflow.js.map +1 -0
- package/dist/modules/compiler-stamp.d.ts +3 -0
- package/dist/modules/compiler-stamp.d.ts.map +1 -0
- package/dist/modules/compiler-stamp.js +44 -0
- package/dist/modules/compiler-stamp.js.map +1 -0
- package/dist/modules/component-meta.d.ts +3 -0
- package/dist/modules/component-meta.d.ts.map +1 -0
- package/dist/modules/component-meta.js +44 -0
- package/dist/modules/component-meta.js.map +1 -0
- package/dist/modules/controlled-input.d.ts +3 -0
- package/dist/modules/controlled-input.d.ts.map +1 -0
- package/dist/modules/controlled-input.js +68 -0
- package/dist/modules/controlled-input.js.map +1 -0
- package/dist/modules/core-synthesis.d.ts +18 -0
- package/dist/modules/core-synthesis.d.ts.map +1 -0
- package/dist/modules/core-synthesis.js +748 -0
- package/dist/modules/core-synthesis.js.map +1 -0
- package/dist/modules/direct-state-in-view.d.ts +3 -0
- package/dist/modules/direct-state-in-view.d.ts.map +1 -0
- package/dist/modules/direct-state-in-view.js +103 -0
- package/dist/modules/direct-state-in-view.js.map +1 -0
- package/dist/modules/each-closure-violation.d.ts +3 -0
- package/dist/modules/each-closure-violation.d.ts.map +1 -0
- package/dist/modules/each-closure-violation.js +255 -0
- package/dist/modules/each-closure-violation.js.map +1 -0
- package/dist/modules/each-memo.d.ts +15 -0
- package/dist/modules/each-memo.d.ts.map +1 -0
- package/dist/modules/each-memo.js +115 -0
- package/dist/modules/each-memo.js.map +1 -0
- package/dist/modules/effect-without-handler.d.ts +3 -0
- package/dist/modules/effect-without-handler.d.ts.map +1 -0
- package/dist/modules/effect-without-handler.js +92 -0
- package/dist/modules/effect-without-handler.js.map +1 -0
- package/dist/modules/element-rewrite.d.ts +22 -0
- package/dist/modules/element-rewrite.d.ts.map +1 -0
- package/dist/modules/element-rewrite.js +1017 -0
- package/dist/modules/element-rewrite.js.map +1 -0
- package/dist/modules/empty-props.d.ts +3 -0
- package/dist/modules/empty-props.d.ts.map +1 -0
- package/dist/modules/empty-props.js +50 -0
- package/dist/modules/empty-props.js.map +1 -0
- package/dist/modules/exhaustive-effect-handling.d.ts +3 -0
- package/dist/modules/exhaustive-effect-handling.d.ts.map +1 -0
- package/dist/modules/exhaustive-effect-handling.js +61 -0
- package/dist/modules/exhaustive-effect-handling.js.map +1 -0
- package/dist/modules/exhaustive-update.d.ts +3 -0
- package/dist/modules/exhaustive-update.d.ts.map +1 -0
- package/dist/modules/exhaustive-update.js +146 -0
- package/dist/modules/exhaustive-update.js.map +1 -0
- package/dist/modules/forgotten-spread.d.ts +3 -0
- package/dist/modules/forgotten-spread.d.ts.map +1 -0
- package/dist/modules/forgotten-spread.js +51 -0
- package/dist/modules/forgotten-spread.js.map +1 -0
- package/dist/modules/form-boilerplate.d.ts +3 -0
- package/dist/modules/form-boilerplate.d.ts.map +1 -0
- package/dist/modules/form-boilerplate.js +101 -0
- package/dist/modules/form-boilerplate.js.map +1 -0
- package/dist/modules/imperative-dom-in-view.d.ts +3 -0
- package/dist/modules/imperative-dom-in-view.d.ts.map +1 -0
- package/dist/modules/imperative-dom-in-view.js +123 -0
- package/dist/modules/imperative-dom-in-view.js.map +1 -0
- package/dist/modules/item-dedup.d.ts +7 -0
- package/dist/modules/item-dedup.d.ts.map +1 -0
- package/dist/modules/item-dedup.js +204 -0
- package/dist/modules/item-dedup.js.map +1 -0
- package/dist/modules/map-on-state-array.d.ts +3 -0
- package/dist/modules/map-on-state-array.d.ts.map +1 -0
- package/dist/modules/map-on-state-array.js +84 -0
- package/dist/modules/map-on-state-array.js.map +1 -0
- package/dist/modules/mask-legend.d.ts +10 -0
- package/dist/modules/mask-legend.d.ts.map +1 -0
- package/dist/modules/mask-legend.js +50 -0
- package/dist/modules/mask-legend.js.map +1 -0
- package/dist/modules/missing-memo.d.ts +3 -0
- package/dist/modules/missing-memo.d.ts.map +1 -0
- package/dist/modules/missing-memo.js +114 -0
- package/dist/modules/missing-memo.js.map +1 -0
- package/dist/modules/msg-annotations.d.ts +9 -0
- package/dist/modules/msg-annotations.d.ts.map +1 -0
- package/dist/modules/msg-annotations.js +54 -0
- package/dist/modules/msg-annotations.js.map +1 -0
- package/dist/modules/msg-schema.d.ts +10 -0
- package/dist/modules/msg-schema.d.ts.map +1 -0
- package/dist/modules/msg-schema.js +70 -0
- package/dist/modules/msg-schema.js.map +1 -0
- package/dist/modules/namespace-import.d.ts +3 -0
- package/dist/modules/namespace-import.d.ts.map +1 -0
- package/dist/modules/namespace-import.js +80 -0
- package/dist/modules/namespace-import.js.map +1 -0
- package/dist/modules/nested-send-in-update.d.ts +3 -0
- package/dist/modules/nested-send-in-update.d.ts.map +1 -0
- package/dist/modules/nested-send-in-update.js +77 -0
- package/dist/modules/nested-send-in-update.js.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts +3 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js +100 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js.map +1 -0
- package/dist/modules/no-eager-item-accessor.d.ts +3 -0
- package/dist/modules/no-eager-item-accessor.d.ts.map +1 -0
- package/dist/modules/no-eager-item-accessor.js +74 -0
- package/dist/modules/no-eager-item-accessor.js.map +1 -0
- package/dist/modules/no-let-reactive-accessor.d.ts +3 -0
- package/dist/modules/no-let-reactive-accessor.d.ts.map +1 -0
- package/dist/modules/no-let-reactive-accessor.js +227 -0
- package/dist/modules/no-let-reactive-accessor.js.map +1 -0
- package/dist/modules/no-list-render-in-sample.d.ts +3 -0
- package/dist/modules/no-list-render-in-sample.d.ts.map +1 -0
- package/dist/modules/no-list-render-in-sample.js +89 -0
- package/dist/modules/no-list-render-in-sample.js.map +1 -0
- package/dist/modules/no-sample-in-accessor.d.ts +3 -0
- package/dist/modules/no-sample-in-accessor.d.ts.map +1 -0
- package/dist/modules/no-sample-in-accessor.js +141 -0
- package/dist/modules/no-sample-in-accessor.js.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts +3 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.js +72 -0
- package/dist/modules/no-sample-in-reactive-position.js.map +1 -0
- package/dist/modules/pure-update-function.d.ts +3 -0
- package/dist/modules/pure-update-function.d.ts.map +1 -0
- package/dist/modules/pure-update-function.js +127 -0
- package/dist/modules/pure-update-function.js.map +1 -0
- package/dist/modules/reactive-paths.d.ts +3 -0
- package/dist/modules/reactive-paths.d.ts.map +1 -0
- package/dist/modules/reactive-paths.js +77 -0
- package/dist/modules/reactive-paths.js.map +1 -0
- package/dist/modules/row-factory.d.ts +12 -0
- package/dist/modules/row-factory.d.ts.map +1 -0
- package/dist/modules/row-factory.js +385 -0
- package/dist/modules/row-factory.js.map +1 -0
- package/dist/modules/schema-hash.d.ts +15 -0
- package/dist/modules/schema-hash.d.ts.map +1 -0
- package/dist/modules/schema-hash.js +70 -0
- package/dist/modules/schema-hash.js.map +1 -0
- package/dist/modules/spread-in-children.d.ts +3 -0
- package/dist/modules/spread-in-children.d.ts.map +1 -0
- package/dist/modules/spread-in-children.js +144 -0
- package/dist/modules/spread-in-children.js.map +1 -0
- package/dist/modules/state-mutation.d.ts +3 -0
- package/dist/modules/state-mutation.d.ts.map +1 -0
- package/dist/modules/state-mutation.js +138 -0
- package/dist/modules/state-mutation.js.map +1 -0
- package/dist/modules/state-schema.d.ts +8 -0
- package/dist/modules/state-schema.d.ts.map +1 -0
- package/dist/modules/state-schema.js +55 -0
- package/dist/modules/state-schema.js.map +1 -0
- package/dist/modules/static-items.d.ts +3 -0
- package/dist/modules/static-items.d.ts.map +1 -0
- package/dist/modules/static-items.js +125 -0
- package/dist/modules/static-items.js.map +1 -0
- package/dist/modules/static-on.d.ts +3 -0
- package/dist/modules/static-on.d.ts.map +1 -0
- package/dist/modules/static-on.js +100 -0
- package/dist/modules/static-on.js.map +1 -0
- package/dist/modules/string-effect-callback.d.ts +3 -0
- package/dist/modules/string-effect-callback.d.ts.map +1 -0
- package/dist/modules/string-effect-callback.js +50 -0
- package/dist/modules/string-effect-callback.js.map +1 -0
- package/dist/modules/structural-mask.d.ts +8 -0
- package/dist/modules/structural-mask.d.ts.map +1 -0
- package/dist/modules/structural-mask.js +76 -0
- package/dist/modules/structural-mask.js.map +1 -0
- package/dist/modules/subapp-requires-reason.d.ts +3 -0
- package/dist/modules/subapp-requires-reason.d.ts.map +1 -0
- package/dist/modules/subapp-requires-reason.js +129 -0
- package/dist/modules/subapp-requires-reason.js.map +1 -0
- package/dist/modules/text-mask.d.ts +12 -0
- package/dist/modules/text-mask.d.ts.map +1 -0
- package/dist/modules/text-mask.js +63 -0
- package/dist/modules/text-mask.js.map +1 -0
- package/dist/modules/view-bag-import.d.ts +3 -0
- package/dist/modules/view-bag-import.d.ts.map +1 -0
- package/dist/modules/view-bag-import.js +80 -0
- package/dist/modules/view-bag-import.js.map +1 -0
- package/dist/msg-annotations.d.ts +104 -0
- package/dist/msg-annotations.d.ts.map +1 -0
- package/dist/msg-annotations.js +242 -0
- package/dist/msg-annotations.js.map +1 -0
- package/dist/msg-schema.d.ts +130 -0
- package/dist/msg-schema.d.ts.map +1 -0
- package/dist/msg-schema.js +770 -0
- package/dist/msg-schema.js.map +1 -0
- package/dist/schema-hash.d.ts +16 -0
- package/dist/schema-hash.d.ts.map +1 -0
- package/dist/schema-hash.js +31 -0
- package/dist/schema-hash.js.map +1 -0
- package/dist/state-schema.d.ts +41 -0
- package/dist/state-schema.d.ts.map +1 -0
- package/dist/state-schema.js +156 -0
- package/dist/state-schema.js.map +1 -0
- package/dist/transform.d.ts +109 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +1390 -0
- package/dist/transform.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// `static-on` — errors when `scope({ on })` or `branch({ on })` receives
|
|
2
|
+
// a discriminant accessor that doesn't read any state. The key never
|
|
3
|
+
// changes, so the subtree mounts once and stagnates. Migrated from
|
|
4
|
+
// `@llui/eslint-plugin/src/rules/static-on.ts`.
|
|
5
|
+
import ts from 'typescript';
|
|
6
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
7
|
+
function bodyMayRead(body) {
|
|
8
|
+
let found = false;
|
|
9
|
+
const walk = (n) => {
|
|
10
|
+
if (found)
|
|
11
|
+
return;
|
|
12
|
+
if (ts.isCallExpression(n) ||
|
|
13
|
+
ts.isPropertyAccessExpression(n) ||
|
|
14
|
+
ts.isElementAccessExpression(n)) {
|
|
15
|
+
found = true;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
ts.forEachChild(n, walk);
|
|
19
|
+
};
|
|
20
|
+
walk(body);
|
|
21
|
+
return found;
|
|
22
|
+
}
|
|
23
|
+
function readsParam(body, paramName) {
|
|
24
|
+
let found = false;
|
|
25
|
+
const walk = (n) => {
|
|
26
|
+
if (found)
|
|
27
|
+
return;
|
|
28
|
+
if (ts.isIdentifier(n) && n.text === paramName) {
|
|
29
|
+
const parent = n.parent;
|
|
30
|
+
if (parent && ts.isParameter(parent) && parent.name === n)
|
|
31
|
+
return;
|
|
32
|
+
found = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
ts.forEachChild(n, walk);
|
|
36
|
+
};
|
|
37
|
+
walk(body);
|
|
38
|
+
return found;
|
|
39
|
+
}
|
|
40
|
+
export function staticOnModule() {
|
|
41
|
+
return {
|
|
42
|
+
name: 'static-on',
|
|
43
|
+
compilerVersion: '^0.3.0',
|
|
44
|
+
diagnostics: [
|
|
45
|
+
{
|
|
46
|
+
id: 'llui/static-on',
|
|
47
|
+
description: '`scope`/`branch` `on` accessor reads no state — key never changes.',
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
visitors: {
|
|
51
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
52
|
+
const visited = node;
|
|
53
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
54
|
+
const walk = (n) => {
|
|
55
|
+
if (ts.isCallExpression(n) &&
|
|
56
|
+
ts.isIdentifier(n.expression) &&
|
|
57
|
+
(n.expression.text === 'scope' || n.expression.text === 'branch')) {
|
|
58
|
+
const name = n.expression.text;
|
|
59
|
+
const opts = n.arguments[0];
|
|
60
|
+
if (opts && ts.isObjectLiteralExpression(opts)) {
|
|
61
|
+
const onProp = opts.properties.find((p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'on');
|
|
62
|
+
if (onProp) {
|
|
63
|
+
const v = onProp.initializer;
|
|
64
|
+
if (ts.isArrowFunction(v) || ts.isFunctionExpression(v)) {
|
|
65
|
+
let isStatic = false;
|
|
66
|
+
if (v.parameters.length === 0) {
|
|
67
|
+
isStatic = v.body ? !bodyMayRead(v.body) : true;
|
|
68
|
+
}
|
|
69
|
+
else if (v.parameters.length === 1) {
|
|
70
|
+
const param = v.parameters[0];
|
|
71
|
+
if (ts.isIdentifier(param.name)) {
|
|
72
|
+
isStatic = v.body ? !readsParam(v.body, param.name.text) : true;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
if (isStatic) {
|
|
76
|
+
ctx.reportDiagnostic({
|
|
77
|
+
id: 'llui/static-on',
|
|
78
|
+
severity: 'error',
|
|
79
|
+
category: 'reactivity',
|
|
80
|
+
message: `\`${name}()\`'s \`on\` accessor reads no state — the key never changes, ` +
|
|
81
|
+
`so the subtree mounts once and never rebuilds. Reference the state field(s) ` +
|
|
82
|
+
`that drive the discriminant, e.g. \`on: (s) => s.activeTab\`.`,
|
|
83
|
+
location: {
|
|
84
|
+
file: sf.fileName,
|
|
85
|
+
range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
ts.forEachChild(n, walk);
|
|
94
|
+
};
|
|
95
|
+
walk(sf);
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=static-on.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-on.js","sourceRoot":"","sources":["../../src/modules/static-on.ts"],"names":[],"mappings":"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","sourcesContent":["// `static-on` — errors when `scope({ on })` or `branch({ on })` receives\n// a discriminant accessor that doesn't read any state. The key never\n// changes, so the subtree mounts once and stagnates. Migrated from\n// `@llui/eslint-plugin/src/rules/static-on.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\nfunction bodyMayRead(body: ts.Node): boolean {\n let found = false\n const walk = (n: ts.Node): void => {\n if (found) return\n if (\n ts.isCallExpression(n) ||\n ts.isPropertyAccessExpression(n) ||\n ts.isElementAccessExpression(n)\n ) {\n found = true\n return\n }\n ts.forEachChild(n, walk)\n }\n walk(body)\n return found\n}\n\nfunction readsParam(body: ts.Node, paramName: string): boolean {\n let found = false\n const walk = (n: ts.Node): void => {\n if (found) return\n if (ts.isIdentifier(n) && n.text === paramName) {\n const parent = n.parent\n if (parent && ts.isParameter(parent) && parent.name === n) return\n found = true\n return\n }\n ts.forEachChild(n, walk)\n }\n walk(body)\n return found\n}\n\nexport function staticOnModule(): CompilerModule {\n return {\n name: 'static-on',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/static-on',\n description: '`scope`/`branch` `on` accessor reads no state — key never changes.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n const walk = (n: ts.Node): void => {\n if (\n ts.isCallExpression(n) &&\n ts.isIdentifier(n.expression) &&\n (n.expression.text === 'scope' || n.expression.text === 'branch')\n ) {\n const name = n.expression.text\n const opts = n.arguments[0]\n if (opts && ts.isObjectLiteralExpression(opts)) {\n const onProp = opts.properties.find(\n (p): p is ts.PropertyAssignment =>\n ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === 'on',\n )\n if (onProp) {\n const v = onProp.initializer\n if (ts.isArrowFunction(v) || ts.isFunctionExpression(v)) {\n let isStatic = false\n if (v.parameters.length === 0) {\n isStatic = v.body ? !bodyMayRead(v.body) : true\n } else if (v.parameters.length === 1) {\n const param = v.parameters[0]!\n if (ts.isIdentifier(param.name)) {\n isStatic = v.body ? !readsParam(v.body, param.name.text) : true\n }\n }\n if (isStatic) {\n ctx.reportDiagnostic({\n id: 'llui/static-on',\n severity: 'error',\n category: 'reactivity',\n message:\n `\\`${name}()\\`'s \\`on\\` accessor reads no state — the key never changes, ` +\n `so the subtree mounts once and never rebuilds. Reference the state field(s) ` +\n `that drive the discriminant, e.g. \\`on: (s) => s.activeTab\\`.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),\n },\n })\n }\n }\n }\n }\n }\n ts.forEachChild(n, walk)\n }\n walk(sf)\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string-effect-callback.d.ts","sourceRoot":"","sources":["../../src/modules/string-effect-callback.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAIlD,wBAAgB,0BAA0B,IAAI,cAAc,CA4C3D"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// `string-effect-callback` — errors when an effect callback property
|
|
2
|
+
// (onSuccess, onError, onLoad, onChange, onMessage) is assigned a
|
|
3
|
+
// bare string. The deprecated string shape silently drops typing;
|
|
4
|
+
// the typed shape is `propName: (data) => ({ type: 'msgType', payload: data })`.
|
|
5
|
+
// Migrated from `@llui/eslint-plugin/src/rules/string-effect-callback.ts`.
|
|
6
|
+
import ts from 'typescript';
|
|
7
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
8
|
+
const CALLBACK_PROPS = new Set(['onSuccess', 'onError', 'onLoad', 'onChange', 'onMessage']);
|
|
9
|
+
export function stringEffectCallbackModule() {
|
|
10
|
+
return {
|
|
11
|
+
name: 'string-effect-callback',
|
|
12
|
+
compilerVersion: '^0.3.0',
|
|
13
|
+
diagnostics: [
|
|
14
|
+
{
|
|
15
|
+
id: 'llui/string-effect-callback',
|
|
16
|
+
description: 'String-based effect callback is deprecated — use a typed message constructor.',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
visitors: {
|
|
20
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
21
|
+
const visited = node;
|
|
22
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
23
|
+
const walk = (n) => {
|
|
24
|
+
if (ts.isPropertyAssignment(n) &&
|
|
25
|
+
ts.isIdentifier(n.name) &&
|
|
26
|
+
CALLBACK_PROPS.has(n.name.text) &&
|
|
27
|
+
ts.isStringLiteral(n.initializer)) {
|
|
28
|
+
const propName = n.name.text;
|
|
29
|
+
const msgType = n.initializer.text;
|
|
30
|
+
ctx.reportDiagnostic({
|
|
31
|
+
id: 'llui/string-effect-callback',
|
|
32
|
+
severity: 'error',
|
|
33
|
+
category: 'agent',
|
|
34
|
+
message: `String-based effect callback \`${propName}: '${msgType}'\` is deprecated. ` +
|
|
35
|
+
`Use a typed message constructor: ` +
|
|
36
|
+
`\`${propName}: (data) => ({ type: '${msgType}', payload: data })\`.`,
|
|
37
|
+
location: {
|
|
38
|
+
file: sf.fileName,
|
|
39
|
+
range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
ts.forEachChild(n, walk);
|
|
44
|
+
};
|
|
45
|
+
walk(sf);
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=string-effect-callback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string-effect-callback.js","sourceRoot":"","sources":["../../src/modules/string-effect-callback.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,kEAAkE;AAClE,kEAAkE;AAClE,iFAAiF;AACjF,2EAA2E;AAE3E,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAGnD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAA;AAE3F,MAAM,UAAU,0BAA0B;IACxC,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE;YACX;gBACE,EAAE,EAAE,6BAA6B;gBACjC,WAAW,EACT,+EAA+E;aAClF;SACF;QACD,QAAQ,EAAE;YACR,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBACxC,MAAM,OAAO,GAAG,IAAqB,CAAA;gBACrC,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;gBAC5F,MAAM,IAAI,GAAG,CAAC,CAAU,EAAQ,EAAE;oBAChC,IACE,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC;wBAC1B,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC;wBACvB,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;wBAC/B,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC,EACjC,CAAC;wBACD,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAA;wBAC5B,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,CAAC,IAAI,CAAA;wBAClC,GAAG,CAAC,gBAAgB,CAAC;4BACnB,EAAE,EAAE,6BAA6B;4BACjC,QAAQ,EAAE,OAAO;4BACjB,QAAQ,EAAE,OAAO;4BACjB,OAAO,EACL,kCAAkC,QAAQ,MAAM,OAAO,qBAAqB;gCAC5E,mCAAmC;gCACnC,KAAK,QAAQ,yBAAyB,OAAO,wBAAwB;4BACvE,QAAQ,EAAE;gCACR,IAAI,EAAE,EAAE,CAAC,QAAQ;gCACjB,KAAK,EAAE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;6BAC7D;yBACF,CAAC,CAAA;oBACJ,CAAC;oBACD,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;gBAC1B,CAAC,CAAA;gBACD,IAAI,CAAC,EAAE,CAAC,CAAA;YACV,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["// `string-effect-callback` — errors when an effect callback property\n// (onSuccess, onError, onLoad, onChange, onMessage) is assigned a\n// bare string. The deprecated string shape silently drops typing;\n// the typed shape is `propName: (data) => ({ type: 'msgType', payload: data })`.\n// Migrated from `@llui/eslint-plugin/src/rules/string-effect-callback.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\nconst CALLBACK_PROPS = new Set(['onSuccess', 'onError', 'onLoad', 'onChange', 'onMessage'])\n\nexport function stringEffectCallbackModule(): CompilerModule {\n return {\n name: 'string-effect-callback',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/string-effect-callback',\n description:\n 'String-based effect callback is deprecated — use a typed message constructor.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n const walk = (n: ts.Node): void => {\n if (\n ts.isPropertyAssignment(n) &&\n ts.isIdentifier(n.name) &&\n CALLBACK_PROPS.has(n.name.text) &&\n ts.isStringLiteral(n.initializer)\n ) {\n const propName = n.name.text\n const msgType = n.initializer.text\n ctx.reportDiagnostic({\n id: 'llui/string-effect-callback',\n severity: 'error',\n category: 'agent',\n message:\n `String-based effect callback \\`${propName}: '${msgType}'\\` is deprecated. ` +\n `Use a typed message constructor: ` +\n `\\`${propName}: (data) => ({ type: '${msgType}', payload: data })\\`.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),\n },\n })\n }\n ts.forEachChild(n, walk)\n }\n walk(sf)\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CompilerModule } from '../module.js';
|
|
2
|
+
export interface StructuralMaskModuleOptions {
|
|
3
|
+
fieldBits: Map<string, number>;
|
|
4
|
+
viewHelperNames: Set<string>;
|
|
5
|
+
viewHelperAliases: Map<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export declare function structuralMaskModule(options: StructuralMaskModuleOptions): CompilerModule;
|
|
8
|
+
//# sourceMappingURL=structural-mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural-mask.d.ts","sourceRoot":"","sources":["../../src/modules/structural-mask.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAIlD,MAAM,WAAW,2BAA2B;IAC1C,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACvC;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,cAAc,CA2DzF"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// `structural-mask` — injects `__mask` into the options object of
|
|
2
|
+
// `each()`, `branch()`, `scope()`, and `show()` calls. The runtime
|
|
3
|
+
// uses this mask to skip Phase 1 reconciliation when irrelevant
|
|
4
|
+
// state changed (e.g. an each() that reads `rows` is skipped when
|
|
5
|
+
// only `selected` changed).
|
|
6
|
+
//
|
|
7
|
+
// Analyzes the *driving* accessor — `items` for each, `on` for
|
|
8
|
+
// branch/scope, `when` for show — and computes the bitmask of
|
|
9
|
+
// state fields it reads via `computeAccessorMask`. The accessor is
|
|
10
|
+
// resolved through `resolveAccessorBody`, so inline arrows, inline
|
|
11
|
+
// `memo(arrow)`, and identifier references to const-bound arrows /
|
|
12
|
+
// memos / function declarations all participate. Anything else
|
|
13
|
+
// leaves the call unchanged; the runtime falls back to FULL_MASK.
|
|
14
|
+
//
|
|
15
|
+
// Fires top-down (transformCallEnter) — runs before subsequent
|
|
16
|
+
// passes see the call. Idempotent: re-running on an already-masked
|
|
17
|
+
// call returns null (the existing `__mask` property is detected).
|
|
18
|
+
// Activation gate (`fieldBits.size > 0`) mirrors the inline check
|
|
19
|
+
// that returned null when no reactive paths exist.
|
|
20
|
+
import ts from 'typescript';
|
|
21
|
+
import { resolveAccessorBody } from '../accessor-resolver.js';
|
|
22
|
+
import { computeAccessorMask, createMaskLiteral, isHelperCall } from '../transform.js';
|
|
23
|
+
export function structuralMaskModule(options) {
|
|
24
|
+
const { fieldBits, viewHelperNames, viewHelperAliases } = options;
|
|
25
|
+
return {
|
|
26
|
+
name: 'structural-mask',
|
|
27
|
+
compilerVersion: '^0.3.0',
|
|
28
|
+
diagnostics: [],
|
|
29
|
+
visitors: {},
|
|
30
|
+
transformCallEnter(ctx, node) {
|
|
31
|
+
if (fieldBits.size === 0)
|
|
32
|
+
return null;
|
|
33
|
+
const isEach = isHelperCall(node.expression, 'each', viewHelperNames, viewHelperAliases);
|
|
34
|
+
const isBranch = isHelperCall(node.expression, 'branch', viewHelperNames, viewHelperAliases);
|
|
35
|
+
const isScope = isHelperCall(node.expression, 'scope', viewHelperNames, viewHelperAliases);
|
|
36
|
+
const isShow = isHelperCall(node.expression, 'show', viewHelperNames, viewHelperAliases);
|
|
37
|
+
if (!isEach && !isBranch && !isScope && !isShow)
|
|
38
|
+
return null;
|
|
39
|
+
const optsArg = node.arguments[0];
|
|
40
|
+
if (!optsArg || !ts.isObjectLiteralExpression(optsArg))
|
|
41
|
+
return null;
|
|
42
|
+
// Idempotent — skip if `__mask` already present.
|
|
43
|
+
for (const prop of optsArg.properties) {
|
|
44
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
45
|
+
ts.isIdentifier(prop.name) &&
|
|
46
|
+
prop.name.text === '__mask') {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const driverProp = isEach ? 'items' : isBranch || isScope ? 'on' : 'when';
|
|
51
|
+
let driverAccessor = null;
|
|
52
|
+
for (const prop of optsArg.properties) {
|
|
53
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
54
|
+
ts.isIdentifier(prop.name) &&
|
|
55
|
+
prop.name.text === driverProp) {
|
|
56
|
+
driverAccessor = resolveAccessorBody(prop.initializer);
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (!driverAccessor)
|
|
61
|
+
return null;
|
|
62
|
+
const { mask } = computeAccessorMask(driverAccessor, fieldBits);
|
|
63
|
+
if (mask === 0 || mask === (0xffffffff | 0))
|
|
64
|
+
return null;
|
|
65
|
+
const f = ctx.factory;
|
|
66
|
+
const maskProp = f.createPropertyAssignment('__mask', createMaskLiteral(f, mask));
|
|
67
|
+
const newProps = [...optsArg.properties, maskProp];
|
|
68
|
+
const newOpts = f.createObjectLiteralExpression(newProps, optsArg.properties.hasTrailingComma);
|
|
69
|
+
return f.createCallExpression(node.expression, node.typeArguments, [
|
|
70
|
+
newOpts,
|
|
71
|
+
...node.arguments.slice(1),
|
|
72
|
+
]);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=structural-mask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structural-mask.js","sourceRoot":"","sources":["../../src/modules/structural-mask.ts"],"names":[],"mappings":"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","sourcesContent":["// `structural-mask` — injects `__mask` into the options object of\n// `each()`, `branch()`, `scope()`, and `show()` calls. The runtime\n// uses this mask to skip Phase 1 reconciliation when irrelevant\n// state changed (e.g. an each() that reads `rows` is skipped when\n// only `selected` changed).\n//\n// Analyzes the *driving* accessor — `items` for each, `on` for\n// branch/scope, `when` for show — and computes the bitmask of\n// state fields it reads via `computeAccessorMask`. The accessor is\n// resolved through `resolveAccessorBody`, so inline arrows, inline\n// `memo(arrow)`, and identifier references to const-bound arrows /\n// memos / function declarations all participate. Anything else\n// leaves the call unchanged; the runtime falls back to FULL_MASK.\n//\n// Fires top-down (transformCallEnter) — runs before subsequent\n// passes see the call. Idempotent: re-running on an already-masked\n// call returns null (the existing `__mask` property is detected).\n// Activation gate (`fieldBits.size > 0`) mirrors the inline check\n// that returned null when no reactive paths exist.\n\nimport ts from 'typescript'\nimport type { CompilerModule } from '../module.js'\nimport { resolveAccessorBody } from '../accessor-resolver.js'\nimport { computeAccessorMask, createMaskLiteral, isHelperCall } from '../transform.js'\n\nexport interface StructuralMaskModuleOptions {\n fieldBits: Map<string, number>\n viewHelperNames: Set<string>\n viewHelperAliases: Map<string, string>\n}\n\nexport function structuralMaskModule(options: StructuralMaskModuleOptions): CompilerModule {\n const { fieldBits, viewHelperNames, viewHelperAliases } = options\n return {\n name: 'structural-mask',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n visitors: {},\n\n transformCallEnter(ctx, node) {\n if (fieldBits.size === 0) return null\n\n const isEach = isHelperCall(node.expression, 'each', viewHelperNames, viewHelperAliases)\n const isBranch = isHelperCall(node.expression, 'branch', viewHelperNames, viewHelperAliases)\n const isScope = isHelperCall(node.expression, 'scope', viewHelperNames, viewHelperAliases)\n const isShow = isHelperCall(node.expression, 'show', viewHelperNames, viewHelperAliases)\n if (!isEach && !isBranch && !isScope && !isShow) return null\n\n const optsArg = node.arguments[0]\n if (!optsArg || !ts.isObjectLiteralExpression(optsArg)) return null\n\n // Idempotent — skip if `__mask` already present.\n for (const prop of optsArg.properties) {\n if (\n ts.isPropertyAssignment(prop) &&\n ts.isIdentifier(prop.name) &&\n prop.name.text === '__mask'\n ) {\n return null\n }\n }\n\n const driverProp = isEach ? 'items' : isBranch || isScope ? 'on' : 'when'\n let driverAccessor: ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration | null =\n null\n for (const prop of optsArg.properties) {\n if (\n ts.isPropertyAssignment(prop) &&\n ts.isIdentifier(prop.name) &&\n prop.name.text === driverProp\n ) {\n driverAccessor = resolveAccessorBody(prop.initializer)\n break\n }\n }\n if (!driverAccessor) return null\n\n const { mask } = computeAccessorMask(driverAccessor, fieldBits)\n if (mask === 0 || mask === (0xffffffff | 0)) return null\n\n const f = ctx.factory\n const maskProp = f.createPropertyAssignment('__mask', createMaskLiteral(f, mask))\n const newProps = [...optsArg.properties, maskProp]\n const newOpts = f.createObjectLiteralExpression(newProps, optsArg.properties.hasTrailingComma)\n return f.createCallExpression(node.expression, node.typeArguments, [\n newOpts,\n ...node.arguments.slice(1),\n ])\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subapp-requires-reason.d.ts","sourceRoot":"","sources":["../../src/modules/subapp-requires-reason.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAyBlD,wBAAgB,0BAA0B,IAAI,cAAc,CAiH3D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// `subapp-requires-reason` — errors when a `subApp({...})` call lacks a
|
|
2
|
+
// non-empty string-literal `reason` property. `reason` is a sticky
|
|
3
|
+
// comment documenting WHY a state-isolation boundary is needed rather
|
|
4
|
+
// than a view function — meant to be auditable the way an
|
|
5
|
+
// `eslint-disable` comment is. Migrated from
|
|
6
|
+
// `@llui/eslint-plugin/src/rules/subapp-requires-reason.ts`.
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
9
|
+
const ORG_EXCUSE_REGEXES = [
|
|
10
|
+
/\bcode\s+organi[zs]ation\b/i,
|
|
11
|
+
/\b(break|breaking|split|splitting)\s+(this|up)\b/i,
|
|
12
|
+
/\b(felt|just)\s+like\b/i,
|
|
13
|
+
/\bsubcomponent\b/i,
|
|
14
|
+
];
|
|
15
|
+
/** Resolve a local `const NAME = "..."` to its string literal value, if any. */
|
|
16
|
+
function resolveLocalStringConst(sf, name) {
|
|
17
|
+
for (const stmt of sf.statements) {
|
|
18
|
+
if (!ts.isVariableStatement(stmt))
|
|
19
|
+
continue;
|
|
20
|
+
if (!(stmt.declarationList.flags & ts.NodeFlags.Const))
|
|
21
|
+
continue;
|
|
22
|
+
for (const d of stmt.declarationList.declarations) {
|
|
23
|
+
if (!ts.isIdentifier(d.name) || d.name.text !== name)
|
|
24
|
+
continue;
|
|
25
|
+
const init = d.initializer;
|
|
26
|
+
if (!init)
|
|
27
|
+
return null;
|
|
28
|
+
if (ts.isStringLiteral(init))
|
|
29
|
+
return init.text;
|
|
30
|
+
if (ts.isNoSubstitutionTemplateLiteral(init))
|
|
31
|
+
return init.text;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
export function subappRequiresReasonModule() {
|
|
37
|
+
return {
|
|
38
|
+
name: 'subapp-requires-reason',
|
|
39
|
+
compilerVersion: '^0.3.0',
|
|
40
|
+
diagnostics: [
|
|
41
|
+
{
|
|
42
|
+
id: 'llui/subapp-requires-reason',
|
|
43
|
+
description: 'subApp() requires a non-empty string-literal `reason` documenting why a state boundary is needed.',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
visitors: {
|
|
47
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
48
|
+
const visited = node;
|
|
49
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
50
|
+
const report = (n, message) => {
|
|
51
|
+
ctx.reportDiagnostic({
|
|
52
|
+
id: 'llui/subapp-requires-reason',
|
|
53
|
+
severity: 'error',
|
|
54
|
+
category: 'composition',
|
|
55
|
+
message,
|
|
56
|
+
location: {
|
|
57
|
+
file: sf.fileName,
|
|
58
|
+
range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
const checkText = (text, reportAt) => {
|
|
63
|
+
const trimmed = text.trim();
|
|
64
|
+
if (trimmed === '') {
|
|
65
|
+
report(reportAt, `subApp()'s \`reason\` must be a non-empty string. Decomposing for code organization ` +
|
|
66
|
+
`is not a valid reason — write a view function instead.`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (ORG_EXCUSE_REGEXES.some((re) => re.test(trimmed))) {
|
|
70
|
+
report(reportAt, `subApp() \`reason\` looks like a code-organization excuse ('${trimmed}'). Real ` +
|
|
71
|
+
`reasons name foreign lifecycle, isolated frame budget, or sealed state. For ` +
|
|
72
|
+
`decomposition, write a view function — see docs/proposals/unified-composition-model.md.`);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const walk = (n) => {
|
|
76
|
+
if (ts.isCallExpression(n)) {
|
|
77
|
+
let calleeName = null;
|
|
78
|
+
if (ts.isIdentifier(n.expression))
|
|
79
|
+
calleeName = n.expression.text;
|
|
80
|
+
else if (ts.isPropertyAccessExpression(n.expression) &&
|
|
81
|
+
ts.isIdentifier(n.expression.name))
|
|
82
|
+
calleeName = n.expression.name.text;
|
|
83
|
+
if (calleeName === 'subApp') {
|
|
84
|
+
const arg = n.arguments[0];
|
|
85
|
+
if (arg && ts.isObjectLiteralExpression(arg)) {
|
|
86
|
+
const reasonProp = arg.properties.find((p) => ts.isPropertyAssignment(p) &&
|
|
87
|
+
((ts.isIdentifier(p.name) && p.name.text === 'reason') ||
|
|
88
|
+
(ts.isStringLiteral(p.name) && p.name.text === 'reason')));
|
|
89
|
+
if (!reasonProp) {
|
|
90
|
+
report(arg, `subApp() requires a \`reason\` property. Add a string literal naming WHY a ` +
|
|
91
|
+
`state-isolation boundary is needed here rather than a view function ` +
|
|
92
|
+
`(e.g. "Monaco owns its own DOM lifecycle").`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
const v = reasonProp.initializer;
|
|
96
|
+
if (ts.isStringLiteral(v) || ts.isNoSubstitutionTemplateLiteral(v)) {
|
|
97
|
+
checkText(v.text, v);
|
|
98
|
+
}
|
|
99
|
+
else if (ts.isTemplateExpression(v)) {
|
|
100
|
+
// Template with expressions → computed
|
|
101
|
+
report(v, `subApp()'s \`reason\` must be a string literal so reviewers can grep for it. ` +
|
|
102
|
+
`A computed string defeats the audit-trail purpose.`);
|
|
103
|
+
}
|
|
104
|
+
else if (ts.isIdentifier(v)) {
|
|
105
|
+
const resolved = resolveLocalStringConst(sf, v.text);
|
|
106
|
+
if (resolved !== null) {
|
|
107
|
+
checkText(resolved, v);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
report(v, `subApp()'s \`reason\` must be a string literal so reviewers can grep for it. ` +
|
|
111
|
+
`A computed string defeats the audit-trail purpose.`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
report(v, `subApp()'s \`reason\` must be a string literal so reviewers can grep for it. ` +
|
|
116
|
+
`A computed string defeats the audit-trail purpose.`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
ts.forEachChild(n, walk);
|
|
123
|
+
};
|
|
124
|
+
walk(sf);
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=subapp-requires-reason.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subapp-requires-reason.js","sourceRoot":"","sources":["../../src/modules/subapp-requires-reason.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,mEAAmE;AACnE,sEAAsE;AACtE,0DAA0D;AAC1D,6CAA6C;AAC7C,6DAA6D;AAE7D,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAGnD,MAAM,kBAAkB,GAAG;IACzB,6BAA6B;IAC7B,mDAAmD;IACnD,yBAAyB;IACzB,mBAAmB;CACpB,CAAA;AAED,gFAAgF;AAChF,SAAS,uBAAuB,CAAC,EAAiB,EAAE,IAAY;IAC9D,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;YAAE,SAAQ;QAC3C,IAAI,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;YAAE,SAAQ;QAChE,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,EAAE,CAAC;YAClD,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI;gBAAE,SAAQ;YAC9D,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,CAAA;YAC1B,IAAI,CAAC,IAAI;gBAAE,OAAO,IAAI,CAAA;YACtB,IAAI,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAA;YAC9C,IAAI,EAAE,CAAC,+BAA+B,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,IAAI,CAAA;QAChE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,OAAO;QACL,IAAI,EAAE,wBAAwB;QAC9B,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE;YACX;gBACE,EAAE,EAAE,6BAA6B;gBACjC,WAAW,EACT,mGAAmG;aACtG;SACF;QACD,QAAQ,EAAE;YACR,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBACxC,MAAM,OAAO,GAAG,IAAqB,CAAA;gBACrC,MAAM,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;gBAE5F,MAAM,MAAM,GAAG,CAAC,CAAU,EAAE,OAAe,EAAQ,EAAE;oBACnD,GAAG,CAAC,gBAAgB,CAAC;wBACnB,EAAE,EAAE,6BAA6B;wBACjC,QAAQ,EAAE,OAAO;wBACjB,QAAQ,EAAE,aAAa;wBACvB,OAAO;wBACP,QAAQ,EAAE;4BACR,IAAI,EAAE,EAAE,CAAC,QAAQ;4BACjB,KAAK,EAAE,gBAAgB,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;yBAC7D;qBACF,CAAC,CAAA;gBACJ,CAAC,CAAA;gBAED,MAAM,SAAS,GAAG,CAAC,IAAY,EAAE,QAAiB,EAAQ,EAAE;oBAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;oBAC3B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;wBACnB,MAAM,CACJ,QAAQ,EACR,sFAAsF;4BACpF,wDAAwD,CAC3D,CAAA;wBACD,OAAM;oBACR,CAAC;oBACD,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;wBACtD,MAAM,CACJ,QAAQ,EACR,+DAA+D,OAAO,WAAW;4BAC/E,8EAA8E;4BAC9E,yFAAyF,CAC5F,CAAA;oBACH,CAAC;gBACH,CAAC,CAAA;gBAED,MAAM,IAAI,GAAG,CAAC,CAAU,EAAQ,EAAE;oBAChC,IAAI,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC;wBAC3B,IAAI,UAAU,GAAkB,IAAI,CAAA;wBACpC,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC;4BAAE,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,IAAI,CAAA;6BAC5D,IACH,EAAE,CAAC,0BAA0B,CAAC,CAAC,CAAC,UAAU,CAAC;4BAC3C,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;4BAElC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAA;wBACrC,IAAI,UAAU,KAAK,QAAQ,EAAE,CAAC;4BAC5B,MAAM,GAAG,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;4BAC1B,IAAI,GAAG,IAAI,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC,EAAE,CAAC;gCAC7C,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,CAAC,IAAI,CACpC,CAAC,CAAC,EAA8B,EAAE,CAChC,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC;oCAC1B,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;wCACpD,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAC9D,CAAA;gCACD,IAAI,CAAC,UAAU,EAAE,CAAC;oCAChB,MAAM,CACJ,GAAG,EACH,6EAA6E;wCAC3E,sEAAsE;wCACtE,6CAA6C,CAChD,CAAA;gCACH,CAAC;qCAAM,CAAC;oCACN,MAAM,CAAC,GAAG,UAAU,CAAC,WAAW,CAAA;oCAChC,IAAI,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,+BAA+B,CAAC,CAAC,CAAC,EAAE,CAAC;wCACnE,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;oCACtB,CAAC;yCAAM,IAAI,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,CAAC;wCACtC,uCAAuC;wCACvC,MAAM,CACJ,CAAC,EACD,+EAA+E;4CAC7E,oDAAoD,CACvD,CAAA;oCACH,CAAC;yCAAM,IAAI,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;wCAC9B,MAAM,QAAQ,GAAG,uBAAuB,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;wCACpD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;4CACtB,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAA;wCACxB,CAAC;6CAAM,CAAC;4CACN,MAAM,CACJ,CAAC,EACD,+EAA+E;gDAC7E,oDAAoD,CACvD,CAAA;wCACH,CAAC;oCACH,CAAC;yCAAM,CAAC;wCACN,MAAM,CACJ,CAAC,EACD,+EAA+E;4CAC7E,oDAAoD,CACvD,CAAA;oCACH,CAAC;gCACH,CAAC;4BACH,CAAC;wBACH,CAAC;oBACH,CAAC;oBACD,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAA;gBAC1B,CAAC,CAAA;gBACD,IAAI,CAAC,EAAE,CAAC,CAAA;YACV,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["// `subapp-requires-reason` — errors when a `subApp({...})` call lacks a\n// non-empty string-literal `reason` property. `reason` is a sticky\n// comment documenting WHY a state-isolation boundary is needed rather\n// than a view function — meant to be auditable the way an\n// `eslint-disable` comment is. Migrated from\n// `@llui/eslint-plugin/src/rules/subapp-requires-reason.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\nconst ORG_EXCUSE_REGEXES = [\n /\\bcode\\s+organi[zs]ation\\b/i,\n /\\b(break|breaking|split|splitting)\\s+(this|up)\\b/i,\n /\\b(felt|just)\\s+like\\b/i,\n /\\bsubcomponent\\b/i,\n]\n\n/** Resolve a local `const NAME = \"...\"` to its string literal value, if any. */\nfunction resolveLocalStringConst(sf: ts.SourceFile, name: string): string | null {\n for (const stmt of sf.statements) {\n if (!ts.isVariableStatement(stmt)) continue\n if (!(stmt.declarationList.flags & ts.NodeFlags.Const)) continue\n for (const d of stmt.declarationList.declarations) {\n if (!ts.isIdentifier(d.name) || d.name.text !== name) continue\n const init = d.initializer\n if (!init) return null\n if (ts.isStringLiteral(init)) return init.text\n if (ts.isNoSubstitutionTemplateLiteral(init)) return init.text\n }\n }\n return null\n}\n\nexport function subappRequiresReasonModule(): CompilerModule {\n return {\n name: 'subapp-requires-reason',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/subapp-requires-reason',\n description:\n 'subApp() requires a non-empty string-literal `reason` documenting why a state boundary is needed.',\n },\n ],\n visitors: {\n [ts.SyntaxKind.SourceFile]: (ctx, node) => {\n const visited = node as ts.SourceFile\n const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true)\n\n const report = (n: ts.Node, message: string): void => {\n ctx.reportDiagnostic({\n id: 'llui/subapp-requires-reason',\n severity: 'error',\n category: 'composition',\n message,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),\n },\n })\n }\n\n const checkText = (text: string, reportAt: ts.Node): void => {\n const trimmed = text.trim()\n if (trimmed === '') {\n report(\n reportAt,\n `subApp()'s \\`reason\\` must be a non-empty string. Decomposing for code organization ` +\n `is not a valid reason — write a view function instead.`,\n )\n return\n }\n if (ORG_EXCUSE_REGEXES.some((re) => re.test(trimmed))) {\n report(\n reportAt,\n `subApp() \\`reason\\` looks like a code-organization excuse ('${trimmed}'). Real ` +\n `reasons name foreign lifecycle, isolated frame budget, or sealed state. For ` +\n `decomposition, write a view function — see docs/proposals/unified-composition-model.md.`,\n )\n }\n }\n\n const walk = (n: ts.Node): void => {\n if (ts.isCallExpression(n)) {\n let calleeName: string | null = null\n if (ts.isIdentifier(n.expression)) calleeName = n.expression.text\n else if (\n ts.isPropertyAccessExpression(n.expression) &&\n ts.isIdentifier(n.expression.name)\n )\n calleeName = n.expression.name.text\n if (calleeName === 'subApp') {\n const arg = n.arguments[0]\n if (arg && ts.isObjectLiteralExpression(arg)) {\n const reasonProp = arg.properties.find(\n (p): p is ts.PropertyAssignment =>\n ts.isPropertyAssignment(p) &&\n ((ts.isIdentifier(p.name) && p.name.text === 'reason') ||\n (ts.isStringLiteral(p.name) && p.name.text === 'reason')),\n )\n if (!reasonProp) {\n report(\n arg,\n `subApp() requires a \\`reason\\` property. Add a string literal naming WHY a ` +\n `state-isolation boundary is needed here rather than a view function ` +\n `(e.g. \"Monaco owns its own DOM lifecycle\").`,\n )\n } else {\n const v = reasonProp.initializer\n if (ts.isStringLiteral(v) || ts.isNoSubstitutionTemplateLiteral(v)) {\n checkText(v.text, v)\n } else if (ts.isTemplateExpression(v)) {\n // Template with expressions → computed\n report(\n v,\n `subApp()'s \\`reason\\` must be a string literal so reviewers can grep for it. ` +\n `A computed string defeats the audit-trail purpose.`,\n )\n } else if (ts.isIdentifier(v)) {\n const resolved = resolveLocalStringConst(sf, v.text)\n if (resolved !== null) {\n checkText(resolved, v)\n } else {\n report(\n v,\n `subApp()'s \\`reason\\` must be a string literal so reviewers can grep for it. ` +\n `A computed string defeats the audit-trail purpose.`,\n )\n }\n } else {\n report(\n v,\n `subApp()'s \\`reason\\` must be a string literal so reviewers can grep for it. ` +\n `A computed string defeats the audit-trail purpose.`,\n )\n }\n }\n }\n }\n }\n ts.forEachChild(n, walk)\n }\n walk(sf)\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import type { CompilerModule } from '../module.js';
|
|
3
|
+
export interface TextMaskModuleOptions {
|
|
4
|
+
fieldBits: Map<string, number>;
|
|
5
|
+
viewHelperNames: Set<string>;
|
|
6
|
+
viewHelperAliases: Map<string, string>;
|
|
7
|
+
/** The `import { ... } from '@llui/dom'` declaration — used for
|
|
8
|
+
* bare-identifier provenance check. */
|
|
9
|
+
lluiImport: ts.ImportDeclaration;
|
|
10
|
+
}
|
|
11
|
+
export declare function textMaskModule(options: TextMaskModuleOptions): CompilerModule;
|
|
12
|
+
//# sourceMappingURL=text-mask.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-mask.d.ts","sourceRoot":"","sources":["../../src/modules/text-mask.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,MAAM,YAAY,CAAA;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAIlD,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,eAAe,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAC5B,iBAAiB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC;4CACwC;IACxC,UAAU,EAAE,EAAE,CAAC,iBAAiB,CAAA;CACjC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,cAAc,CAsC7E"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// `text-mask` — injects the precise reactive bitmask as the second
|
|
2
|
+
// argument of every `text(accessor)` call. The runtime gates the
|
|
3
|
+
// binding's re-evaluation on `(mask & dirty)` so a `text()` that
|
|
4
|
+
// reads only `s.count` is skipped when only `s.theme` changes.
|
|
5
|
+
//
|
|
6
|
+
// Accessor resolution mirrors `structural-mask`'s contract: inline
|
|
7
|
+
// arrow, inline `memo(arrow)`, or identifier referencing a const-bound
|
|
8
|
+
// arrow / memo / function declaration. Anything else (static
|
|
9
|
+
// strings, opaque imports, parameters) leaves the call unchanged
|
|
10
|
+
// and the runtime falls back to FULL_MASK — correct but slower.
|
|
11
|
+
//
|
|
12
|
+
// Fires top-down (`transformCallEnter`) so the element rewrite
|
|
13
|
+
// chain sees the masked form. Idempotent: skips when the call
|
|
14
|
+
// already has a second argument (caller-supplied mask).
|
|
15
|
+
//
|
|
16
|
+
// Bare-identifier `text` is gated on provenance — the module
|
|
17
|
+
// verifies the identifier resolves to the @llui/dom import via
|
|
18
|
+
// `lluiImport`. Destructured aliases and member-expression forms
|
|
19
|
+
// (`{ text: t } => t(...)`, `h.text(...)`) are provenance-safe
|
|
20
|
+
// by construction.
|
|
21
|
+
import ts from 'typescript';
|
|
22
|
+
import { resolveAccessorBody } from '../accessor-resolver.js';
|
|
23
|
+
import { computeAccessorMask, createMaskLiteral, isHelperCall } from '../transform.js';
|
|
24
|
+
export function textMaskModule(options) {
|
|
25
|
+
const { fieldBits, viewHelperNames, viewHelperAliases, lluiImport } = options;
|
|
26
|
+
return {
|
|
27
|
+
name: 'text-mask',
|
|
28
|
+
compilerVersion: '^0.3.0',
|
|
29
|
+
diagnostics: [],
|
|
30
|
+
visitors: {},
|
|
31
|
+
transformCallEnter(ctx, node) {
|
|
32
|
+
if (!isHelperCall(node.expression, 'text', viewHelperNames, viewHelperAliases))
|
|
33
|
+
return null;
|
|
34
|
+
// Bare-identifier provenance: skip if `text` doesn't resolve to
|
|
35
|
+
// the @llui/dom import. Aliased/member-expression forms are
|
|
36
|
+
// provenance-safe.
|
|
37
|
+
if (ts.isIdentifier(node.expression) && !viewHelperAliases.has(node.expression.text)) {
|
|
38
|
+
const clause = lluiImport.importClause;
|
|
39
|
+
if (!clause?.namedBindings || !ts.isNamedImports(clause.namedBindings))
|
|
40
|
+
return null;
|
|
41
|
+
const hasText = clause.namedBindings.elements.some((s) => s.name.text === 'text' || s.propertyName?.text === 'text');
|
|
42
|
+
if (!hasText)
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
const firstArg = node.arguments[0];
|
|
46
|
+
if (!firstArg)
|
|
47
|
+
return null;
|
|
48
|
+
// Idempotent — skip if caller supplied a mask.
|
|
49
|
+
if (node.arguments.length >= 2)
|
|
50
|
+
return null;
|
|
51
|
+
const accessor = resolveAccessorBody(firstArg);
|
|
52
|
+
if (!accessor)
|
|
53
|
+
return null;
|
|
54
|
+
const { mask } = computeAccessorMask(accessor, fieldBits);
|
|
55
|
+
const f = ctx.factory;
|
|
56
|
+
return f.createCallExpression(node.expression, node.typeArguments, [
|
|
57
|
+
firstArg,
|
|
58
|
+
createMaskLiteral(f, mask === 0 ? 0xffffffff | 0 : mask),
|
|
59
|
+
]);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=text-mask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-mask.js","sourceRoot":"","sources":["../../src/modules/text-mask.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,iEAAiE;AACjE,iEAAiE;AACjE,+DAA+D;AAC/D,EAAE;AACF,mEAAmE;AACnE,uEAAuE;AACvE,6DAA6D;AAC7D,iEAAiE;AACjE,gEAAgE;AAChE,EAAE;AACF,+DAA+D;AAC/D,8DAA8D;AAC9D,wDAAwD;AACxD,EAAE;AACF,6DAA6D;AAC7D,+DAA+D;AAC/D,iEAAiE;AACjE,+DAA+D;AAC/D,mBAAmB;AAEnB,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAA;AAC7D,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAWtF,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,UAAU,EAAE,GAAG,OAAO,CAAA;IAC7E,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,EAAE;QAEZ,kBAAkB,CAAC,GAAG,EAAE,IAAI;YAC1B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,EAAE,iBAAiB,CAAC;gBAAE,OAAO,IAAI,CAAA;YAE3F,gEAAgE;YAChE,4DAA4D;YAC5D,mBAAmB;YACnB,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrF,MAAM,MAAM,GAAG,UAAU,CAAC,YAAY,CAAA;gBACtC,IAAI,CAAC,MAAM,EAAE,aAAa,IAAI,CAAC,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC,aAAa,CAAC;oBAAE,OAAO,IAAI,CAAA;gBACnF,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,CAChD,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,YAAY,EAAE,IAAI,KAAK,MAAM,CACjE,CAAA;gBACD,IAAI,CAAC,OAAO;oBAAE,OAAO,IAAI,CAAA;YAC3B,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;YAClC,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAA;YAC1B,+CAA+C;YAC/C,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAA;YAC3C,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAA;YAC9C,IAAI,CAAC,QAAQ;gBAAE,OAAO,IAAI,CAAA;YAE1B,MAAM,EAAE,IAAI,EAAE,GAAG,mBAAmB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;YACzD,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAA;YACrB,OAAO,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE;gBACjE,QAAQ;gBACR,iBAAiB,CAAC,CAAC,EAAE,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;aACzD,CAAC,CAAA;QACJ,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// `text-mask` — injects the precise reactive bitmask as the second\n// argument of every `text(accessor)` call. The runtime gates the\n// binding's re-evaluation on `(mask & dirty)` so a `text()` that\n// reads only `s.count` is skipped when only `s.theme` changes.\n//\n// Accessor resolution mirrors `structural-mask`'s contract: inline\n// arrow, inline `memo(arrow)`, or identifier referencing a const-bound\n// arrow / memo / function declaration. Anything else (static\n// strings, opaque imports, parameters) leaves the call unchanged\n// and the runtime falls back to FULL_MASK — correct but slower.\n//\n// Fires top-down (`transformCallEnter`) so the element rewrite\n// chain sees the masked form. Idempotent: skips when the call\n// already has a second argument (caller-supplied mask).\n//\n// Bare-identifier `text` is gated on provenance — the module\n// verifies the identifier resolves to the @llui/dom import via\n// `lluiImport`. Destructured aliases and member-expression forms\n// (`{ text: t } => t(...)`, `h.text(...)`) are provenance-safe\n// by construction.\n\nimport ts from 'typescript'\nimport type { CompilerModule } from '../module.js'\nimport { resolveAccessorBody } from '../accessor-resolver.js'\nimport { computeAccessorMask, createMaskLiteral, isHelperCall } from '../transform.js'\n\nexport interface TextMaskModuleOptions {\n fieldBits: Map<string, number>\n viewHelperNames: Set<string>\n viewHelperAliases: Map<string, string>\n /** The `import { ... } from '@llui/dom'` declaration — used for\n * bare-identifier provenance check. */\n lluiImport: ts.ImportDeclaration\n}\n\nexport function textMaskModule(options: TextMaskModuleOptions): CompilerModule {\n const { fieldBits, viewHelperNames, viewHelperAliases, lluiImport } = options\n return {\n name: 'text-mask',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n visitors: {},\n\n transformCallEnter(ctx, node) {\n if (!isHelperCall(node.expression, 'text', viewHelperNames, viewHelperAliases)) return null\n\n // Bare-identifier provenance: skip if `text` doesn't resolve to\n // the @llui/dom import. Aliased/member-expression forms are\n // provenance-safe.\n if (ts.isIdentifier(node.expression) && !viewHelperAliases.has(node.expression.text)) {\n const clause = lluiImport.importClause\n if (!clause?.namedBindings || !ts.isNamedImports(clause.namedBindings)) return null\n const hasText = clause.namedBindings.elements.some(\n (s) => s.name.text === 'text' || s.propertyName?.text === 'text',\n )\n if (!hasText) return null\n }\n\n const firstArg = node.arguments[0]\n if (!firstArg) return null\n // Idempotent — skip if caller supplied a mask.\n if (node.arguments.length >= 2) return null\n const accessor = resolveAccessorBody(firstArg)\n if (!accessor) return null\n\n const { mask } = computeAccessorMask(accessor, fieldBits)\n const f = ctx.factory\n return f.createCallExpression(node.expression, node.typeArguments, [\n firstArg,\n createMaskLiteral(f, mask === 0 ? 0xffffffff | 0 : mask),\n ])\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"view-bag-import.d.ts","sourceRoot":"","sources":["../../src/modules/view-bag-import.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAKlD,wBAAgB,mBAAmB,IAAI,cAAc,CAgEpD"}
|