@llui/compiler 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/emit-names.d.ts +42 -0
- package/dist/emit-names.d.ts.map +1 -0
- package/dist/emit-names.js +73 -0
- package/dist/emit-names.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lint-modules.d.ts.map +1 -1
- package/dist/lint-modules.js +4 -0
- package/dist/lint-modules.js.map +1 -1
- package/dist/modules/no-repeated-item-current.d.ts +3 -0
- package/dist/modules/no-repeated-item-current.d.ts.map +1 -0
- package/dist/modules/no-repeated-item-current.js +164 -0
- package/dist/modules/no-repeated-item-current.js.map +1 -0
- package/dist/modules/no-sample-in-event-handler.d.ts +3 -0
- package/dist/modules/no-sample-in-event-handler.d.ts.map +1 -0
- package/dist/modules/no-sample-in-event-handler.js +101 -0
- package/dist/modules/no-sample-in-event-handler.js.map +1 -0
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +87 -45
- package/dist/transform.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the compiler's emission name registry.
|
|
3
|
+
*
|
|
4
|
+
* Two disjoint sets:
|
|
5
|
+
*
|
|
6
|
+
* - `COMPILER_RENAMEABLE_KEYS` — property keys the compiler synthesizes
|
|
7
|
+
* onto `component({...})` literals. The runtime reads these via
|
|
8
|
+
* property access (`def.__view`, `def.__prefixes`, etc.) inside the
|
|
9
|
+
* same bundle that the compiler emitted them into. Their producer and
|
|
10
|
+
* consumer are colocated in the bundle, so the vite-plugin's post-
|
|
11
|
+
* bundle property-rename pass can shorten them to `$a`/`$b`/… without
|
|
12
|
+
* breaking the contract.
|
|
13
|
+
*
|
|
14
|
+
* - `COMPILER_DOM_INTERNAL_IMPORTS` — runtime helpers the compiler
|
|
15
|
+
* references by NAME (not by property key) via an
|
|
16
|
+
* `import { __cloneStaticTemplate } from '@llui/dom/internal'`
|
|
17
|
+
* declaration. These cross a module boundary at consumer build time.
|
|
18
|
+
* Anything the rename pass touches that ends up in an import specifier
|
|
19
|
+
* would be rewritten to `$X`, which the source package never exports,
|
|
20
|
+
* and rolldown fails the build with `MISSING_EXPORT`. **These names
|
|
21
|
+
* must NEVER be renamed.**
|
|
22
|
+
*
|
|
23
|
+
* The two sets are disjoint by construction — the type-level
|
|
24
|
+
* `Extract<...>` assertion below fails compilation if any name appears
|
|
25
|
+
* in both lists. New compiler-emitted names land in whichever list
|
|
26
|
+
* matches their lifetime; if you accidentally add one to both, `tsc`
|
|
27
|
+
* tells you before the bug ships.
|
|
28
|
+
*
|
|
29
|
+
* Subpath choice matters: the helpers live at `@llui/dom/internal`, not
|
|
30
|
+
* at the root `@llui/dom`, because the rename regex matches any
|
|
31
|
+
* `__`-prefixed identifier in the bundle. By hosting the helpers on a
|
|
32
|
+
* subpath whose import specifier never gets touched by the rename, we
|
|
33
|
+
* keep both the regex and the runtime export surface internally
|
|
34
|
+
* consistent without needing an AST-aware rename pass.
|
|
35
|
+
*/
|
|
36
|
+
export declare const COMPILER_RENAMEABLE_KEYS: readonly ["__view", "__view$", "__prefixes", "__handlers", "__compilerVersion", "__directUpdate", "__mask", "__maskHi", "__maskLegend", "__perItem", "__rowUpd", "__rowUpdate", "__schemaHash", "__tpl", "__msgSchema", "__msgAnnotations", "__bindingDescriptors", "__stateSchema", "__effectSchema", "__componentMeta", "__renderToString", "__update", "__dirty"];
|
|
37
|
+
export type CompilerRenameableKey = (typeof COMPILER_RENAMEABLE_KEYS)[number];
|
|
38
|
+
export declare const COMPILER_DOM_INTERNAL_IMPORTS: readonly ["__bindUncertain", "__cloneStaticTemplate", "__runPhase2", "__handleMsg", "__registerScopeVariants", "__clientOnlyStub"];
|
|
39
|
+
export type CompilerDomInternalImport = (typeof COMPILER_DOM_INTERNAL_IMPORTS)[number];
|
|
40
|
+
/** Module specifier the compiler emits for the internal-helper imports. */
|
|
41
|
+
export declare const DOM_INTERNAL_MODULE_SPECIFIER = "@llui/dom/internal";
|
|
42
|
+
//# sourceMappingURL=emit-names.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-names.d.ts","sourceRoot":"","sources":["../src/emit-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,eAAO,MAAM,wBAAwB,sWAwB3B,CAAA;AAEV,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAA;AAE7E,eAAO,MAAM,6BAA6B,oIAOhC,CAAA;AAEV,MAAM,MAAM,yBAAyB,GAAG,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAA;AAUtF,2EAA2E;AAC3E,eAAO,MAAM,6BAA6B,uBAAuB,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for the compiler's emission name registry.
|
|
3
|
+
*
|
|
4
|
+
* Two disjoint sets:
|
|
5
|
+
*
|
|
6
|
+
* - `COMPILER_RENAMEABLE_KEYS` — property keys the compiler synthesizes
|
|
7
|
+
* onto `component({...})` literals. The runtime reads these via
|
|
8
|
+
* property access (`def.__view`, `def.__prefixes`, etc.) inside the
|
|
9
|
+
* same bundle that the compiler emitted them into. Their producer and
|
|
10
|
+
* consumer are colocated in the bundle, so the vite-plugin's post-
|
|
11
|
+
* bundle property-rename pass can shorten them to `$a`/`$b`/… without
|
|
12
|
+
* breaking the contract.
|
|
13
|
+
*
|
|
14
|
+
* - `COMPILER_DOM_INTERNAL_IMPORTS` — runtime helpers the compiler
|
|
15
|
+
* references by NAME (not by property key) via an
|
|
16
|
+
* `import { __cloneStaticTemplate } from '@llui/dom/internal'`
|
|
17
|
+
* declaration. These cross a module boundary at consumer build time.
|
|
18
|
+
* Anything the rename pass touches that ends up in an import specifier
|
|
19
|
+
* would be rewritten to `$X`, which the source package never exports,
|
|
20
|
+
* and rolldown fails the build with `MISSING_EXPORT`. **These names
|
|
21
|
+
* must NEVER be renamed.**
|
|
22
|
+
*
|
|
23
|
+
* The two sets are disjoint by construction — the type-level
|
|
24
|
+
* `Extract<...>` assertion below fails compilation if any name appears
|
|
25
|
+
* in both lists. New compiler-emitted names land in whichever list
|
|
26
|
+
* matches their lifetime; if you accidentally add one to both, `tsc`
|
|
27
|
+
* tells you before the bug ships.
|
|
28
|
+
*
|
|
29
|
+
* Subpath choice matters: the helpers live at `@llui/dom/internal`, not
|
|
30
|
+
* at the root `@llui/dom`, because the rename regex matches any
|
|
31
|
+
* `__`-prefixed identifier in the bundle. By hosting the helpers on a
|
|
32
|
+
* subpath whose import specifier never gets touched by the rename, we
|
|
33
|
+
* keep both the regex and the runtime export surface internally
|
|
34
|
+
* consistent without needing an AST-aware rename pass.
|
|
35
|
+
*/
|
|
36
|
+
export const COMPILER_RENAMEABLE_KEYS = [
|
|
37
|
+
'__view',
|
|
38
|
+
'__view$',
|
|
39
|
+
'__prefixes',
|
|
40
|
+
'__handlers',
|
|
41
|
+
'__compilerVersion',
|
|
42
|
+
'__directUpdate',
|
|
43
|
+
'__mask',
|
|
44
|
+
'__maskHi',
|
|
45
|
+
'__maskLegend',
|
|
46
|
+
'__perItem',
|
|
47
|
+
'__rowUpd',
|
|
48
|
+
'__rowUpdate',
|
|
49
|
+
'__schemaHash',
|
|
50
|
+
'__tpl',
|
|
51
|
+
'__msgSchema',
|
|
52
|
+
'__msgAnnotations',
|
|
53
|
+
'__bindingDescriptors',
|
|
54
|
+
'__stateSchema',
|
|
55
|
+
'__effectSchema',
|
|
56
|
+
'__componentMeta',
|
|
57
|
+
'__renderToString',
|
|
58
|
+
'__update',
|
|
59
|
+
'__dirty',
|
|
60
|
+
];
|
|
61
|
+
export const COMPILER_DOM_INTERNAL_IMPORTS = [
|
|
62
|
+
'__bindUncertain',
|
|
63
|
+
'__cloneStaticTemplate',
|
|
64
|
+
'__runPhase2',
|
|
65
|
+
'__handleMsg',
|
|
66
|
+
'__registerScopeVariants',
|
|
67
|
+
'__clientOnlyStub',
|
|
68
|
+
];
|
|
69
|
+
const _disjointnessProof = true;
|
|
70
|
+
void _disjointnessProof;
|
|
71
|
+
/** Module specifier the compiler emits for the internal-helper imports. */
|
|
72
|
+
export const DOM_INTERNAL_MODULE_SPECIFIER = '@llui/dom/internal';
|
|
73
|
+
//# sourceMappingURL=emit-names.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit-names.js","sourceRoot":"","sources":["../src/emit-names.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,QAAQ;IACR,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,mBAAmB;IACnB,gBAAgB;IAChB,QAAQ;IACR,UAAU;IACV,cAAc;IACd,WAAW;IACX,UAAU;IACV,aAAa;IACb,cAAc;IACd,OAAO;IACP,aAAa;IACb,kBAAkB;IAClB,sBAAsB;IACtB,eAAe;IACf,gBAAgB;IAChB,iBAAiB;IACjB,kBAAkB;IAClB,UAAU;IACV,SAAS;CACD,CAAA;AAIV,MAAM,CAAC,MAAM,6BAA6B,GAAG;IAC3C,iBAAiB;IACjB,uBAAuB;IACvB,aAAa;IACb,aAAa;IACb,yBAAyB;IACzB,kBAAkB;CACV,CAAA;AASV,MAAM,kBAAkB,GAA2C,IAAI,CAAA;AACvE,KAAK,kBAAkB,CAAA;AAEvB,2EAA2E;AAC3E,MAAM,CAAC,MAAM,6BAA6B,GAAG,oBAAoB,CAAA","sourcesContent":["/**\n * Single source of truth for the compiler's emission name registry.\n *\n * Two disjoint sets:\n *\n * - `COMPILER_RENAMEABLE_KEYS` — property keys the compiler synthesizes\n * onto `component({...})` literals. The runtime reads these via\n * property access (`def.__view`, `def.__prefixes`, etc.) inside the\n * same bundle that the compiler emitted them into. Their producer and\n * consumer are colocated in the bundle, so the vite-plugin's post-\n * bundle property-rename pass can shorten them to `$a`/`$b`/… without\n * breaking the contract.\n *\n * - `COMPILER_DOM_INTERNAL_IMPORTS` — runtime helpers the compiler\n * references by NAME (not by property key) via an\n * `import { __cloneStaticTemplate } from '@llui/dom/internal'`\n * declaration. These cross a module boundary at consumer build time.\n * Anything the rename pass touches that ends up in an import specifier\n * would be rewritten to `$X`, which the source package never exports,\n * and rolldown fails the build with `MISSING_EXPORT`. **These names\n * must NEVER be renamed.**\n *\n * The two sets are disjoint by construction — the type-level\n * `Extract<...>` assertion below fails compilation if any name appears\n * in both lists. New compiler-emitted names land in whichever list\n * matches their lifetime; if you accidentally add one to both, `tsc`\n * tells you before the bug ships.\n *\n * Subpath choice matters: the helpers live at `@llui/dom/internal`, not\n * at the root `@llui/dom`, because the rename regex matches any\n * `__`-prefixed identifier in the bundle. By hosting the helpers on a\n * subpath whose import specifier never gets touched by the rename, we\n * keep both the regex and the runtime export surface internally\n * consistent without needing an AST-aware rename pass.\n */\n\nexport const COMPILER_RENAMEABLE_KEYS = [\n '__view',\n '__view$',\n '__prefixes',\n '__handlers',\n '__compilerVersion',\n '__directUpdate',\n '__mask',\n '__maskHi',\n '__maskLegend',\n '__perItem',\n '__rowUpd',\n '__rowUpdate',\n '__schemaHash',\n '__tpl',\n '__msgSchema',\n '__msgAnnotations',\n '__bindingDescriptors',\n '__stateSchema',\n '__effectSchema',\n '__componentMeta',\n '__renderToString',\n '__update',\n '__dirty',\n] as const\n\nexport type CompilerRenameableKey = (typeof COMPILER_RENAMEABLE_KEYS)[number]\n\nexport const COMPILER_DOM_INTERNAL_IMPORTS = [\n '__bindUncertain',\n '__cloneStaticTemplate',\n '__runPhase2',\n '__handleMsg',\n '__registerScopeVariants',\n '__clientOnlyStub',\n] as const\n\nexport type CompilerDomInternalImport = (typeof COMPILER_DOM_INTERNAL_IMPORTS)[number]\n\n// Compile-time proof that the two sets are disjoint. If any name appears\n// in both lists, `Extract<...>` resolves to that name's string literal\n// instead of `never`, and the assignment fails. Move the offending name\n// to one list or the other; never both.\ntype _Disjoint = Extract<CompilerRenameableKey, CompilerDomInternalImport>\nconst _disjointnessProof: _Disjoint extends never ? true : false = true\nvoid _disjointnessProof\n\n/** Module specifier the compiler emits for the internal-helper imports. */\nexport const DOM_INTERNAL_MODULE_SPECIFIER = '@llui/dom/internal'\n"]}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA;AACtC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,4BAA4B,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AASzD,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,GACjC,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnF,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AACtF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,GACxB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AACzF,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,GACvB,MAAM,6BAA6B,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA;AACtC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,4BAA4B,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AASzD,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EACL,cAAc,EACd,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,YAAY,GAClB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,oBAAoB,EACpB,KAAK,2BAA2B,GACjC,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnF,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AACtF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,2BAA2B,EAChC,KAAK,kBAAkB,GACxB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,gBAAgB,EAAE,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AACzF,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,0BAA0B,EAC/B,KAAK,iBAAiB,GACvB,MAAM,6BAA6B,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// @llui/compiler — engine. Adapters consume through these re-exports.
|
|
2
2
|
// Migration in progress (see docs/proposals/v2-compiler/v2a.md §4.4).
|
|
3
|
+
export * from './emit-names.js';
|
|
3
4
|
export * from './accessor-resolver.js';
|
|
4
5
|
export * from './binding-descriptors.js';
|
|
5
6
|
export * from './collect-deps.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,sEAAsE;AACtE,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA;AACtC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,4BAA4B,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,wEAAwE;AACxE,2EAA2E;AAC3E,uEAAuE;AACvE,oEAAoE;AACpE,wEAAwE;AACxE,qEAAqE;AACrE,6BAA6B;AAC7B,wEAAwE;AACxE,OAAO,EAAE,gBAAgB,EAAgC,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EACL,cAAc,EACd,cAAc,GAGf,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,oBAAoB,GAErB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,cAAc,EAA8B,MAAM,wBAAwB,CAAA;AACnF,OAAO,EAAE,eAAe,EAA+B,MAAM,yBAAyB,CAAA;AACtF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAGrB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,gBAAgB,EAAgC,MAAM,0BAA0B,CAAA;AACzF,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GAGpB,MAAM,6BAA6B,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA","sourcesContent":["// @llui/compiler — engine. Adapters consume through these re-exports.\n// Migration in progress (see docs/proposals/v2-compiler/v2a.md §4.4).\nexport * from './accessor-resolver.js'\nexport * from './binding-descriptors.js'\nexport * from './collect-deps.js'\nexport * from './compiler-cache.js'\nexport * from './cross-file-resolver.js'\nexport * from './cross-file-walker.js'\nexport * from './diagnostic.js'\nexport * from './manifest.js'\nexport * from './module.js'\nexport * from './version.js'\nexport * from './introspection-factory.js'\nexport { findComponentCalls } from './modules/_shared.js'\n// Introspection modules (schemaHashModule, msg-schema, msg-annotations,\n// state-schema, binding-descriptors) moved to @llui/compiler-introspection\n// in v2c/decomp-26. Adapters that previously imported these names from\n// @llui/compiler must now import from @llui/compiler-introspection.\n// BINDING_DESCRIPTORS_SLOT is re-exported from introspection-factory.js\n// (above) so the orchestrator can read the slot without depending on\n// the introspection package.\n// componentMetaModule moved to @llui/compiler-devtools (v2c/decomp-27).\nexport { maskLegendModule, type MaskLegendModuleOptions } from './modules/mask-legend.js'\nexport { compilerStampModule } from './modules/compiler-stamp.js'\nexport {\n eachMemoModule,\n EACH_MEMO_SLOT,\n type EachMemoModuleOptions,\n type EachMemoSlot,\n} from './modules/each-memo.js'\nexport {\n structuralMaskModule,\n type StructuralMaskModuleOptions,\n} from './modules/structural-mask.js'\nexport { textMaskModule, type TextMaskModuleOptions } from './modules/text-mask.js'\nexport { itemDedupModule, type ItemDedupModuleOptions } from './modules/item-dedup.js'\nexport {\n elementRewriteModule,\n ELEMENT_REWRITE_SLOT,\n type ElementRewriteModuleOptions,\n type ElementRewriteSlot,\n} from './modules/element-rewrite.js'\nexport { rowFactoryModule, type RowFactoryModuleOptions } from './modules/row-factory.js'\nexport {\n coreSynthesisModule,\n CORE_SYNTHESIS_SLOT,\n type CoreSynthesisModuleOptions,\n type CoreSynthesisSlot,\n} from './modules/core-synthesis.js'\nexport * from './msg-annotations.js'\nexport * from './msg-schema.js'\nexport * from './schema-hash.js'\nexport * from './state-schema.js'\nexport * from './transform.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,sEAAsE;AACtE,cAAc,iBAAiB,CAAA;AAC/B,cAAc,wBAAwB,CAAA;AACtC,cAAc,0BAA0B,CAAA;AACxC,cAAc,mBAAmB,CAAA;AACjC,cAAc,qBAAqB,CAAA;AACnC,cAAc,0BAA0B,CAAA;AACxC,cAAc,wBAAwB,CAAA;AACtC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,4BAA4B,CAAA;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,wEAAwE;AACxE,2EAA2E;AAC3E,uEAAuE;AACvE,oEAAoE;AACpE,wEAAwE;AACxE,qEAAqE;AACrE,6BAA6B;AAC7B,wEAAwE;AACxE,OAAO,EAAE,gBAAgB,EAAgC,MAAM,0BAA0B,CAAA;AACzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EACL,cAAc,EACd,cAAc,GAGf,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,oBAAoB,GAErB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,cAAc,EAA8B,MAAM,wBAAwB,CAAA;AACnF,OAAO,EAAE,eAAe,EAA+B,MAAM,yBAAyB,CAAA;AACtF,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GAGrB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,gBAAgB,EAAgC,MAAM,0BAA0B,CAAA;AACzF,OAAO,EACL,mBAAmB,EACnB,mBAAmB,GAGpB,MAAM,6BAA6B,CAAA;AACpC,cAAc,sBAAsB,CAAA;AACpC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,kBAAkB,CAAA;AAChC,cAAc,mBAAmB,CAAA;AACjC,cAAc,gBAAgB,CAAA","sourcesContent":["// @llui/compiler — engine. Adapters consume through these re-exports.\n// Migration in progress (see docs/proposals/v2-compiler/v2a.md §4.4).\nexport * from './emit-names.js'\nexport * from './accessor-resolver.js'\nexport * from './binding-descriptors.js'\nexport * from './collect-deps.js'\nexport * from './compiler-cache.js'\nexport * from './cross-file-resolver.js'\nexport * from './cross-file-walker.js'\nexport * from './diagnostic.js'\nexport * from './manifest.js'\nexport * from './module.js'\nexport * from './version.js'\nexport * from './introspection-factory.js'\nexport { findComponentCalls } from './modules/_shared.js'\n// Introspection modules (schemaHashModule, msg-schema, msg-annotations,\n// state-schema, binding-descriptors) moved to @llui/compiler-introspection\n// in v2c/decomp-26. Adapters that previously imported these names from\n// @llui/compiler must now import from @llui/compiler-introspection.\n// BINDING_DESCRIPTORS_SLOT is re-exported from introspection-factory.js\n// (above) so the orchestrator can read the slot without depending on\n// the introspection package.\n// componentMetaModule moved to @llui/compiler-devtools (v2c/decomp-27).\nexport { maskLegendModule, type MaskLegendModuleOptions } from './modules/mask-legend.js'\nexport { compilerStampModule } from './modules/compiler-stamp.js'\nexport {\n eachMemoModule,\n EACH_MEMO_SLOT,\n type EachMemoModuleOptions,\n type EachMemoSlot,\n} from './modules/each-memo.js'\nexport {\n structuralMaskModule,\n type StructuralMaskModuleOptions,\n} from './modules/structural-mask.js'\nexport { textMaskModule, type TextMaskModuleOptions } from './modules/text-mask.js'\nexport { itemDedupModule, type ItemDedupModuleOptions } from './modules/item-dedup.js'\nexport {\n elementRewriteModule,\n ELEMENT_REWRITE_SLOT,\n type ElementRewriteModuleOptions,\n type ElementRewriteSlot,\n} from './modules/element-rewrite.js'\nexport { rowFactoryModule, type RowFactoryModuleOptions } from './modules/row-factory.js'\nexport {\n coreSynthesisModule,\n CORE_SYNTHESIS_SLOT,\n type CoreSynthesisModuleOptions,\n type CoreSynthesisSlot,\n} from './modules/core-synthesis.js'\nexport * from './msg-annotations.js'\nexport * from './msg-schema.js'\nexport * from './schema-hash.js'\nexport * from './state-schema.js'\nexport * from './transform.js'\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint-modules.d.ts","sourceRoot":"","sources":["../src/lint-modules.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"lint-modules.d.ts","sourceRoot":"","sources":["../src/lint-modules.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AA6CjD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,cAAc,EAAE,CA8CpD"}
|
package/dist/lint-modules.js
CHANGED
|
@@ -51,6 +51,8 @@ import { staticOnModule } from './modules/static-on.js';
|
|
|
51
51
|
import { noListRenderInSampleModule } from './modules/no-list-render-in-sample.js';
|
|
52
52
|
import { noSampleInAccessorModule } from './modules/no-sample-in-accessor.js';
|
|
53
53
|
import { noSampleInReactivePositionModule } from './modules/no-sample-in-reactive-position.js';
|
|
54
|
+
import { noSampleInEventHandlerModule } from './modules/no-sample-in-event-handler.js';
|
|
55
|
+
import { noRepeatedItemCurrentModule } from './modules/no-repeated-item-current.js';
|
|
54
56
|
import { agentEmitsDriftModule } from './modules/agent-emits-drift.js';
|
|
55
57
|
import { agentMsgResolvableModule } from './modules/agent-msg-resolvable.js';
|
|
56
58
|
/**
|
|
@@ -101,6 +103,8 @@ export function createLintModules() {
|
|
|
101
103
|
noListRenderInSampleModule(),
|
|
102
104
|
noSampleInAccessorModule(),
|
|
103
105
|
noSampleInReactivePositionModule(),
|
|
106
|
+
noSampleInEventHandlerModule(),
|
|
107
|
+
noRepeatedItemCurrentModule(),
|
|
104
108
|
agentEmitsDriftModule(),
|
|
105
109
|
agentMsgResolvableModule(),
|
|
106
110
|
];
|
package/dist/lint-modules.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lint-modules.js","sourceRoot":"","sources":["../src/lint-modules.ts"],"names":[],"mappings":"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","sourcesContent":["// Always-on lint modules.\n//\n// Every entry here is a zero-arg `CompilerModule` factory whose output\n// is registered unconditionally on every `transformLlui` invocation.\n// The function is the single source of truth — `transform.ts`'s active-\n// module list spreads it, and `scripts/generate-rule-docs.ts` calls it\n// to enumerate diagnostic IDs for the rule reference. Adding or removing\n// a rule in one place propagates everywhere.\n//\n// Note: this does NOT include modules with per-file options (e.g.\n// `maskLegendModule({ fieldBits, fieldBitsHi })`, `coreSynthesisModule`,\n// etc.) — those stay constructed inline in `transform.ts` because they\n// take per-file context. The `compilerStampModule` is unconditional but\n// it's an instance-not-factory; appended separately by callers.\n\nimport type { CompilerModule } from './module.js'\nimport { bitmaskOverflowModule } from './modules/bitmask-overflow.js'\nimport { asyncUpdateModule } from './modules/async-update.js'\nimport { mapOnStateArrayModule } from './modules/map-on-state-array.js'\nimport { nestedSendInUpdateModule } from './modules/nested-send-in-update.js'\nimport { directStateInViewModule } from './modules/direct-state-in-view.js'\nimport { imperativeDomInViewModule } from './modules/imperative-dom-in-view.js'\nimport { accessorSideEffectModule } from './modules/accessor-side-effect.js'\nimport { stateMutationModule } from './modules/state-mutation.js'\nimport { effectWithoutHandlerModule } from './modules/effect-without-handler.js'\nimport { exhaustiveEffectHandlingModule } from './modules/exhaustive-effect-handling.js'\nimport { noEagerItemAccessorModule } from './modules/no-eager-item-accessor.js'\nimport { pureUpdateFunctionModule } from './modules/pure-update-function.js'\nimport { exhaustiveUpdateModule } from './modules/exhaustive-update.js'\nimport { noLetReactiveAccessorModule } from './modules/no-let-reactive-accessor.js'\nimport { eachClosureViolationModule } from './modules/each-closure-violation.js'\nimport { stringEffectCallbackModule } from './modules/string-effect-callback.js'\nimport { agentMissingIntentModule } from './modules/agent-missing-intent.js'\nimport { agentWarningOnConfirmModule } from './modules/agent-warning-on-confirm.js'\nimport { agentExampleOnPayloadModule } from './modules/agent-example-on-payload.js'\nimport { agentExclusiveAnnotationsModule } from './modules/agent-exclusive-annotations.js'\nimport { agentOptionalFieldUndocumentedModule } from './modules/agent-optional-field-undocumented.js'\nimport { agentTagsendTranslatorMissingModule } from './modules/agent-tagsend-translator-missing.js'\nimport { agentNonextractableHandlerModule } from './modules/agent-nonextractable-handler.js'\nimport { subappRequiresReasonModule } from './modules/subapp-requires-reason.js'\nimport { emptyPropsModule } from './modules/empty-props.js'\nimport { forgottenSpreadModule } from './modules/forgotten-spread.js'\nimport { accessibilityModule } from './modules/accessibility.js'\nimport { viewBagImportModule } from './modules/view-bag-import.js'\nimport { controlledInputModule } from './modules/controlled-input.js'\nimport { missingMemoModule } from './modules/missing-memo.js'\nimport { namespaceImportModule } from './modules/namespace-import.js'\nimport { noBarrelImportWhenSubpathExistsModule } from './modules/no-barrel-import-when-subpath-exists.js'\nimport { formBoilerplateModule } from './modules/form-boilerplate.js'\nimport { spreadInChildrenModule } from './modules/spread-in-children.js'\nimport { staticItemsModule } from './modules/static-items.js'\nimport { staticOnModule } from './modules/static-on.js'\nimport { noListRenderInSampleModule } from './modules/no-list-render-in-sample.js'\nimport { noSampleInAccessorModule } from './modules/no-sample-in-accessor.js'\nimport { noSampleInReactivePositionModule } from './modules/no-sample-in-reactive-position.js'\nimport { agentEmitsDriftModule } from './modules/agent-emits-drift.js'\nimport { agentMsgResolvableModule } from './modules/agent-msg-resolvable.js'\n\n/**\n * Construct fresh instances of every always-on lint module.\n *\n * Returns a new array per call. Modules are stateful within a single\n * `ModuleRegistry.run()` (slot accumulators), so reusing instances\n * across files would leak state — always call this once per file.\n */\nexport function createLintModules(): CompilerModule[] {\n return [\n bitmaskOverflowModule(),\n asyncUpdateModule(),\n mapOnStateArrayModule(),\n nestedSendInUpdateModule(),\n directStateInViewModule(),\n imperativeDomInViewModule(),\n accessorSideEffectModule(),\n stateMutationModule(),\n effectWithoutHandlerModule(),\n exhaustiveEffectHandlingModule(),\n noEagerItemAccessorModule(),\n pureUpdateFunctionModule(),\n exhaustiveUpdateModule(),\n noLetReactiveAccessorModule(),\n eachClosureViolationModule(),\n stringEffectCallbackModule(),\n agentMissingIntentModule(),\n agentWarningOnConfirmModule(),\n agentExampleOnPayloadModule(),\n agentExclusiveAnnotationsModule(),\n agentOptionalFieldUndocumentedModule(),\n agentTagsendTranslatorMissingModule(),\n agentNonextractableHandlerModule(),\n subappRequiresReasonModule(),\n emptyPropsModule(),\n forgottenSpreadModule(),\n accessibilityModule(),\n viewBagImportModule(),\n controlledInputModule(),\n missingMemoModule(),\n namespaceImportModule(),\n noBarrelImportWhenSubpathExistsModule(),\n formBoilerplateModule(),\n spreadInChildrenModule(),\n staticItemsModule(),\n staticOnModule(),\n noListRenderInSampleModule(),\n noSampleInAccessorModule(),\n noSampleInReactivePositionModule(),\n agentEmitsDriftModule(),\n agentMsgResolvableModule(),\n ]\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lint-modules.js","sourceRoot":"","sources":["../src/lint-modules.ts"],"names":[],"mappings":"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","sourcesContent":["// Always-on lint modules.\n//\n// Every entry here is a zero-arg `CompilerModule` factory whose output\n// is registered unconditionally on every `transformLlui` invocation.\n// The function is the single source of truth — `transform.ts`'s active-\n// module list spreads it, and `scripts/generate-rule-docs.ts` calls it\n// to enumerate diagnostic IDs for the rule reference. Adding or removing\n// a rule in one place propagates everywhere.\n//\n// Note: this does NOT include modules with per-file options (e.g.\n// `maskLegendModule({ fieldBits, fieldBitsHi })`, `coreSynthesisModule`,\n// etc.) — those stay constructed inline in `transform.ts` because they\n// take per-file context. The `compilerStampModule` is unconditional but\n// it's an instance-not-factory; appended separately by callers.\n\nimport type { CompilerModule } from './module.js'\nimport { bitmaskOverflowModule } from './modules/bitmask-overflow.js'\nimport { asyncUpdateModule } from './modules/async-update.js'\nimport { mapOnStateArrayModule } from './modules/map-on-state-array.js'\nimport { nestedSendInUpdateModule } from './modules/nested-send-in-update.js'\nimport { directStateInViewModule } from './modules/direct-state-in-view.js'\nimport { imperativeDomInViewModule } from './modules/imperative-dom-in-view.js'\nimport { accessorSideEffectModule } from './modules/accessor-side-effect.js'\nimport { stateMutationModule } from './modules/state-mutation.js'\nimport { effectWithoutHandlerModule } from './modules/effect-without-handler.js'\nimport { exhaustiveEffectHandlingModule } from './modules/exhaustive-effect-handling.js'\nimport { noEagerItemAccessorModule } from './modules/no-eager-item-accessor.js'\nimport { pureUpdateFunctionModule } from './modules/pure-update-function.js'\nimport { exhaustiveUpdateModule } from './modules/exhaustive-update.js'\nimport { noLetReactiveAccessorModule } from './modules/no-let-reactive-accessor.js'\nimport { eachClosureViolationModule } from './modules/each-closure-violation.js'\nimport { stringEffectCallbackModule } from './modules/string-effect-callback.js'\nimport { agentMissingIntentModule } from './modules/agent-missing-intent.js'\nimport { agentWarningOnConfirmModule } from './modules/agent-warning-on-confirm.js'\nimport { agentExampleOnPayloadModule } from './modules/agent-example-on-payload.js'\nimport { agentExclusiveAnnotationsModule } from './modules/agent-exclusive-annotations.js'\nimport { agentOptionalFieldUndocumentedModule } from './modules/agent-optional-field-undocumented.js'\nimport { agentTagsendTranslatorMissingModule } from './modules/agent-tagsend-translator-missing.js'\nimport { agentNonextractableHandlerModule } from './modules/agent-nonextractable-handler.js'\nimport { subappRequiresReasonModule } from './modules/subapp-requires-reason.js'\nimport { emptyPropsModule } from './modules/empty-props.js'\nimport { forgottenSpreadModule } from './modules/forgotten-spread.js'\nimport { accessibilityModule } from './modules/accessibility.js'\nimport { viewBagImportModule } from './modules/view-bag-import.js'\nimport { controlledInputModule } from './modules/controlled-input.js'\nimport { missingMemoModule } from './modules/missing-memo.js'\nimport { namespaceImportModule } from './modules/namespace-import.js'\nimport { noBarrelImportWhenSubpathExistsModule } from './modules/no-barrel-import-when-subpath-exists.js'\nimport { formBoilerplateModule } from './modules/form-boilerplate.js'\nimport { spreadInChildrenModule } from './modules/spread-in-children.js'\nimport { staticItemsModule } from './modules/static-items.js'\nimport { staticOnModule } from './modules/static-on.js'\nimport { noListRenderInSampleModule } from './modules/no-list-render-in-sample.js'\nimport { noSampleInAccessorModule } from './modules/no-sample-in-accessor.js'\nimport { noSampleInReactivePositionModule } from './modules/no-sample-in-reactive-position.js'\nimport { noSampleInEventHandlerModule } from './modules/no-sample-in-event-handler.js'\nimport { noRepeatedItemCurrentModule } from './modules/no-repeated-item-current.js'\nimport { agentEmitsDriftModule } from './modules/agent-emits-drift.js'\nimport { agentMsgResolvableModule } from './modules/agent-msg-resolvable.js'\n\n/**\n * Construct fresh instances of every always-on lint module.\n *\n * Returns a new array per call. Modules are stateful within a single\n * `ModuleRegistry.run()` (slot accumulators), so reusing instances\n * across files would leak state — always call this once per file.\n */\nexport function createLintModules(): CompilerModule[] {\n return [\n bitmaskOverflowModule(),\n asyncUpdateModule(),\n mapOnStateArrayModule(),\n nestedSendInUpdateModule(),\n directStateInViewModule(),\n imperativeDomInViewModule(),\n accessorSideEffectModule(),\n stateMutationModule(),\n effectWithoutHandlerModule(),\n exhaustiveEffectHandlingModule(),\n noEagerItemAccessorModule(),\n pureUpdateFunctionModule(),\n exhaustiveUpdateModule(),\n noLetReactiveAccessorModule(),\n eachClosureViolationModule(),\n stringEffectCallbackModule(),\n agentMissingIntentModule(),\n agentWarningOnConfirmModule(),\n agentExampleOnPayloadModule(),\n agentExclusiveAnnotationsModule(),\n agentOptionalFieldUndocumentedModule(),\n agentTagsendTranslatorMissingModule(),\n agentNonextractableHandlerModule(),\n subappRequiresReasonModule(),\n emptyPropsModule(),\n forgottenSpreadModule(),\n accessibilityModule(),\n viewBagImportModule(),\n controlledInputModule(),\n missingMemoModule(),\n namespaceImportModule(),\n noBarrelImportWhenSubpathExistsModule(),\n formBoilerplateModule(),\n spreadInChildrenModule(),\n staticItemsModule(),\n staticOnModule(),\n noListRenderInSampleModule(),\n noSampleInAccessorModule(),\n noSampleInReactivePositionModule(),\n noSampleInEventHandlerModule(),\n noRepeatedItemCurrentModule(),\n agentEmitsDriftModule(),\n agentMsgResolvableModule(),\n ]\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-repeated-item-current.d.ts","sourceRoot":"","sources":["../../src/modules/no-repeated-item-current.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAoGlD,wBAAgB,2BAA2B,IAAI,cAAc,CA4C5D"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// `no-repeated-item-current` — warns when an `each.render` callback's
|
|
2
|
+
// accessor body calls `item.current()` more than once with a property
|
|
3
|
+
// chain after each call (e.g. `item.current().facts[K]` repeated
|
|
4
|
+
// inside the same text/show.when accessor).
|
|
5
|
+
//
|
|
6
|
+
// Two reasons the pattern is dangerous:
|
|
7
|
+
//
|
|
8
|
+
// 1. **Bitmask trap.** `item.current().X` hides the read from the
|
|
9
|
+
// compiler's static analyzer — the accessor falls back to
|
|
10
|
+
// FULL_MASK and fires on every state change instead of only when
|
|
11
|
+
// `X` changes.
|
|
12
|
+
// 2. **Reconcile-race undefined.** Repeated `.current()` calls
|
|
13
|
+
// across a single accessor body can observe intermediate state
|
|
14
|
+
// during a structural transition. The chained property access
|
|
15
|
+
// then throws `Cannot read properties of undefined (reading
|
|
16
|
+
// 'X')`. The dungeonlogs 2026-05-20 report named exactly this
|
|
17
|
+
// class of bug.
|
|
18
|
+
//
|
|
19
|
+
// The fix is one of:
|
|
20
|
+
// - destructure once: `const e = item.current(); use e.X, e.Y`
|
|
21
|
+
// - project to a row type in `items` so each cell becomes a simple
|
|
22
|
+
// field read (`item.X` shorthand)
|
|
23
|
+
//
|
|
24
|
+
// Severity: warn. A single `item.current()` call is fine (sometimes
|
|
25
|
+
// necessary, e.g. guarding for primitive T's `current` accessor); the
|
|
26
|
+
// warning fires only when the same accessor body calls it 2+ times.
|
|
27
|
+
import ts from 'typescript';
|
|
28
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
29
|
+
/** True when `node` is `item.current()` — bare `item` identifier root. */
|
|
30
|
+
function isItemCurrentCall(node) {
|
|
31
|
+
if (!ts.isCallExpression(node))
|
|
32
|
+
return false;
|
|
33
|
+
if (node.arguments.length !== 0)
|
|
34
|
+
return false;
|
|
35
|
+
if (!ts.isPropertyAccessExpression(node.expression))
|
|
36
|
+
return false;
|
|
37
|
+
const obj = node.expression.expression;
|
|
38
|
+
if (!ts.isIdentifier(obj) || obj.text !== 'item')
|
|
39
|
+
return false;
|
|
40
|
+
const name = node.expression.name;
|
|
41
|
+
return ts.isIdentifier(name) && name.text === 'current';
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Find direct-children `item.current()` calls of `body` that are
|
|
45
|
+
* followed by a property access (e.g. `item.current().X`). Skip nested
|
|
46
|
+
* function bodies — a `.current()` inside an inner arrow runs in a
|
|
47
|
+
* different scope.
|
|
48
|
+
*/
|
|
49
|
+
function findChainedItemCurrents(body) {
|
|
50
|
+
const out = [];
|
|
51
|
+
const walk = (n) => {
|
|
52
|
+
if (isItemCurrentCall(n)) {
|
|
53
|
+
// We only flag chained access — `item.current()` alone is fine
|
|
54
|
+
// (used as a return value, passed to a helper, etc.).
|
|
55
|
+
const parent = n.parent;
|
|
56
|
+
if (parent && ts.isPropertyAccessExpression(parent) && parent.expression === n) {
|
|
57
|
+
out.push(n);
|
|
58
|
+
}
|
|
59
|
+
else if (parent && ts.isElementAccessExpression(parent) && parent.expression === n) {
|
|
60
|
+
out.push(n);
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if ((ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n)) &&
|
|
65
|
+
n !== body) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
ts.forEachChild(n, walk);
|
|
69
|
+
};
|
|
70
|
+
walk(body);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Walk a function body and report when any inner accessor (arrow arg
|
|
75
|
+
* to `text` / `unsafeHtml` / `h.show({ when })` / similar) calls
|
|
76
|
+
* `item.current().X` more than once.
|
|
77
|
+
*/
|
|
78
|
+
function checkRenderBody(body, sf, report) {
|
|
79
|
+
const visit = (n) => {
|
|
80
|
+
if (ts.isArrowFunction(n) || ts.isFunctionExpression(n)) {
|
|
81
|
+
const calls = findChainedItemCurrents(n.body);
|
|
82
|
+
if (calls.length >= 2) {
|
|
83
|
+
// Report the SECOND call — the first one is fine on its own;
|
|
84
|
+
// the warning is "you're doing this repeatedly."
|
|
85
|
+
report(calls[1]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
ts.forEachChild(n, visit);
|
|
89
|
+
};
|
|
90
|
+
visit(body);
|
|
91
|
+
void sf;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Detect an `each(...)` or `h.each(...)` call. Returns the render
|
|
95
|
+
* function's body if present.
|
|
96
|
+
*/
|
|
97
|
+
function eachRenderBody(call) {
|
|
98
|
+
let isEach = false;
|
|
99
|
+
if (ts.isIdentifier(call.expression) && call.expression.text === 'each')
|
|
100
|
+
isEach = true;
|
|
101
|
+
else if (ts.isPropertyAccessExpression(call.expression) &&
|
|
102
|
+
ts.isIdentifier(call.expression.name) &&
|
|
103
|
+
call.expression.name.text === 'each') {
|
|
104
|
+
isEach = true;
|
|
105
|
+
}
|
|
106
|
+
if (!isEach)
|
|
107
|
+
return undefined;
|
|
108
|
+
const arg = call.arguments[0];
|
|
109
|
+
if (!arg || !ts.isObjectLiteralExpression(arg))
|
|
110
|
+
return undefined;
|
|
111
|
+
for (const prop of arg.properties) {
|
|
112
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
113
|
+
ts.isIdentifier(prop.name) &&
|
|
114
|
+
prop.name.text === 'render' &&
|
|
115
|
+
(ts.isArrowFunction(prop.initializer) || ts.isFunctionExpression(prop.initializer))) {
|
|
116
|
+
return prop.initializer.body;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
export function noRepeatedItemCurrentModule() {
|
|
122
|
+
return {
|
|
123
|
+
name: 'no-repeated-item-current',
|
|
124
|
+
compilerVersion: '^0.3.0',
|
|
125
|
+
diagnostics: [
|
|
126
|
+
{
|
|
127
|
+
id: 'llui/no-repeated-item-current',
|
|
128
|
+
description: 'Repeated `item.current().X` calls inside the same accessor — bitmask falls back to FULL_MASK and chained access can throw during reconcile races. Destructure once or project to a row type.',
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
visitors: {
|
|
132
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
133
|
+
const visited = node;
|
|
134
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
135
|
+
const walk = (n) => {
|
|
136
|
+
if (ts.isCallExpression(n)) {
|
|
137
|
+
const body = eachRenderBody(n);
|
|
138
|
+
if (body) {
|
|
139
|
+
checkRenderBody(body, sf, (offender) => {
|
|
140
|
+
ctx.reportDiagnostic({
|
|
141
|
+
id: 'llui/no-repeated-item-current',
|
|
142
|
+
severity: 'warning',
|
|
143
|
+
category: 'reactivity',
|
|
144
|
+
message: `Repeated \`item.current()\` calls inside an each.render accessor. The compiler can't trace through ` +
|
|
145
|
+
`\`item.current().X\` so the binding falls back to FULL_MASK (fires on every state change), and ` +
|
|
146
|
+
`chained access can throw \`Cannot read properties of undefined\` during structural reconciles. ` +
|
|
147
|
+
`Either destructure once at the top — \`const e = item.current(); /* use e.X, e.Y */\` — or project ` +
|
|
148
|
+
`to a row type in \`items\` so each cell becomes a simple field read (\`item.X\` shorthand).`,
|
|
149
|
+
location: {
|
|
150
|
+
file: sf.fileName,
|
|
151
|
+
range: rangeFromOffsets(sf.text, offender.getStart(sf), offender.getEnd()),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
ts.forEachChild(n, walk);
|
|
158
|
+
};
|
|
159
|
+
walk(sf);
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=no-repeated-item-current.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-repeated-item-current.js","sourceRoot":"","sources":["../../src/modules/no-repeated-item-current.ts"],"names":[],"mappings":"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","sourcesContent":["// `no-repeated-item-current` — warns when an `each.render` callback's\n// accessor body calls `item.current()` more than once with a property\n// chain after each call (e.g. `item.current().facts[K]` repeated\n// inside the same text/show.when accessor).\n//\n// Two reasons the pattern is dangerous:\n//\n// 1. **Bitmask trap.** `item.current().X` hides the read from the\n// compiler's static analyzer — the accessor falls back to\n// FULL_MASK and fires on every state change instead of only when\n// `X` changes.\n// 2. **Reconcile-race undefined.** Repeated `.current()` calls\n// across a single accessor body can observe intermediate state\n// during a structural transition. The chained property access\n// then throws `Cannot read properties of undefined (reading\n// 'X')`. The dungeonlogs 2026-05-20 report named exactly this\n// class of bug.\n//\n// The fix is one of:\n// - destructure once: `const e = item.current(); use e.X, e.Y`\n// - project to a row type in `items` so each cell becomes a simple\n// field read (`item.X` shorthand)\n//\n// Severity: warn. A single `item.current()` call is fine (sometimes\n// necessary, e.g. guarding for primitive T's `current` accessor); the\n// warning fires only when the same accessor body calls it 2+ times.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\n/** True when `node` is `item.current()` — bare `item` identifier root. */\nfunction isItemCurrentCall(node: ts.Node): node is ts.CallExpression {\n if (!ts.isCallExpression(node)) return false\n if (node.arguments.length !== 0) return false\n if (!ts.isPropertyAccessExpression(node.expression)) return false\n const obj = node.expression.expression\n if (!ts.isIdentifier(obj) || obj.text !== 'item') return false\n const name = node.expression.name\n return ts.isIdentifier(name) && name.text === 'current'\n}\n\n/**\n * Find direct-children `item.current()` calls of `body` that are\n * followed by a property access (e.g. `item.current().X`). Skip nested\n * function bodies — a `.current()` inside an inner arrow runs in a\n * different scope.\n */\nfunction findChainedItemCurrents(body: ts.Node): ts.CallExpression[] {\n const out: ts.CallExpression[] = []\n const walk = (n: ts.Node): void => {\n if (isItemCurrentCall(n)) {\n // We only flag chained access — `item.current()` alone is fine\n // (used as a return value, passed to a helper, etc.).\n const parent = n.parent\n if (parent && ts.isPropertyAccessExpression(parent) && parent.expression === n) {\n out.push(n)\n } else if (parent && ts.isElementAccessExpression(parent) && parent.expression === n) {\n out.push(n)\n }\n return\n }\n if (\n (ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n)) &&\n n !== body\n ) {\n return\n }\n ts.forEachChild(n, walk)\n }\n walk(body)\n return out\n}\n\n/**\n * Walk a function body and report when any inner accessor (arrow arg\n * to `text` / `unsafeHtml` / `h.show({ when })` / similar) calls\n * `item.current().X` more than once.\n */\nfunction checkRenderBody(\n body: ts.Node,\n sf: ts.SourceFile,\n report: (offender: ts.CallExpression) => void,\n): void {\n const visit = (n: ts.Node): void => {\n if (ts.isArrowFunction(n) || ts.isFunctionExpression(n)) {\n const calls = findChainedItemCurrents(n.body)\n if (calls.length >= 2) {\n // Report the SECOND call — the first one is fine on its own;\n // the warning is \"you're doing this repeatedly.\"\n report(calls[1]!)\n }\n }\n ts.forEachChild(n, visit)\n }\n visit(body)\n void sf\n}\n\n/**\n * Detect an `each(...)` or `h.each(...)` call. Returns the render\n * function's body if present.\n */\nfunction eachRenderBody(call: ts.CallExpression): ts.Node | undefined {\n let isEach = false\n if (ts.isIdentifier(call.expression) && call.expression.text === 'each') isEach = true\n else if (\n ts.isPropertyAccessExpression(call.expression) &&\n ts.isIdentifier(call.expression.name) &&\n call.expression.name.text === 'each'\n ) {\n isEach = true\n }\n if (!isEach) return 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 === 'render' &&\n (ts.isArrowFunction(prop.initializer) || ts.isFunctionExpression(prop.initializer))\n ) {\n return prop.initializer.body\n }\n }\n return undefined\n}\n\nexport function noRepeatedItemCurrentModule(): CompilerModule {\n return {\n name: 'no-repeated-item-current',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/no-repeated-item-current',\n description:\n 'Repeated `item.current().X` calls inside the same accessor — bitmask falls back to FULL_MASK and chained access can throw during reconcile races. Destructure once or project to a row type.',\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 (ts.isCallExpression(n)) {\n const body = eachRenderBody(n)\n if (body) {\n checkRenderBody(body, sf, (offender) => {\n ctx.reportDiagnostic({\n id: 'llui/no-repeated-item-current',\n severity: 'warning',\n category: 'reactivity',\n message:\n `Repeated \\`item.current()\\` calls inside an each.render accessor. The compiler can't trace through ` +\n `\\`item.current().X\\` so the binding falls back to FULL_MASK (fires on every state change), and ` +\n `chained access can throw \\`Cannot read properties of undefined\\` during structural reconciles. ` +\n `Either destructure once at the top — \\`const e = item.current(); /* use e.X, e.Y */\\` — or project ` +\n `to a row type in \\`items\\` so each cell becomes a simple field read (\\`item.X\\` shorthand).`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, offender.getStart(sf), offender.getEnd()),\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":"no-sample-in-event-handler.d.ts","sourceRoot":"","sources":["../../src/modules/no-sample-in-event-handler.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AA0ClD,wBAAgB,4BAA4B,IAAI,cAAc,CAgD7D"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// `no-sample-in-event-handler` — errors when `sample()` / `h.sample()`
|
|
2
|
+
// appears inside an event-handler property (`onClick`, `onInput`,
|
|
3
|
+
// `onSubmit`, …). Event handlers run AFTER mount, with no active
|
|
4
|
+
// render context, so the runtime `sample()` throws `[LLui] sample()
|
|
5
|
+
// can only be called inside a component's view() function`.
|
|
6
|
+
//
|
|
7
|
+
// Catching at compile time turns "open the dev console, click the
|
|
8
|
+
// button, see the error" into a build failure on the offending file.
|
|
9
|
+
// Aligned with the framework's "compile-time errors not lint warnings"
|
|
10
|
+
// philosophy.
|
|
11
|
+
//
|
|
12
|
+
// The right pattern is to capture at render time:
|
|
13
|
+
// const id = h.sample((s) => s.id)
|
|
14
|
+
// button({ onClick: () => send({ type: 'select', id }) })
|
|
15
|
+
// or to use the mount handle: `handle.getState()` inside the handler.
|
|
16
|
+
import ts from 'typescript';
|
|
17
|
+
import { rangeFromOffsets } from '../diagnostic.js';
|
|
18
|
+
const EVENT_HANDLER_KEY_RE = /^on[A-Z]/;
|
|
19
|
+
function isSampleCall(n) {
|
|
20
|
+
if (!ts.isCallExpression(n))
|
|
21
|
+
return false;
|
|
22
|
+
if (ts.isIdentifier(n.expression) && n.expression.text === 'sample')
|
|
23
|
+
return true;
|
|
24
|
+
if (ts.isPropertyAccessExpression(n.expression) &&
|
|
25
|
+
ts.isIdentifier(n.expression.name) &&
|
|
26
|
+
n.expression.name.text === 'sample') {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
function findFirstSampleInside(body) {
|
|
32
|
+
let found;
|
|
33
|
+
const walk = (n) => {
|
|
34
|
+
if (found)
|
|
35
|
+
return;
|
|
36
|
+
if (isSampleCall(n)) {
|
|
37
|
+
found = n;
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// DON'T descend into nested functions — sample() inside an inner
|
|
41
|
+
// function (e.g. a setTimeout body or another arrow that captures
|
|
42
|
+
// the event handler's closure) runs at a different time and has
|
|
43
|
+
// its own context check at runtime. Only the directly-synchronous
|
|
44
|
+
// sample call in the handler body is the trap this rule targets.
|
|
45
|
+
if ((ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n)) &&
|
|
46
|
+
n !== body) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
ts.forEachChild(n, walk);
|
|
50
|
+
};
|
|
51
|
+
walk(body);
|
|
52
|
+
return found;
|
|
53
|
+
}
|
|
54
|
+
export function noSampleInEventHandlerModule() {
|
|
55
|
+
return {
|
|
56
|
+
name: 'no-sample-in-event-handler',
|
|
57
|
+
compilerVersion: '^0.3.0',
|
|
58
|
+
diagnostics: [
|
|
59
|
+
{
|
|
60
|
+
id: 'llui/no-sample-in-event-handler',
|
|
61
|
+
description: '`sample()` inside an event handler — handlers run with no render context; throws at runtime. Capture at render time instead.',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
visitors: {
|
|
65
|
+
[ts.SyntaxKind.SourceFile]: (ctx, node) => {
|
|
66
|
+
const visited = node;
|
|
67
|
+
const sf = ts.createSourceFile(visited.fileName, visited.text, ts.ScriptTarget.Latest, true);
|
|
68
|
+
const walk = (n) => {
|
|
69
|
+
if (ts.isPropertyAssignment(n)) {
|
|
70
|
+
const key = n.name;
|
|
71
|
+
if (ts.isIdentifier(key) && EVENT_HANDLER_KEY_RE.test(key.text)) {
|
|
72
|
+
const value = n.initializer;
|
|
73
|
+
if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
|
|
74
|
+
const offender = findFirstSampleInside(value.body);
|
|
75
|
+
if (offender) {
|
|
76
|
+
ctx.reportDiagnostic({
|
|
77
|
+
id: 'llui/no-sample-in-event-handler',
|
|
78
|
+
severity: 'error',
|
|
79
|
+
category: 'reactivity',
|
|
80
|
+
message: `\`sample()\` is being called inside the \`${key.text}\` handler. Handlers run AFTER mount with no active render context, so this throws at runtime ` +
|
|
81
|
+
`(\`[LLui] sample() can only be called inside a component's view() function\`). Capture the value at render time instead: ` +
|
|
82
|
+
`\`const id = h.sample(s => s.id); button({ ${key.text}: () => send({ ..., id }) })\`. ` +
|
|
83
|
+
`If you need the LATEST state at click time (rare), use the mount handle: ` +
|
|
84
|
+
`\`handle.getState()\` inside the handler.`,
|
|
85
|
+
location: {
|
|
86
|
+
file: sf.fileName,
|
|
87
|
+
range: rangeFromOffsets(sf.text, offender.getStart(sf), offender.getEnd()),
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
ts.forEachChild(n, walk);
|
|
95
|
+
};
|
|
96
|
+
walk(sf);
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=no-sample-in-event-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-sample-in-event-handler.js","sourceRoot":"","sources":["../../src/modules/no-sample-in-event-handler.ts"],"names":[],"mappings":"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","sourcesContent":["// `no-sample-in-event-handler` — errors when `sample()` / `h.sample()`\n// appears inside an event-handler property (`onClick`, `onInput`,\n// `onSubmit`, …). Event handlers run AFTER mount, with no active\n// render context, so the runtime `sample()` throws `[LLui] sample()\n// can only be called inside a component's view() function`.\n//\n// Catching at compile time turns \"open the dev console, click the\n// button, see the error\" into a build failure on the offending file.\n// Aligned with the framework's \"compile-time errors not lint warnings\"\n// philosophy.\n//\n// The right pattern is to capture at render time:\n// const id = h.sample((s) => s.id)\n// button({ onClick: () => send({ type: 'select', id }) })\n// or to use the mount handle: `handle.getState()` inside the handler.\n\nimport ts from 'typescript'\nimport { rangeFromOffsets } from '../diagnostic.js'\nimport type { CompilerModule } from '../module.js'\n\nconst EVENT_HANDLER_KEY_RE = /^on[A-Z]/\n\nfunction isSampleCall(n: ts.Node): boolean {\n if (!ts.isCallExpression(n)) return false\n if (ts.isIdentifier(n.expression) && n.expression.text === 'sample') return true\n if (\n ts.isPropertyAccessExpression(n.expression) &&\n ts.isIdentifier(n.expression.name) &&\n n.expression.name.text === 'sample'\n ) {\n return true\n }\n return false\n}\n\nfunction findFirstSampleInside(body: ts.Node): ts.CallExpression | undefined {\n let found: ts.CallExpression | undefined\n const walk = (n: ts.Node): void => {\n if (found) return\n if (isSampleCall(n)) {\n found = n as ts.CallExpression\n return\n }\n // DON'T descend into nested functions — sample() inside an inner\n // function (e.g. a setTimeout body or another arrow that captures\n // the event handler's closure) runs at a different time and has\n // its own context check at runtime. Only the directly-synchronous\n // sample call in the handler body is the trap this rule targets.\n if (\n (ts.isArrowFunction(n) || ts.isFunctionExpression(n) || ts.isFunctionDeclaration(n)) &&\n n !== body\n ) {\n return\n }\n ts.forEachChild(n, walk)\n }\n walk(body)\n return found\n}\n\nexport function noSampleInEventHandlerModule(): CompilerModule {\n return {\n name: 'no-sample-in-event-handler',\n compilerVersion: '^0.3.0',\n diagnostics: [\n {\n id: 'llui/no-sample-in-event-handler',\n description:\n '`sample()` inside an event handler — handlers run with no render context; throws at runtime. Capture at render time 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 const walk = (n: ts.Node): void => {\n if (ts.isPropertyAssignment(n)) {\n const key = n.name\n if (ts.isIdentifier(key) && EVENT_HANDLER_KEY_RE.test(key.text)) {\n const value = n.initializer\n if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {\n const offender = findFirstSampleInside(value.body)\n if (offender) {\n ctx.reportDiagnostic({\n id: 'llui/no-sample-in-event-handler',\n severity: 'error',\n category: 'reactivity',\n message:\n `\\`sample()\\` is being called inside the \\`${key.text}\\` handler. Handlers run AFTER mount with no active render context, so this throws at runtime ` +\n `(\\`[LLui] sample() can only be called inside a component's view() function\\`). Capture the value at render time instead: ` +\n `\\`const id = h.sample(s => s.id); button({ ${key.text}: () => send({ ..., id }) })\\`. ` +\n `If you need the LATEST state at click time (rare), use the mount handle: ` +\n `\\`handle.getState()\\` inside the handler.`,\n location: {\n file: sf.fileName,\n range: rangeFromOffsets(sf.text, offender.getStart(sf), offender.getEnd()),\n },\n })\n }\n }\n }\n }\n ts.forEachChild(n, walk)\n }\n walk(sf)\n },\n },\n }\n}\n"]}
|
package/dist/transform.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAG3B,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAOtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAiCjD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,UAAU,CAQhF;AAwED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IAC5C,GAAG,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1C,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9C;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAA;IAC/C,cAAc,CAAC,EAAE,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAA;IACzD,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAA;IACnD,YAAY,CAAC,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA;CACtD;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,OAAO,UAAQ,EACf,iBAAiB,UAAQ,EACzB,OAAO,GAAE,MAAM,GAAG,IAAW,EAC7B,OAAO,UAAQ,EACf,WAAW,CAAC,EAAE,mBAAmB,EACjC,YAAY,CAAC,EAAE,mBAAmB,EAClC,cAAc,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GACnC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../src/transform.ts"],"names":[],"mappings":"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"}
|