@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 @@
|
|
|
1
|
+
{"version":3,"file":"map-on-state-array.js","sourceRoot":"","sources":["../../src/modules/map-on-state-array.ts"],"names":[],"mappings":"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","sourcesContent":["// `map-on-state-array` — errors when a state-derived array is iterated\n// with `.map()` inside a view function. The reactive primitive is\n// `each(...)`; `.map()` produces a static list with no per-row scope,\n// no key-based reconciliation, and no precise mask gating. Migrated\n// from `@llui/eslint-plugin/src/rules/map-on-state-array.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\nimport { findComponentCalls } from './_shared.js'\n\nfunction findViewProperty(call: ts.CallExpression): ts.PropertyAssignment | undefined {\n const arg = call.arguments[0]\n if (!arg || !ts.isObjectLiteralExpression(arg)) return undefined\n for (const prop of arg.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'view') {\n return prop\n }\n }\n return undefined\n}\n\n/**\n * True when `expr` resolves to a state-like reference. We recognize\n * the conventional names — `state`, `s`, `_state` — and chained\n * property accesses rooted at one of them (e.g. `s.items.filtered`).\n * Type-aware resolution would be more precise; the conventions are\n * stable enough across the codebase that name matching catches the\n * intended cases without a checker.\n */\nfunction isStateReference(expr: ts.Expression): boolean {\n if (ts.isIdentifier(expr)) {\n return expr.text === 'state' || expr.text === 's' || expr.text === '_state'\n }\n if (ts.isPropertyAccessExpression(expr)) {\n return isStateReference(expr.expression)\n }\n return false\n}\n\nexport function mapOnStateArrayModule(): CompilerModule {\n return {\n name: 'map-on-state-array',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/map-on-state-array',\n description:\n 'Array .map() on a state-derived value in view(). Use each() for reactive lists.',\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 for (const call of findComponentCalls(sf)) {\n const viewProp = findViewProperty(call)\n if (!viewProp) continue\n const fn = viewProp.initializer\n if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue\n\n const walk = (n: ts.Node): void => {\n if (\n ts.isCallExpression(n) &&\n ts.isPropertyAccessExpression(n.expression) &&\n ts.isIdentifier(n.expression.name) &&\n n.expression.name.text === 'map' &&\n isStateReference(n.expression.expression)\n ) {\n ctx.reportDiagnostic({\n id: 'llui/map-on-state-array',\n severity: 'error',\n category: 'reactivity',\n message:\n 'Array `.map()` on a state-derived value inside view(). `.map()` produces a static list — use `each({ items, key, render })` for reactive lists with per-row scope and key-based reconciliation.',\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 if (fn.body) walk(fn.body)\n }\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CompilerModule } from '../module.js';
|
|
2
|
+
export interface MaskLegendModuleOptions {
|
|
3
|
+
/** Path → low-word bit. */
|
|
4
|
+
fieldBits: Map<string, number>;
|
|
5
|
+
/** Path → high-word bit. Currently unused by legend emission but
|
|
6
|
+
* accepted so the activation gate matches the monolith. */
|
|
7
|
+
fieldBitsHi: Map<string, number>;
|
|
8
|
+
}
|
|
9
|
+
export declare function maskLegendModule(options: MaskLegendModuleOptions): CompilerModule;
|
|
10
|
+
//# sourceMappingURL=mask-legend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-legend.d.ts","sourceRoot":"","sources":["../../src/modules/mask-legend.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,cAAc,CAAA;AAExE,MAAM,WAAW,uBAAuB;IACtC,2BAA2B;IAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B;gEAC4D;IAC5D,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACjC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,cAAc,CA8BjF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// `mask-legend` — emits `__maskLegend`, the per-component map from
|
|
2
|
+
// top-level state field name → aggregated bitmask. The agent layer
|
|
3
|
+
// uses this to decode a runtime `dirty` number back into the human
|
|
4
|
+
// names of fields that changed.
|
|
5
|
+
//
|
|
6
|
+
// Inputs are the file-level `fieldBits` / `fieldBitsHi` maps the
|
|
7
|
+
// umbrella has already computed via `collectDeps`. Sub-paths
|
|
8
|
+
// (`route.page`, `route.data`) collapse into one entry per top-level
|
|
9
|
+
// field so the legend reads per-field rather than per-path.
|
|
10
|
+
//
|
|
11
|
+
// The monolith historically iterated only the low-word map; the high
|
|
12
|
+
// word participates in mask computation but not in legend entries.
|
|
13
|
+
// The module preserves that behaviour exactly — see the matching
|
|
14
|
+
// comment in `transform.ts` near the inline `legendProps` builder.
|
|
15
|
+
import ts from 'typescript';
|
|
16
|
+
export function maskLegendModule(options) {
|
|
17
|
+
const { fieldBits, fieldBitsHi } = options;
|
|
18
|
+
return {
|
|
19
|
+
name: 'mask-legend',
|
|
20
|
+
compilerVersion: '^0.3.0',
|
|
21
|
+
diagnostics: [],
|
|
22
|
+
visitors: {},
|
|
23
|
+
emit(ctx) {
|
|
24
|
+
if (fieldBits.size === 0 && fieldBitsHi.size === 0)
|
|
25
|
+
return [];
|
|
26
|
+
const topLevelBits = new Map();
|
|
27
|
+
for (const [path, bit] of fieldBits) {
|
|
28
|
+
const topField = path.split('.')[0];
|
|
29
|
+
topLevelBits.set(topField, (topLevelBits.get(topField) ?? 0) | bit);
|
|
30
|
+
}
|
|
31
|
+
const f = ctx.factory;
|
|
32
|
+
const legendProps = [];
|
|
33
|
+
for (const [field, bit] of topLevelBits) {
|
|
34
|
+
legendProps.push(f.createPropertyAssignment(f.createStringLiteral(field), createMaskLiteral(f, bit)));
|
|
35
|
+
}
|
|
36
|
+
const contribution = {
|
|
37
|
+
module: 'mask-legend',
|
|
38
|
+
field: '__maskLegend',
|
|
39
|
+
value: f.createObjectLiteralExpression(legendProps, false),
|
|
40
|
+
};
|
|
41
|
+
return [contribution];
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function createMaskLiteral(f, mask) {
|
|
46
|
+
if (mask >= 0)
|
|
47
|
+
return f.createNumericLiteral(mask);
|
|
48
|
+
return f.createBinaryExpression(f.createNumericLiteral(0xffffffff), ts.SyntaxKind.BarToken, f.createNumericLiteral(0));
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=mask-legend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mask-legend.js","sourceRoot":"","sources":["../../src/modules/mask-legend.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,mEAAmE;AACnE,mEAAmE;AACnE,gCAAgC;AAChC,EAAE;AACF,iEAAiE;AACjE,6DAA6D;AAC7D,qEAAqE;AACrE,4DAA4D;AAC5D,EAAE;AACF,qEAAqE;AACrE,mEAAmE;AACnE,iEAAiE;AACjE,mEAAmE;AAEnE,OAAO,EAAE,MAAM,YAAY,CAAA;AAW3B,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,OAAO,CAAA;IAC1C,OAAO;QACL,IAAI,EAAE,aAAa;QACnB,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,EAAE;QAEZ,IAAI,CAAC,GAAG;YACN,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YAC7D,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAA;YAC9C,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;gBACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,CAAA;gBACpC,YAAY,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAA;YACrE,CAAC;YACD,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAA;YACrB,MAAM,WAAW,GAA4B,EAAE,CAAA;YAC/C,KAAK,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC;gBACxC,WAAW,CAAC,IAAI,CACd,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,iBAAiB,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CACpF,CAAA;YACH,CAAC;YACD,MAAM,YAAY,GAAyB;gBACzC,MAAM,EAAE,aAAa;gBACrB,KAAK,EAAE,cAAc;gBACrB,KAAK,EAAE,CAAC,CAAC,6BAA6B,CAAC,WAAW,EAAE,KAAK,CAAC;aAC3D,CAAA;YACD,OAAO,CAAC,YAAY,CAAC,CAAA;QACvB,CAAC;KACF,CAAA;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,CAAiB,EAAE,IAAY;IACxD,IAAI,IAAI,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAA;IAClD,OAAO,CAAC,CAAC,sBAAsB,CAC7B,CAAC,CAAC,oBAAoB,CAAC,UAAU,CAAC,EAClC,EAAE,CAAC,UAAU,CAAC,QAAQ,EACtB,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAC1B,CAAA;AACH,CAAC","sourcesContent":["// `mask-legend` — emits `__maskLegend`, the per-component map from\n// top-level state field name → aggregated bitmask. The agent layer\n// uses this to decode a runtime `dirty` number back into the human\n// names of fields that changed.\n//\n// Inputs are the file-level `fieldBits` / `fieldBitsHi` maps the\n// umbrella has already computed via `collectDeps`. Sub-paths\n// (`route.page`, `route.data`) collapse into one entry per top-level\n// field so the legend reads per-field rather than per-path.\n//\n// The monolith historically iterated only the low-word map; the high\n// word participates in mask computation but not in legend entries.\n// The module preserves that behaviour exactly — see the matching\n// comment in `transform.ts` near the inline `legendProps` builder.\n\nimport ts from 'typescript'\nimport type { CompilerModule, EmissionContribution } from '../module.js'\n\nexport interface MaskLegendModuleOptions {\n /** Path → low-word bit. */\n fieldBits: Map<string, number>\n /** Path → high-word bit. Currently unused by legend emission but\n * accepted so the activation gate matches the monolith. */\n fieldBitsHi: Map<string, number>\n}\n\nexport function maskLegendModule(options: MaskLegendModuleOptions): CompilerModule {\n const { fieldBits, fieldBitsHi } = options\n return {\n name: 'mask-legend',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n visitors: {},\n\n emit(ctx) {\n if (fieldBits.size === 0 && fieldBitsHi.size === 0) return []\n const topLevelBits = new Map<string, number>()\n for (const [path, bit] of fieldBits) {\n const topField = path.split('.')[0]!\n topLevelBits.set(topField, (topLevelBits.get(topField) ?? 0) | bit)\n }\n const f = ctx.factory\n const legendProps: ts.PropertyAssignment[] = []\n for (const [field, bit] of topLevelBits) {\n legendProps.push(\n f.createPropertyAssignment(f.createStringLiteral(field), createMaskLiteral(f, bit)),\n )\n }\n const contribution: EmissionContribution = {\n module: 'mask-legend',\n field: '__maskLegend',\n value: f.createObjectLiteralExpression(legendProps, false),\n }\n return [contribution]\n },\n }\n}\n\nfunction createMaskLiteral(f: ts.NodeFactory, mask: number): ts.Expression {\n if (mask >= 0) return f.createNumericLiteral(mask)\n return f.createBinaryExpression(\n f.createNumericLiteral(0xffffffff),\n ts.SyntaxKind.BarToken,\n f.createNumericLiteral(0),\n )\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-memo.d.ts","sourceRoot":"","sources":["../../src/modules/missing-memo.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAiDlD,wBAAgB,iBAAiB,IAAI,cAAc,CA2DlD"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// `missing-memo` — errors when a duplicate accessor arrow appears at
|
|
2
|
+
// 2+ reactive binding sites inside a component's view, without any of
|
|
3
|
+
// them wrapped in `memo()`. The duplicates re-compute on every commit;
|
|
4
|
+
// wrapping in `memo()` shares the computation. Migrated from
|
|
5
|
+
// `@llui/eslint-plugin/src/rules/missing-memo.ts`.
|
|
6
|
+
import ts from 'typescript';
|
|
7
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
8
|
+
import { findComponentCalls } from './_shared.js';
|
|
9
|
+
import { ELEMENT_HELPERS } from './_element-helpers.js';
|
|
10
|
+
function findViewProperty(call) {
|
|
11
|
+
const arg = call.arguments[0];
|
|
12
|
+
if (!arg || !ts.isObjectLiteralExpression(arg))
|
|
13
|
+
return undefined;
|
|
14
|
+
for (const prop of arg.properties) {
|
|
15
|
+
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'view') {
|
|
16
|
+
return prop;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
function isReactiveBinding(arrow) {
|
|
22
|
+
const parent = arrow.parent;
|
|
23
|
+
if (!parent)
|
|
24
|
+
return false;
|
|
25
|
+
// Case 1: first arg to text()
|
|
26
|
+
if (ts.isCallExpression(parent) &&
|
|
27
|
+
ts.isIdentifier(parent.expression) &&
|
|
28
|
+
parent.expression.text === 'text' &&
|
|
29
|
+
parent.arguments[0] === arrow) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
// Case 2: property value of an object literal passed to an element helper
|
|
33
|
+
if (ts.isPropertyAssignment(parent)) {
|
|
34
|
+
const objLit = parent.parent;
|
|
35
|
+
if (!objLit || !ts.isObjectLiteralExpression(objLit))
|
|
36
|
+
return false;
|
|
37
|
+
const call = objLit.parent;
|
|
38
|
+
if (!call || !ts.isCallExpression(call))
|
|
39
|
+
return false;
|
|
40
|
+
if (!ts.isIdentifier(call.expression))
|
|
41
|
+
return false;
|
|
42
|
+
return ELEMENT_HELPERS.has(call.expression.text);
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
function isInMemoCall(arrow) {
|
|
47
|
+
const parent = arrow.parent;
|
|
48
|
+
return (!!parent &&
|
|
49
|
+
ts.isCallExpression(parent) &&
|
|
50
|
+
ts.isIdentifier(parent.expression) &&
|
|
51
|
+
parent.expression.text === 'memo');
|
|
52
|
+
}
|
|
53
|
+
export function missingMemoModule() {
|
|
54
|
+
return {
|
|
55
|
+
name: 'missing-memo',
|
|
56
|
+
compilerVersion: '^0.3.0',
|
|
57
|
+
diagnostics: [
|
|
58
|
+
{
|
|
59
|
+
id: 'llui/missing-memo',
|
|
60
|
+
description: 'Duplicate accessor arrow used at multiple reactive binding sites without memo().',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
visitors: {
|
|
64
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
65
|
+
const visited = node;
|
|
66
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
67
|
+
for (const call of findComponentCalls(sf)) {
|
|
68
|
+
const viewProp = findViewProperty(call);
|
|
69
|
+
if (!viewProp)
|
|
70
|
+
continue;
|
|
71
|
+
const fn = viewProp.initializer;
|
|
72
|
+
if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
|
|
73
|
+
continue;
|
|
74
|
+
if (!fn.body)
|
|
75
|
+
continue;
|
|
76
|
+
const arrowsByText = new Map();
|
|
77
|
+
const walk = (n) => {
|
|
78
|
+
if (ts.isArrowFunction(n) && n.parameters.length > 0 && isReactiveBinding(n)) {
|
|
79
|
+
const text = n.getText(sf).replace(/\s+/g, ' ').trim();
|
|
80
|
+
const entries = arrowsByText.get(text) ?? [];
|
|
81
|
+
entries.push({ node: n, inMemo: isInMemoCall(n) });
|
|
82
|
+
arrowsByText.set(text, entries);
|
|
83
|
+
}
|
|
84
|
+
ts.forEachChild(n, walk);
|
|
85
|
+
};
|
|
86
|
+
walk(fn.body);
|
|
87
|
+
for (const [, entries] of arrowsByText) {
|
|
88
|
+
if (entries.length < 2)
|
|
89
|
+
continue;
|
|
90
|
+
const unmemoized = entries.filter((e) => !e.inMemo);
|
|
91
|
+
if (unmemoized.length < 2)
|
|
92
|
+
continue;
|
|
93
|
+
for (let i = 1; i < unmemoized.length; i++) {
|
|
94
|
+
const entry = unmemoized[i];
|
|
95
|
+
ctx.reportDiagnostic({
|
|
96
|
+
id: 'llui/missing-memo',
|
|
97
|
+
severity: 'error',
|
|
98
|
+
category: 'perf',
|
|
99
|
+
message: `Duplicate accessor arrow used at multiple reactive binding sites without ` +
|
|
100
|
+
`\`memo()\`. Wrap in \`memo()\` to share the computation: ` +
|
|
101
|
+
`\`const myAccessor = memo((s) => …)\`, then reference it at each site.`,
|
|
102
|
+
location: {
|
|
103
|
+
file: sf.fileName,
|
|
104
|
+
range: rangeFromOffsets(sf.text, entry.node.getStart(sf), entry.node.getEnd()),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=missing-memo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-memo.js","sourceRoot":"","sources":["../../src/modules/missing-memo.ts"],"names":[],"mappings":"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","sourcesContent":["// `missing-memo` — errors when a duplicate accessor arrow appears at\n// 2+ reactive binding sites inside a component's view, without any of\n// them wrapped in `memo()`. The duplicates re-compute on every commit;\n// wrapping in `memo()` shares the computation. Migrated from\n// `@llui/eslint-plugin/src/rules/missing-memo.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\nimport { findComponentCalls } from './_shared.js'\nimport { ELEMENT_HELPERS } from './_element-helpers.js'\n\nfunction findViewProperty(call: ts.CallExpression): ts.PropertyAssignment | undefined {\n const arg = call.arguments[0]\n if (!arg || !ts.isObjectLiteralExpression(arg)) return undefined\n for (const prop of arg.properties) {\n if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name) && prop.name.text === 'view') {\n return prop\n }\n }\n return undefined\n}\n\nfunction isReactiveBinding(arrow: ts.ArrowFunction): boolean {\n const parent = arrow.parent\n if (!parent) return false\n // Case 1: first arg to text()\n if (\n ts.isCallExpression(parent) &&\n ts.isIdentifier(parent.expression) &&\n parent.expression.text === 'text' &&\n parent.arguments[0] === arrow\n ) {\n return true\n }\n // Case 2: property value of an object literal passed to an element helper\n if (ts.isPropertyAssignment(parent)) {\n const objLit = parent.parent\n if (!objLit || !ts.isObjectLiteralExpression(objLit)) return false\n const call = objLit.parent\n if (!call || !ts.isCallExpression(call)) return false\n if (!ts.isIdentifier(call.expression)) return false\n return ELEMENT_HELPERS.has(call.expression.text)\n }\n return false\n}\n\nfunction isInMemoCall(arrow: ts.ArrowFunction): boolean {\n const parent = arrow.parent\n return (\n !!parent &&\n ts.isCallExpression(parent) &&\n ts.isIdentifier(parent.expression) &&\n parent.expression.text === 'memo'\n )\n}\n\nexport function missingMemoModule(): CompilerModule {\n return {\n name: 'missing-memo',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/missing-memo',\n description:\n 'Duplicate accessor arrow used at multiple reactive binding sites without memo().',\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 for (const call of findComponentCalls(sf)) {\n const viewProp = findViewProperty(call)\n if (!viewProp) continue\n const fn = viewProp.initializer\n if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue\n if (!fn.body) continue\n\n const arrowsByText = new Map<string, { node: ts.ArrowFunction; inMemo: boolean }[]>()\n const walk = (n: ts.Node): void => {\n if (ts.isArrowFunction(n) && n.parameters.length > 0 && isReactiveBinding(n)) {\n const text = n.getText(sf).replace(/\\s+/g, ' ').trim()\n const entries = arrowsByText.get(text) ?? []\n entries.push({ node: n, inMemo: isInMemoCall(n) })\n arrowsByText.set(text, entries)\n }\n ts.forEachChild(n, walk)\n }\n walk(fn.body)\n\n for (const [, entries] of arrowsByText) {\n if (entries.length < 2) continue\n const unmemoized = entries.filter((e) => !e.inMemo)\n if (unmemoized.length < 2) continue\n for (let i = 1; i < unmemoized.length; i++) {\n const entry = unmemoized[i]!\n ctx.reportDiagnostic({\n id: 'llui/missing-memo',\n severity: 'error',\n category: 'perf',\n message:\n `Duplicate accessor arrow used at multiple reactive binding sites without ` +\n `\\`memo()\\`. Wrap in \\`memo()\\` to share the computation: ` +\n `\\`const myAccessor = memo((s) => …)\\`, then reference it at each site.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, entry.node.getStart(sf), entry.node.getEnd()),\n },\n })\n }\n }\n }\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CompilerModule } from '../module.js';
|
|
2
|
+
import { type MessageAnnotations } from '../msg-annotations.js';
|
|
3
|
+
export interface MsgAnnotationsModuleOptions {
|
|
4
|
+
/** Pre-computed annotation map. Null when extraction failed; empty
|
|
5
|
+
* record when there are no Msg variants at all. */
|
|
6
|
+
msgAnnotations: Record<string, MessageAnnotations> | null;
|
|
7
|
+
}
|
|
8
|
+
export declare function msgAnnotationsModule(opts: MsgAnnotationsModuleOptions): CompilerModule;
|
|
9
|
+
//# sourceMappingURL=msg-annotations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-annotations.d.ts","sourceRoot":"","sources":["../../src/modules/msg-annotations.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,cAAc,CAAA;AACxE,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAA;AAI9B,MAAM,WAAW,2BAA2B;IAC1C;uDACmD;IACnD,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;CAC1D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,2BAA2B,GAAG,cAAc,CAsCtF"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// `msg-annotations` — emits `__msgAnnotations` per `component()` call
|
|
2
|
+
// when the annotation map has any non-default field, and populates the
|
|
3
|
+
// schema-hash inputs slot. Factory module taking the pre-computed
|
|
4
|
+
// annotation map (from `extractMsgAnnotations` / cross-file resolver
|
|
5
|
+
// upstream).
|
|
6
|
+
//
|
|
7
|
+
// Migrated from inline `injectMsgAnnotations` in transform.ts (v2c/
|
|
8
|
+
// decomp-4). The "gate on hasNonDefaultAnnotation" rule preserves: when
|
|
9
|
+
// every variant carries default intent/alwaysAffordable/etc., the
|
|
10
|
+
// emission is suppressed (runtime treats absence as defaults). This is
|
|
11
|
+
// a real win for un-annotated Msg unions, which dominate.
|
|
12
|
+
import { annotationsToObjectLiteral, hasNonDefaultAnnotation, } from '../msg-annotations.js';
|
|
13
|
+
import { SCHEMA_HASH_INPUTS_SLOT } from './schema-hash.js';
|
|
14
|
+
import { findComponentCalls } from './_shared.js';
|
|
15
|
+
export function msgAnnotationsModule(opts) {
|
|
16
|
+
return {
|
|
17
|
+
name: 'msg-annotations',
|
|
18
|
+
compilerVersion: '^0.3.0',
|
|
19
|
+
diagnostics: [],
|
|
20
|
+
// Targets captured in `emit` — see `_shared.ts`.
|
|
21
|
+
visitors: {},
|
|
22
|
+
emit(_ctx, analysis) {
|
|
23
|
+
const annotations = opts.msgAnnotations;
|
|
24
|
+
// Populate schema-hash inputs regardless of `hasNonDefault` — the
|
|
25
|
+
// hash is over the full annotation map (defaults included), so it
|
|
26
|
+
// must reflect the resolver's output verbatim.
|
|
27
|
+
if (annotations !== null) {
|
|
28
|
+
const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT);
|
|
29
|
+
if (hashInputs) {
|
|
30
|
+
hashInputs.msgAnnotations = annotations;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {
|
|
34
|
+
msgSchema: null,
|
|
35
|
+
stateSchema: null,
|
|
36
|
+
msgAnnotations: annotations,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (!annotations || !hasNonDefaultAnnotation(annotations))
|
|
41
|
+
return [];
|
|
42
|
+
const calls = findComponentCalls(analysis.sourceFile);
|
|
43
|
+
if (calls.length === 0)
|
|
44
|
+
return [];
|
|
45
|
+
return calls.map((call) => ({
|
|
46
|
+
module: 'msg-annotations',
|
|
47
|
+
field: '__msgAnnotations',
|
|
48
|
+
value: annotationsToObjectLiteral(annotations),
|
|
49
|
+
target: call,
|
|
50
|
+
}));
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=msg-annotations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-annotations.js","sourceRoot":"","sources":["../../src/modules/msg-annotations.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;AAClE,qEAAqE;AACrE,aAAa;AACb,EAAE;AACF,oEAAoE;AACpE,wEAAwE;AACxE,kEAAkE;AAClE,uEAAuE;AACvE,0DAA0D;AAG1D,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,GAExB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,uBAAuB,EAAyB,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAQjD,MAAM,UAAU,oBAAoB,CAAC,IAAiC;IACpE,OAAO;QACL,IAAI,EAAE,iBAAiB;QACvB,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE,EAAE;QACf,iDAAiD;QACjD,QAAQ,EAAE,EAAE;QAEZ,IAAI,CAAC,IAAI,EAAE,QAAQ;YACjB,MAAM,WAAW,GAAG,IAAI,CAAC,cAAc,CAAA;YACvC,kEAAkE;YAClE,kEAAkE;YAClE,+CAA+C;YAC/C,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;gBACzB,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAEpD,CAAA;gBACb,IAAI,UAAU,EAAE,CAAC;oBACf,UAAU,CAAC,cAAc,GAAG,WAAW,CAAA;gBACzC,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,EAAE;wBAC9C,SAAS,EAAE,IAAI;wBACf,WAAW,EAAE,IAAI;wBACjB,cAAc,EAAE,WAAW;qBACR,CAAC,CAAA;gBACxB,CAAC;YACH,CAAC;YACD,IAAI,CAAC,WAAW,IAAI,CAAC,uBAAuB,CAAC,WAAW,CAAC;gBAAE,OAAO,EAAE,CAAA;YACpE,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YACjC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC1B,MAAM,EAAE,iBAAiB;gBACzB,KAAK,EAAE,kBAAkB;gBACzB,KAAK,EAAE,0BAA0B,CAAC,WAAW,CAAC;gBAC9C,MAAM,EAAE,IAAI;aACb,CAAC,CAAC,CAAA;QACL,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// `msg-annotations` — emits `__msgAnnotations` per `component()` call\n// when the annotation map has any non-default field, and populates the\n// schema-hash inputs slot. Factory module taking the pre-computed\n// annotation map (from `extractMsgAnnotations` / cross-file resolver\n// upstream).\n//\n// Migrated from inline `injectMsgAnnotations` in transform.ts (v2c/\n// decomp-4). The \"gate on hasNonDefaultAnnotation\" rule preserves: when\n// every variant carries default intent/alwaysAffordable/etc., the\n// emission is suppressed (runtime treats absence as defaults). This is\n// a real win for un-annotated Msg unions, which dominate.\n\nimport type { CompilerModule, EmissionContribution } from '../module.js'\nimport {\n annotationsToObjectLiteral,\n hasNonDefaultAnnotation,\n type MessageAnnotations,\n} from '../msg-annotations.js'\nimport { SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs } from './schema-hash.js'\nimport { findComponentCalls } from './_shared.js'\n\nexport interface MsgAnnotationsModuleOptions {\n /** Pre-computed annotation map. Null when extraction failed; empty\n * record when there are no Msg variants at all. */\n msgAnnotations: Record<string, MessageAnnotations> | null\n}\n\nexport function msgAnnotationsModule(opts: MsgAnnotationsModuleOptions): CompilerModule {\n return {\n name: 'msg-annotations',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n // Targets captured in `emit` — see `_shared.ts`.\n visitors: {},\n\n emit(_ctx, analysis): EmissionContribution[] {\n const annotations = opts.msgAnnotations\n // Populate schema-hash inputs regardless of `hasNonDefault` — the\n // hash is over the full annotation map (defaults included), so it\n // must reflect the resolver's output verbatim.\n if (annotations !== null) {\n const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT) as\n | SchemaHashInputs\n | undefined\n if (hashInputs) {\n hashInputs.msgAnnotations = annotations\n } else {\n analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {\n msgSchema: null,\n stateSchema: null,\n msgAnnotations: annotations,\n } as SchemaHashInputs)\n }\n }\n if (!annotations || !hasNonDefaultAnnotation(annotations)) return []\n const calls = findComponentCalls(analysis.sourceFile)\n if (calls.length === 0) return []\n return calls.map((call) => ({\n module: 'msg-annotations',\n field: '__msgAnnotations',\n value: annotationsToObjectLiteral(annotations),\n target: call,\n }))\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { CompilerModule } from '../module.js';
|
|
2
|
+
import { type MsgSchema } from '../msg-schema.js';
|
|
3
|
+
export interface MsgSchemaModuleOptions {
|
|
4
|
+
/** Pre-computed Msg schema; null when extraction failed. */
|
|
5
|
+
msgSchema: MsgSchema | null;
|
|
6
|
+
/** Pre-computed Effect schema; null when not present in source. */
|
|
7
|
+
effectSchema: MsgSchema | null;
|
|
8
|
+
}
|
|
9
|
+
export declare function msgSchemaModule(opts: MsgSchemaModuleOptions): CompilerModule;
|
|
10
|
+
//# sourceMappingURL=msg-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-schema.d.ts","sourceRoot":"","sources":["../../src/modules/msg-schema.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,cAAc,EAAwB,MAAM,cAAc,CAAA;AACxE,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAIrE,MAAM,WAAW,sBAAsB;IACrC,4DAA4D;IAC5D,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IAC3B,mEAAmE;IACnE,YAAY,EAAE,SAAS,GAAG,IAAI,CAAA;CAC/B;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,cAAc,CAkD5E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// `msg-schema` — emits `__msgSchema` + `__effectSchema` per
|
|
2
|
+
// `component()` call and populates the schema-hash inputs slot.
|
|
3
|
+
// Factory module taking the pre-computed MsgSchema for Msg and Effect.
|
|
4
|
+
//
|
|
5
|
+
// Migrated from inline `injectMsgSchema` + `injectEffectSchema` in
|
|
6
|
+
// transform.ts (v2c/decomp-5). The literal builder
|
|
7
|
+
// `msgSchemaToLiteral` (from `msg-schema.ts`) is shared between the
|
|
8
|
+
// two emissions — Msg and Effect use the same discriminated-union
|
|
9
|
+
// shape on the wire.
|
|
10
|
+
//
|
|
11
|
+
// When this module is in the active list alongside `state-schema`
|
|
12
|
+
// and `msg-annotations`, the schema-hash inputs slot is fully
|
|
13
|
+
// populated and `schemaHashModule` produces the authoritative
|
|
14
|
+
// `__schemaHash` emission. At that point the inline
|
|
15
|
+
// `injectSchemaHash` in transform.ts also deletes — see
|
|
16
|
+
// v2c/decomp-5's migration.
|
|
17
|
+
import { msgSchemaToLiteral } from '../msg-schema.js';
|
|
18
|
+
import { SCHEMA_HASH_INPUTS_SLOT } from './schema-hash.js';
|
|
19
|
+
import { findComponentCalls } from './_shared.js';
|
|
20
|
+
export function msgSchemaModule(opts) {
|
|
21
|
+
return {
|
|
22
|
+
name: 'msg-schema',
|
|
23
|
+
compilerVersion: '^0.3.0',
|
|
24
|
+
diagnostics: [],
|
|
25
|
+
// Targets captured in `emit` — see `_shared.ts`.
|
|
26
|
+
visitors: {},
|
|
27
|
+
emit(ctx, analysis) {
|
|
28
|
+
// Populate schema-hash inputs slot with our msgSchema input — the
|
|
29
|
+
// schemaHashModule will pick it up alongside whatever
|
|
30
|
+
// stateSchemaModule and msgAnnotationsModule wrote.
|
|
31
|
+
if (opts.msgSchema !== null) {
|
|
32
|
+
const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT);
|
|
33
|
+
if (hashInputs) {
|
|
34
|
+
hashInputs.msgSchema = opts.msgSchema;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {
|
|
38
|
+
msgSchema: opts.msgSchema,
|
|
39
|
+
stateSchema: null,
|
|
40
|
+
msgAnnotations: null,
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const calls = findComponentCalls(analysis.sourceFile);
|
|
45
|
+
if (calls.length === 0)
|
|
46
|
+
return [];
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const call of calls) {
|
|
49
|
+
if (opts.msgSchema !== null) {
|
|
50
|
+
out.push({
|
|
51
|
+
module: 'msg-schema',
|
|
52
|
+
field: '__msgSchema',
|
|
53
|
+
value: msgSchemaToLiteral(opts.msgSchema, ctx.factory),
|
|
54
|
+
target: call,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
if (opts.effectSchema !== null) {
|
|
58
|
+
out.push({
|
|
59
|
+
module: 'msg-schema',
|
|
60
|
+
field: '__effectSchema',
|
|
61
|
+
value: msgSchemaToLiteral(opts.effectSchema, ctx.factory),
|
|
62
|
+
target: call,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=msg-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"msg-schema.js","sourceRoot":"","sources":["../../src/modules/msg-schema.ts"],"names":[],"mappings":"AAAA,4DAA4D;AAC5D,gEAAgE;AAChE,uEAAuE;AACvE,EAAE;AACF,mEAAmE;AACnE,mDAAmD;AACnD,oEAAoE;AACpE,kEAAkE;AAClE,qBAAqB;AACrB,EAAE;AACF,kEAAkE;AAClE,8DAA8D;AAC9D,8DAA8D;AAC9D,oDAAoD;AACpD,wDAAwD;AACxD,4BAA4B;AAG5B,OAAO,EAAE,kBAAkB,EAAkB,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,uBAAuB,EAAyB,MAAM,kBAAkB,CAAA;AACjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AASjD,MAAM,UAAU,eAAe,CAAC,IAA4B;IAC1D,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,eAAe,EAAE,QAAQ;QACzB,WAAW,EAAE,EAAE;QACf,iDAAiD;QACjD,QAAQ,EAAE,EAAE;QAEZ,IAAI,CAAC,GAAG,EAAE,QAAQ;YAChB,kEAAkE;YAClE,sDAAsD;YACtD,oDAAoD;YACpD,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;gBAC5B,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,CAEpD,CAAA;gBACb,IAAI,UAAU,EAAE,CAAC;oBACf,UAAU,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;gBACvC,CAAC;qBAAM,CAAC;oBACN,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,uBAAuB,EAAE;wBAC9C,SAAS,EAAE,IAAI,CAAC,SAAS;wBACzB,WAAW,EAAE,IAAI;wBACjB,cAAc,EAAE,IAAI;qBACD,CAAC,CAAA;gBACxB,CAAC;YACH,CAAC;YACD,MAAM,KAAK,GAAG,kBAAkB,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAA;YACjC,MAAM,GAAG,GAA2B,EAAE,CAAA;YACtC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;oBAC5B,GAAG,CAAC,IAAI,CAAC;wBACP,MAAM,EAAE,YAAY;wBACpB,KAAK,EAAE,aAAa;wBACpB,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC;wBACtD,MAAM,EAAE,IAAI;qBACb,CAAC,CAAA;gBACJ,CAAC;gBACD,IAAI,IAAI,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;oBAC/B,GAAG,CAAC,IAAI,CAAC;wBACP,MAAM,EAAE,YAAY;wBACpB,KAAK,EAAE,gBAAgB;wBACvB,KAAK,EAAE,kBAAkB,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,OAAO,CAAC;wBACzD,MAAM,EAAE,IAAI;qBACb,CAAC,CAAA;gBACJ,CAAC;YACH,CAAC;YACD,OAAO,GAAG,CAAA;QACZ,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["// `msg-schema` — emits `__msgSchema` + `__effectSchema` per\n// `component()` call and populates the schema-hash inputs slot.\n// Factory module taking the pre-computed MsgSchema for Msg and Effect.\n//\n// Migrated from inline `injectMsgSchema` + `injectEffectSchema` in\n// transform.ts (v2c/decomp-5). The literal builder\n// `msgSchemaToLiteral` (from `msg-schema.ts`) is shared between the\n// two emissions — Msg and Effect use the same discriminated-union\n// shape on the wire.\n//\n// When this module is in the active list alongside `state-schema`\n// and `msg-annotations`, the schema-hash inputs slot is fully\n// populated and `schemaHashModule` produces the authoritative\n// `__schemaHash` emission. At that point the inline\n// `injectSchemaHash` in transform.ts also deletes — see\n// v2c/decomp-5's migration.\n\nimport type { CompilerModule, EmissionContribution } from '../module.js'\nimport { msgSchemaToLiteral, type MsgSchema } from '../msg-schema.js'\nimport { SCHEMA_HASH_INPUTS_SLOT, type SchemaHashInputs } from './schema-hash.js'\nimport { findComponentCalls } from './_shared.js'\n\nexport interface MsgSchemaModuleOptions {\n /** Pre-computed Msg schema; null when extraction failed. */\n msgSchema: MsgSchema | null\n /** Pre-computed Effect schema; null when not present in source. */\n effectSchema: MsgSchema | null\n}\n\nexport function msgSchemaModule(opts: MsgSchemaModuleOptions): CompilerModule {\n return {\n name: 'msg-schema',\n compilerVersion: '^0.3.0',\n diagnostics: [],\n // Targets captured in `emit` — see `_shared.ts`.\n visitors: {},\n\n emit(ctx, analysis): EmissionContribution[] {\n // Populate schema-hash inputs slot with our msgSchema input — the\n // schemaHashModule will pick it up alongside whatever\n // stateSchemaModule and msgAnnotationsModule wrote.\n if (opts.msgSchema !== null) {\n const hashInputs = analysis.perModule.get(SCHEMA_HASH_INPUTS_SLOT) as\n | SchemaHashInputs\n | undefined\n if (hashInputs) {\n hashInputs.msgSchema = opts.msgSchema\n } else {\n analysis.perModule.set(SCHEMA_HASH_INPUTS_SLOT, {\n msgSchema: opts.msgSchema,\n stateSchema: null,\n msgAnnotations: null,\n } as SchemaHashInputs)\n }\n }\n const calls = findComponentCalls(analysis.sourceFile)\n if (calls.length === 0) return []\n const out: EmissionContribution[] = []\n for (const call of calls) {\n if (opts.msgSchema !== null) {\n out.push({\n module: 'msg-schema',\n field: '__msgSchema',\n value: msgSchemaToLiteral(opts.msgSchema, ctx.factory),\n target: call,\n })\n }\n if (opts.effectSchema !== null) {\n out.push({\n module: 'msg-schema',\n field: '__effectSchema',\n value: msgSchemaToLiteral(opts.effectSchema, ctx.factory),\n target: call,\n })\n }\n }\n return out\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace-import.d.ts","sourceRoot":"","sources":["../../src/modules/namespace-import.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAWlD,wBAAgB,qBAAqB,IAAI,cAAc,CA4DtD"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// `namespace-import` — errors on `import * as L from '@llui/*'` for any
|
|
2
|
+
// LLui package whose surface is reactive-aware or modular. The compiler
|
|
3
|
+
// walks files looking for *named* references to helpers; namespace
|
|
4
|
+
// imports route through `L.div(...)` which the matcher doesn't see,
|
|
5
|
+
// silently disabling compile optimizations. Also defeats tree-shaking.
|
|
6
|
+
// Migrated from `@llui/eslint-plugin/src/rules/namespace-import.ts`.
|
|
7
|
+
// Autofix dropped per the migration plan; the error message includes
|
|
8
|
+
// the exact named-import replacement enumerating used members.
|
|
9
|
+
import ts from 'typescript';
|
|
10
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
11
|
+
const TARGETS = new Set([
|
|
12
|
+
'@llui/dom',
|
|
13
|
+
'@llui/components',
|
|
14
|
+
'@llui/router',
|
|
15
|
+
'@llui/transitions',
|
|
16
|
+
'@llui/effects',
|
|
17
|
+
'@llui/agent',
|
|
18
|
+
]);
|
|
19
|
+
export function namespaceImportModule() {
|
|
20
|
+
return {
|
|
21
|
+
name: 'namespace-import',
|
|
22
|
+
compilerVersion: '^0.3.0',
|
|
23
|
+
diagnostics: [
|
|
24
|
+
{
|
|
25
|
+
id: 'llui/namespace-import',
|
|
26
|
+
description: 'Namespace import from @llui/* disables compiler opts and defeats tree-shaking.',
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
visitors: {
|
|
30
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
31
|
+
const visited = node;
|
|
32
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
33
|
+
for (const stmt of sf.statements) {
|
|
34
|
+
if (!ts.isImportDeclaration(stmt))
|
|
35
|
+
continue;
|
|
36
|
+
if (!ts.isStringLiteral(stmt.moduleSpecifier))
|
|
37
|
+
continue;
|
|
38
|
+
const src = stmt.moduleSpecifier.text;
|
|
39
|
+
if (!TARGETS.has(src))
|
|
40
|
+
continue;
|
|
41
|
+
const clause = stmt.importClause;
|
|
42
|
+
if (!clause || !clause.namedBindings)
|
|
43
|
+
continue;
|
|
44
|
+
if (!ts.isNamespaceImport(clause.namedBindings))
|
|
45
|
+
continue;
|
|
46
|
+
const localName = clause.namedBindings.name.text;
|
|
47
|
+
// Collect referenced members: `local.member` accesses.
|
|
48
|
+
const usedMembers = new Set();
|
|
49
|
+
const collect = (n) => {
|
|
50
|
+
if (ts.isPropertyAccessExpression(n) &&
|
|
51
|
+
ts.isIdentifier(n.expression) &&
|
|
52
|
+
n.expression.text === localName &&
|
|
53
|
+
ts.isIdentifier(n.name)) {
|
|
54
|
+
usedMembers.add(n.name.text);
|
|
55
|
+
}
|
|
56
|
+
ts.forEachChild(n, collect);
|
|
57
|
+
};
|
|
58
|
+
collect(sf);
|
|
59
|
+
const sortedMembers = [...usedMembers].sort();
|
|
60
|
+
const fix = sortedMembers.length > 0
|
|
61
|
+
? `Fix: replace with \`import { ${sortedMembers.join(', ')} } from '${src}'\` and rewrite each \`${localName}.X\` to bare \`X\`.`
|
|
62
|
+
: `Fix: replace with named imports listing exactly the helpers you use from '${src}'.`;
|
|
63
|
+
ctx.reportDiagnostic({
|
|
64
|
+
id: 'llui/namespace-import',
|
|
65
|
+
severity: 'error',
|
|
66
|
+
category: 'style',
|
|
67
|
+
message: `Namespace import \`${localName}\` from '${src}' disables compiler optimizations ` +
|
|
68
|
+
`(template-clone + elSplit rewriting recognize *named* call sites only) and defeats ` +
|
|
69
|
+
`tree-shaking on broad-surface packages. ${fix}`,
|
|
70
|
+
location: {
|
|
71
|
+
file: sf.fileName,
|
|
72
|
+
range: rangeFromOffsets(sf.text, stmt.getStart(sf), stmt.getEnd()),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=namespace-import.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace-import.js","sourceRoot":"","sources":["../../src/modules/namespace-import.ts"],"names":[],"mappings":"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","sourcesContent":["// `namespace-import` — errors on `import * as L from '@llui/*'` for any\n// LLui package whose surface is reactive-aware or modular. The compiler\n// walks files looking for *named* references to helpers; namespace\n// imports route through `L.div(...)` which the matcher doesn't see,\n// silently disabling compile optimizations. Also defeats tree-shaking.\n// Migrated from `@llui/eslint-plugin/src/rules/namespace-import.ts`.\n// Autofix dropped per the migration plan; the error message includes\n// the exact named-import replacement enumerating used members.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\nconst TARGETS = new Set([\n '@llui/dom',\n '@llui/components',\n '@llui/router',\n '@llui/transitions',\n '@llui/effects',\n '@llui/agent',\n])\n\nexport function namespaceImportModule(): CompilerModule {\n return {\n name: 'namespace-import',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/namespace-import',\n description:\n 'Namespace import from @llui/* disables compiler opts and defeats tree-shaking.',\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 for (const stmt of sf.statements) {\n if (!ts.isImportDeclaration(stmt)) continue\n if (!ts.isStringLiteral(stmt.moduleSpecifier)) continue\n const src = stmt.moduleSpecifier.text\n if (!TARGETS.has(src)) continue\n const clause = stmt.importClause\n if (!clause || !clause.namedBindings) continue\n if (!ts.isNamespaceImport(clause.namedBindings)) continue\n const localName = clause.namedBindings.name.text\n // Collect referenced members: `local.member` accesses.\n const usedMembers = new Set<string>()\n const collect = (n: ts.Node): void => {\n if (\n ts.isPropertyAccessExpression(n) &&\n ts.isIdentifier(n.expression) &&\n n.expression.text === localName &&\n ts.isIdentifier(n.name)\n ) {\n usedMembers.add(n.name.text)\n }\n ts.forEachChild(n, collect)\n }\n collect(sf)\n const sortedMembers = [...usedMembers].sort()\n const fix =\n sortedMembers.length > 0\n ? `Fix: replace with \\`import { ${sortedMembers.join(', ')} } from '${src}'\\` and rewrite each \\`${localName}.X\\` to bare \\`X\\`.`\n : `Fix: replace with named imports listing exactly the helpers you use from '${src}'.`\n ctx.reportDiagnostic({\n id: 'llui/namespace-import',\n severity: 'error',\n category: 'style',\n message:\n `Namespace import \\`${localName}\\` from '${src}' disables compiler optimizations ` +\n `(template-clone + elSplit rewriting recognize *named* call sites only) and defeats ` +\n `tree-shaking on broad-surface packages. ${fix}`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, stmt.getStart(sf), stmt.getEnd()),\n },\n })\n }\n },\n },\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nested-send-in-update.d.ts","sourceRoot":"","sources":["../../src/modules/nested-send-in-update.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAkBlD,wBAAgB,wBAAwB,IAAI,cAAc,CAuDzD"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// `nested-send-in-update` — errors when a `send()` call appears
|
|
2
|
+
// directly inside a component's update function. Calling `send` from
|
|
3
|
+
// update causes recursive dispatch (update → send → update → …);
|
|
4
|
+
// the correct pattern is to return `[newState, [effect]]` so the
|
|
5
|
+
// runtime drives the next message via the effect handler. Migrated
|
|
6
|
+
// from `@llui/eslint-plugin/src/rules/nested-send-in-update.ts`.
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
9
|
+
import { findComponentCalls } from './_shared.js';
|
|
10
|
+
function findUpdateProperty(call) {
|
|
11
|
+
const arg = call.arguments[0];
|
|
12
|
+
if (!arg || !ts.isObjectLiteralExpression(arg))
|
|
13
|
+
return undefined;
|
|
14
|
+
for (const prop of arg.properties) {
|
|
15
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
16
|
+
ts.isIdentifier(prop.name) &&
|
|
17
|
+
prop.name.text === 'update') {
|
|
18
|
+
return prop;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
export function nestedSendInUpdateModule() {
|
|
24
|
+
return {
|
|
25
|
+
name: 'nested-send-in-update',
|
|
26
|
+
compilerVersion: '^0.3.0',
|
|
27
|
+
diagnostics: [
|
|
28
|
+
{
|
|
29
|
+
id: 'llui/nested-send-in-update',
|
|
30
|
+
description: 'send() called inside update() — return effects instead.',
|
|
31
|
+
},
|
|
32
|
+
],
|
|
33
|
+
visitors: {
|
|
34
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
35
|
+
const visited = node;
|
|
36
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
37
|
+
for (const call of findComponentCalls(sf)) {
|
|
38
|
+
const updateProp = findUpdateProperty(call);
|
|
39
|
+
if (!updateProp)
|
|
40
|
+
continue;
|
|
41
|
+
const fn = updateProp.initializer;
|
|
42
|
+
if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn))
|
|
43
|
+
continue;
|
|
44
|
+
// Walk the update body. `send()` calls directly inside update
|
|
45
|
+
// are violations; calls inside a nested function are deferred
|
|
46
|
+
// callbacks and fine.
|
|
47
|
+
const walk = (n) => {
|
|
48
|
+
if (ts.isArrowFunction(n) ||
|
|
49
|
+
ts.isFunctionExpression(n) ||
|
|
50
|
+
ts.isFunctionDeclaration(n)) {
|
|
51
|
+
if (n !== fn)
|
|
52
|
+
return; // boundary — don't descend into nested fns
|
|
53
|
+
}
|
|
54
|
+
if (ts.isCallExpression(n) &&
|
|
55
|
+
ts.isIdentifier(n.expression) &&
|
|
56
|
+
n.expression.text === 'send') {
|
|
57
|
+
ctx.reportDiagnostic({
|
|
58
|
+
id: 'llui/nested-send-in-update',
|
|
59
|
+
severity: 'error',
|
|
60
|
+
category: 'reactivity',
|
|
61
|
+
message: '`send()` called directly inside update() — causes recursive dispatch. Return an effect instead: `return [newState, [myEffect]]`, and the effect handler can send follow-up messages.',
|
|
62
|
+
location: {
|
|
63
|
+
file: sf.fileName,
|
|
64
|
+
range: rangeFromOffsets(sf.text, n.getStart(sf), n.getEnd()),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
ts.forEachChild(n, walk);
|
|
69
|
+
};
|
|
70
|
+
if (fn.body)
|
|
71
|
+
walk(fn.body);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=nested-send-in-update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nested-send-in-update.js","sourceRoot":"","sources":["../../src/modules/nested-send-in-update.ts"],"names":[],"mappings":"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","sourcesContent":["// `nested-send-in-update` — errors when a `send()` call appears\n// directly inside a component's update function. Calling `send` from\n// update causes recursive dispatch (update → send → update → …);\n// the correct pattern is to return `[newState, [effect]]` so the\n// runtime drives the next message via the effect handler. Migrated\n// from `@llui/eslint-plugin/src/rules/nested-send-in-update.ts`.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\nimport { findComponentCalls } from './_shared.js'\n\nfunction findUpdateProperty(call: ts.CallExpression): ts.PropertyAssignment | undefined {\n const arg = call.arguments[0]\n if (!arg || !ts.isObjectLiteralExpression(arg)) return undefined\n for (const prop of arg.properties) {\n if (\n ts.isPropertyAssignment(prop) &&\n ts.isIdentifier(prop.name) &&\n prop.name.text === 'update'\n ) {\n return prop\n }\n }\n return undefined\n}\n\nexport function nestedSendInUpdateModule(): CompilerModule {\n return {\n name: 'nested-send-in-update',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/nested-send-in-update',\n description: 'send() called inside update() — return effects instead.',\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 for (const call of findComponentCalls(sf)) {\n const updateProp = findUpdateProperty(call)\n if (!updateProp) continue\n const fn = updateProp.initializer\n if (!ts.isArrowFunction(fn) && !ts.isFunctionExpression(fn)) continue\n\n // Walk the update body. `send()` calls directly inside update\n // are violations; calls inside a nested function are deferred\n // callbacks and fine.\n const walk = (n: ts.Node): void => {\n if (\n ts.isArrowFunction(n) ||\n ts.isFunctionExpression(n) ||\n ts.isFunctionDeclaration(n)\n ) {\n if (n !== fn) return // boundary — don't descend into nested fns\n }\n if (\n ts.isCallExpression(n) &&\n ts.isIdentifier(n.expression) &&\n n.expression.text === 'send'\n ) {\n ctx.reportDiagnostic({\n id: 'llui/nested-send-in-update',\n severity: 'error',\n category: 'reactivity',\n message:\n '`send()` called directly inside update() — causes recursive dispatch. Return an effect instead: `return [newState, [myEffect]]`, and the effect handler can send follow-up messages.',\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 if (fn.body) walk(fn.body)\n }\n },\n },\n }\n}\n"]}
|