@tsrx/core 0.1.6 → 0.1.7
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/package.json +24 -9
- package/src/index.js +1 -2
- package/src/runtime/events.js +10 -0
- package/src/runtime/hash.js +1 -0
- package/src/runtime/html.js +3 -0
- package/src/transform/jsx/index.js +29 -47
- package/src/transform/lazy.js +289 -152
- package/src/utils/ast.js +13 -0
- package/src/utils/dom.js +158 -0
- package/src/utils.js +6 -159
- package/types/index.d.ts +1 -0
- package/types/jsx-platform.d.ts +4 -2
- package/types/runtime/events.d.ts +11 -0
- package/types/runtime/hash.d.ts +2 -0
- package/types/runtime/html.d.ts +4 -0
- package/types/runtime/language-helpers.d.ts +17 -0
- /package/src/runtime/{index.js → iterable.js} +0 -0
- /package/types/runtime/{index.d.ts → iterable.d.ts} +0 -0
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core compiler infrastructure for TSRX syntax",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Dominic Gannaway",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.7",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -31,11 +31,26 @@
|
|
|
31
31
|
"types": "./types/runtime/ref.d.ts",
|
|
32
32
|
"default": "./src/runtime/ref.js"
|
|
33
33
|
},
|
|
34
|
-
"./runtime": {
|
|
35
|
-
"types": "./types/runtime/
|
|
36
|
-
"default": "./src/runtime/
|
|
34
|
+
"./runtime/events": {
|
|
35
|
+
"types": "./types/runtime/events.d.ts",
|
|
36
|
+
"default": "./src/runtime/events.js"
|
|
37
|
+
},
|
|
38
|
+
"./runtime/hash": {
|
|
39
|
+
"types": "./types/runtime/hash.d.ts",
|
|
40
|
+
"default": "./src/runtime/hash.js"
|
|
41
|
+
},
|
|
42
|
+
"./runtime/html": {
|
|
43
|
+
"types": "./types/runtime/html.d.ts",
|
|
44
|
+
"default": "./src/runtime/html.js"
|
|
45
|
+
},
|
|
46
|
+
"./runtime/language-helpers": {
|
|
47
|
+
"types": "./types/runtime/language-helpers.d.ts",
|
|
48
|
+
"default": "./src/runtime/language-helpers.js"
|
|
49
|
+
},
|
|
50
|
+
"./runtime/iterable": {
|
|
51
|
+
"types": "./types/runtime/iterable.d.ts",
|
|
52
|
+
"default": "./src/runtime/iterable.js"
|
|
37
53
|
},
|
|
38
|
-
"./runtime/*": "./src/runtime/*.js",
|
|
39
54
|
"./test-harness/source-mappings": "./tests/shared/source-mappings.js",
|
|
40
55
|
"./test-harness/compile": "./tests/shared/compile.js",
|
|
41
56
|
"./test-harness/runtime/*": "./tests/shared/runtime/*.js",
|
|
@@ -67,10 +82,10 @@
|
|
|
67
82
|
"vscode-languageserver-types": "^3.17.5",
|
|
68
83
|
"vue": "3.6.0-beta.10",
|
|
69
84
|
"vue-jsx-vapor": "^3.2.12",
|
|
70
|
-
"@tsrx/preact": "0.1.
|
|
71
|
-
"@tsrx/react": "0.2.
|
|
72
|
-
"@tsrx/solid": "0.1.
|
|
73
|
-
"@tsrx/vue": "0.1.
|
|
85
|
+
"@tsrx/preact": "0.1.7",
|
|
86
|
+
"@tsrx/react": "0.2.7",
|
|
87
|
+
"@tsrx/solid": "0.1.7",
|
|
88
|
+
"@tsrx/vue": "0.1.7"
|
|
74
89
|
},
|
|
75
90
|
"files": [
|
|
76
91
|
"src",
|
package/src/index.js
CHANGED
|
@@ -88,6 +88,7 @@ export {
|
|
|
88
88
|
is_class_node as isClassNode,
|
|
89
89
|
is_component_node as isComponentNode,
|
|
90
90
|
is_function_node as isFunctionNode,
|
|
91
|
+
is_function_or_component_node as isFunctionOrComponentNode,
|
|
91
92
|
is_inside_component as isInsideComponent,
|
|
92
93
|
} from './utils/ast.js';
|
|
93
94
|
|
|
@@ -205,11 +206,9 @@ export {
|
|
|
205
206
|
export {
|
|
206
207
|
create_lazy_context as createLazyContext,
|
|
207
208
|
collect_lazy_bindings as collectLazyBindings,
|
|
208
|
-
collect_lazy_bindings_from_component as collectLazyBindingsFromComponent,
|
|
209
209
|
collect_lazy_bindings_from_statements as collectLazyBindingsFromStatements,
|
|
210
210
|
preallocate_lazy_ids as preallocateLazyIds,
|
|
211
211
|
apply_lazy_transforms as applyLazyTransforms,
|
|
212
|
-
replace_lazy_params as replaceLazyParams,
|
|
213
212
|
} from './transform/lazy.js';
|
|
214
213
|
export {
|
|
215
214
|
find_first_top_level_await as findFirstTopLevelAwait,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { simple_hash, strong_hash } from '../utils/hashing.js';
|
|
@@ -34,12 +34,7 @@ import {
|
|
|
34
34
|
jsx_id as build_jsx_id,
|
|
35
35
|
} from '../../utils/builders.js';
|
|
36
36
|
import * as b from '../../utils/builders.js';
|
|
37
|
-
import {
|
|
38
|
-
apply_lazy_transforms,
|
|
39
|
-
collect_lazy_bindings_from_component,
|
|
40
|
-
preallocate_lazy_ids,
|
|
41
|
-
replace_lazy_params,
|
|
42
|
-
} from '../lazy.js';
|
|
37
|
+
import { apply_lazy_transforms, preallocate_lazy_ids } from '../lazy.js';
|
|
43
38
|
import { find_first_top_level_await_in_component_body } from '../await.js';
|
|
44
39
|
import { prepare_stylesheet_for_render, annotate_component_with_hash } from '../scoping.js';
|
|
45
40
|
import {
|
|
@@ -50,7 +45,7 @@ import {
|
|
|
50
45
|
validate_component_return_statement,
|
|
51
46
|
validate_component_unsupported_loop_statement,
|
|
52
47
|
} from '../../analyze/validation.js';
|
|
53
|
-
import { get_component_from_path } from '../../utils/ast.js';
|
|
48
|
+
import { get_component_from_path, is_function_or_component_node } from '../../utils/ast.js';
|
|
54
49
|
import {
|
|
55
50
|
is_interleaved_body as is_interleaved_body_core,
|
|
56
51
|
is_capturable_jsx_child,
|
|
@@ -646,15 +641,6 @@ export function component_to_function_declaration(component, transform_context,
|
|
|
646
641
|
// Collect param bindings from original patterns (lazy patterns still intact).
|
|
647
642
|
const param_bindings = collect_param_bindings(params);
|
|
648
643
|
|
|
649
|
-
// Collect lazy binding info WITHOUT mutating patterns. Stores lazy_id on metadata
|
|
650
|
-
// for later replacement. Body bindings (count, setCount, etc.) are still in the
|
|
651
|
-
// original patterns, so collect_statement_bindings during build will find them.
|
|
652
|
-
// In type-only mode the lazy rewrite is skipped entirely so destructuring
|
|
653
|
-
// patterns survive into the virtual TSX and TypeScript can flow real types.
|
|
654
|
-
const lazy_bindings = transform_context.typeOnly
|
|
655
|
-
? new Map()
|
|
656
|
-
: collect_lazy_bindings_from_component(params, body, transform_context);
|
|
657
|
-
|
|
658
644
|
// Save and set context for this component scope
|
|
659
645
|
const saved_helper_state = transform_context.helper_state;
|
|
660
646
|
const saved_bindings = transform_context.available_bindings;
|
|
@@ -662,16 +648,7 @@ export function component_to_function_declaration(component, transform_context,
|
|
|
662
648
|
transform_context.available_bindings = new Map(param_bindings);
|
|
663
649
|
|
|
664
650
|
const body_statements = build_component_statements(body, transform_context);
|
|
665
|
-
|
|
666
|
-
// Replace lazy param patterns with generated identifiers
|
|
667
|
-
const final_params = lazy_bindings.size > 0 ? replace_lazy_params(params) : params;
|
|
668
|
-
|
|
669
|
-
// Wrap body_statements in a BlockStatement so that apply_lazy_transforms
|
|
670
|
-
// runs collect_block_shadowed_names and detects body-level declarations
|
|
671
|
-
// (e.g. `const name = ...`) that shadow lazy binding names.
|
|
672
651
|
const body_block = b.block(body_statements);
|
|
673
|
-
const final_body =
|
|
674
|
-
lazy_bindings.size > 0 ? apply_lazy_transforms(body_block, lazy_bindings) : body_block;
|
|
675
652
|
|
|
676
653
|
/** @type {AST.FunctionDeclaration | AST.FunctionExpression | AST.ArrowFunctionExpression} */
|
|
677
654
|
let fn;
|
|
@@ -679,18 +656,38 @@ export function component_to_function_declaration(component, transform_context,
|
|
|
679
656
|
if (component.id) {
|
|
680
657
|
fn = b.function_declaration(
|
|
681
658
|
component.id,
|
|
682
|
-
|
|
683
|
-
|
|
659
|
+
params,
|
|
660
|
+
body_block,
|
|
684
661
|
is_async_component,
|
|
685
662
|
component.typeParameters,
|
|
686
663
|
);
|
|
687
664
|
} else if (component.metadata?.arrow) {
|
|
688
|
-
fn = b.arrow(
|
|
665
|
+
fn = b.arrow(params, body_block, is_async_component, component.typeParameters);
|
|
689
666
|
} else {
|
|
690
|
-
fn = b.function(null,
|
|
667
|
+
fn = b.function(null, params, body_block, is_async_component, component.typeParameters);
|
|
691
668
|
}
|
|
692
669
|
/** @type {any} */ (fn.metadata).is_component = true;
|
|
693
670
|
|
|
671
|
+
// `preallocate_lazy_ids` stamped `has_lazy_descendants` on the source
|
|
672
|
+
// `Component` node; the freshly-built `fn` shares the same params/body
|
|
673
|
+
// subtree, so the flag is equally applicable. Propagating it lets
|
|
674
|
+
// `apply_lazy_transforms` honor its constant-time early-return path.
|
|
675
|
+
if (/** @type {any} */ (component).metadata?.has_lazy_descendants) {
|
|
676
|
+
/** @type {any} */ (fn.metadata).has_lazy_descendants = true;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Apply lazy `&{}` / `&[]` rewrites end-to-end: the function-handler in
|
|
680
|
+
// `apply_lazy_transforms` collects param bindings, merges with body bindings
|
|
681
|
+
// discovered by the BlockStatement handler, replaces lazy params with their
|
|
682
|
+
// `__lazyN` ids, and rewrites every reference. Constant-time fast-path for
|
|
683
|
+
// functions whose subtrees contain no lazy patterns (flagged ahead of time
|
|
684
|
+
// by `preallocate_lazy_ids`). In type-only mode the rewrite is skipped so
|
|
685
|
+
// destructuring patterns survive into the virtual TSX and TypeScript can
|
|
686
|
+
// flow real types.
|
|
687
|
+
if (!transform_context.typeOnly) {
|
|
688
|
+
fn = /** @type {typeof fn} */ (apply_lazy_transforms(fn, new Map()));
|
|
689
|
+
}
|
|
690
|
+
|
|
694
691
|
// Restore context
|
|
695
692
|
transform_context.helper_state = saved_helper_state;
|
|
696
693
|
transform_context.available_bindings = saved_bindings;
|
|
@@ -2689,7 +2686,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
2689
2686
|
return;
|
|
2690
2687
|
}
|
|
2691
2688
|
|
|
2692
|
-
if (
|
|
2689
|
+
if (is_function_or_component_node(node)) {
|
|
2693
2690
|
return;
|
|
2694
2691
|
}
|
|
2695
2692
|
|
|
@@ -2831,7 +2828,7 @@ function validate_hook_outer_assignments_in_node(
|
|
|
2831
2828
|
function validate_hook_callback_outer_mutations(call_node, shadowed_names, transform_context) {
|
|
2832
2829
|
const hook_name = get_hook_callee_name(call_node.callee);
|
|
2833
2830
|
for (const argument of call_node.arguments || []) {
|
|
2834
|
-
if (!
|
|
2831
|
+
if (!is_function_or_component_node(argument)) {
|
|
2835
2832
|
continue;
|
|
2836
2833
|
}
|
|
2837
2834
|
const callback_shadowed_names = create_function_like_shadowed_names(argument, shadowed_names);
|
|
@@ -2844,21 +2841,6 @@ function validate_hook_callback_outer_mutations(call_node, shadowed_names, trans
|
|
|
2844
2841
|
}
|
|
2845
2842
|
}
|
|
2846
2843
|
|
|
2847
|
-
/**
|
|
2848
|
-
* @param {any} node
|
|
2849
|
-
* @returns {boolean}
|
|
2850
|
-
*/
|
|
2851
|
-
function is_function_like_node(node) {
|
|
2852
|
-
return (
|
|
2853
|
-
node.type === 'FunctionDeclaration' ||
|
|
2854
|
-
node.type === 'FunctionExpression' ||
|
|
2855
|
-
node.type === 'ArrowFunctionExpression' ||
|
|
2856
|
-
// this is just in case but we should already
|
|
2857
|
-
// have a component replaced with a function node
|
|
2858
|
-
node.type === 'Component'
|
|
2859
|
-
);
|
|
2860
|
-
}
|
|
2861
|
-
|
|
2862
2844
|
/**
|
|
2863
2845
|
* @param {any} node
|
|
2864
2846
|
* @param {Set<string>} shadowed_names
|
|
@@ -2906,7 +2888,7 @@ function validate_hook_callback_outer_mutations_in_node(
|
|
|
2906
2888
|
return;
|
|
2907
2889
|
}
|
|
2908
2890
|
|
|
2909
|
-
if (
|
|
2891
|
+
if (is_function_or_component_node(node)) {
|
|
2910
2892
|
validate_hook_callback_outer_mutations_in_node(
|
|
2911
2893
|
node.body,
|
|
2912
2894
|
create_function_like_shadowed_names(node, shadowed_names),
|
package/src/transform/lazy.js
CHANGED
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
/** @import * as AST from 'estree' */
|
|
2
2
|
|
|
3
3
|
import * as b from '../utils/builders.js';
|
|
4
|
+
import { is_function_or_component_node } from '../utils/ast.js';
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Lazy destructuring transform — framework-agnostic.
|
|
7
8
|
*
|
|
8
|
-
* Shared between `@tsrx/react
|
|
9
|
-
* references to names introduced by `&{ ... }` /
|
|
10
|
-
* patterns into member-expression accesses on a
|
|
9
|
+
* Shared between `@tsrx/react`, `@tsrx/preact`, `@tsrx/solid`, and `@tsrx/vue`.
|
|
10
|
+
* Walks an AST and rewrites references to names introduced by `&{ ... }` /
|
|
11
|
+
* `&[ ... ]` destructuring patterns into member-expression accesses on a
|
|
12
|
+
* generated source identifier.
|
|
11
13
|
*
|
|
12
14
|
* Usage:
|
|
13
15
|
* 1. Create a context with `createLazyContext()` (or provide any object with
|
|
14
16
|
* a `lazy_next_id: number` field).
|
|
15
17
|
* 2. Run `preallocateLazyIds(root, context)` once over the full program to
|
|
16
|
-
* assign stable `metadata.lazy_id` values to every lazy pattern
|
|
17
|
-
*
|
|
18
|
-
* `
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* assign stable `metadata.lazy_id` values to every lazy pattern and to
|
|
19
|
+
* flag function-like nodes whose subtree contains any lazy pattern via
|
|
20
|
+
* `metadata.has_lazy_descendants`.
|
|
21
|
+
* 3. After converting components to functions, call `applyLazyTransforms(fn,
|
|
22
|
+
* new Map())` on each top-level function. The function-handler walks the
|
|
23
|
+
* whole subtree, collects param + body bindings, replaces lazy patterns
|
|
24
|
+
* with their generated identifiers, and rewrites every reference.
|
|
22
25
|
*
|
|
23
26
|
* The transform is purely AST-to-AST and has no framework-specific knowledge.
|
|
24
27
|
*/
|
|
@@ -196,33 +199,60 @@ function set_lazy_param_binding_mappings(lazy_id, pattern) {
|
|
|
196
199
|
* Collect lazy bindings from a destructuring pattern.
|
|
197
200
|
*
|
|
198
201
|
* For `&{ name, age }` on source `S`, maps `name` → `S.name`, `age` → `S.age`.
|
|
199
|
-
* For `&[a, b]` on source `S`, maps `a` → `S[0]`, `b` → `S[1]`.
|
|
200
|
-
* `
|
|
202
|
+
* For `&[a, b]` on source `S`, maps `a` → `S[0]`, `b` → `S[1]`. Recurses into
|
|
203
|
+
* nested `ObjectPattern` / `ArrayPattern` values so that `&{ outer: &{ inner } }`
|
|
204
|
+
* on source `S` maps `inner` → `S.outer.inner`, and `&{ pair: &[first, second] }`
|
|
205
|
+
* maps `first` → `S.pair[0]`. Handles `AssignmentPattern` (default values lost,
|
|
206
|
+
* but the binding still resolves to the member chain). Skips `RestElement`.
|
|
201
207
|
*
|
|
202
208
|
* @param {any} pattern
|
|
203
209
|
* @param {string} source_name
|
|
204
210
|
* @param {Map<string, LazyBinding>} lazy_bindings
|
|
205
211
|
*/
|
|
206
212
|
export function collect_lazy_bindings(pattern, source_name, lazy_bindings) {
|
|
213
|
+
collect_lazy_bindings_at(
|
|
214
|
+
pattern,
|
|
215
|
+
source_name,
|
|
216
|
+
() => create_generated_identifier(source_name),
|
|
217
|
+
lazy_bindings,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Walk a destructure pattern and register a `LazyBinding` for each leaf
|
|
223
|
+
* `Identifier`, where `build_parent` produces the AST expression that reaches
|
|
224
|
+
* this pattern's value from the synthesized source identifier. Each nested
|
|
225
|
+
* level composes its accessor onto `build_parent`, so leaves get the full
|
|
226
|
+
* member chain (e.g. `source.outer.inner` for `&{ outer: &{ inner } }`).
|
|
227
|
+
*
|
|
228
|
+
* @param {any} pattern
|
|
229
|
+
* @param {string} source_name
|
|
230
|
+
* @param {(reference?: any) => any} build_parent
|
|
231
|
+
* @param {Map<string, LazyBinding>} lazy_bindings
|
|
232
|
+
*/
|
|
233
|
+
function collect_lazy_bindings_at(pattern, source_name, build_parent, lazy_bindings) {
|
|
207
234
|
if (pattern.type === 'ObjectPattern') {
|
|
208
235
|
for (const prop of pattern.properties || []) {
|
|
209
236
|
if (prop.type === 'RestElement') continue;
|
|
210
237
|
const value = prop.value;
|
|
211
238
|
const actual = value.type === 'AssignmentPattern' ? value.left : value;
|
|
239
|
+
const key = prop.key;
|
|
240
|
+
const computed = prop.computed || key.type !== 'Identifier';
|
|
241
|
+
|
|
242
|
+
/** @type {(reference?: any) => any} */
|
|
243
|
+
const build_self = (reference) =>
|
|
244
|
+
b.member(
|
|
245
|
+
build_parent(),
|
|
246
|
+
computed || key.type !== 'Identifier'
|
|
247
|
+
? { ...key }
|
|
248
|
+
: create_generated_identifier(key.name, reference, reference?.name),
|
|
249
|
+
computed,
|
|
250
|
+
);
|
|
251
|
+
|
|
212
252
|
if (actual.type === 'Identifier') {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
source_name,
|
|
217
|
-
read: (reference) =>
|
|
218
|
-
b.member(
|
|
219
|
-
create_generated_identifier(source_name),
|
|
220
|
-
computed || key.type !== 'Identifier'
|
|
221
|
-
? { ...key }
|
|
222
|
-
: create_generated_identifier(key.name, reference, reference?.name),
|
|
223
|
-
computed,
|
|
224
|
-
),
|
|
225
|
-
});
|
|
253
|
+
lazy_bindings.set(actual.name, { source_name, read: build_self });
|
|
254
|
+
} else if (actual.type === 'ObjectPattern' || actual.type === 'ArrayPattern') {
|
|
255
|
+
collect_lazy_bindings_at(actual, source_name, build_self, lazy_bindings);
|
|
226
256
|
}
|
|
227
257
|
}
|
|
228
258
|
} else if (pattern.type === 'ArrayPattern') {
|
|
@@ -231,48 +261,18 @@ export function collect_lazy_bindings(pattern, source_name, lazy_bindings) {
|
|
|
231
261
|
if (!element) continue;
|
|
232
262
|
if (element.type === 'RestElement') continue;
|
|
233
263
|
const actual = element.type === 'AssignmentPattern' ? element.left : element;
|
|
234
|
-
|
|
235
|
-
const index = i;
|
|
236
|
-
lazy_bindings.set(actual.name, {
|
|
237
|
-
source_name,
|
|
238
|
-
read: () => b.member(create_generated_identifier(source_name), b.literal(index), true),
|
|
239
|
-
});
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
264
|
+
const index = i;
|
|
244
265
|
|
|
245
|
-
/**
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
* @returns {Map<string, LazyBinding>}
|
|
253
|
-
*/
|
|
254
|
-
export function collect_lazy_bindings_from_component(params, body, context) {
|
|
255
|
-
/** @type {Map<string, LazyBinding>} */
|
|
256
|
-
const lazy_bindings = new Map();
|
|
257
|
-
|
|
258
|
-
for (const param of params) {
|
|
259
|
-
const pattern = param.type === 'AssignmentPattern' ? param.left : param;
|
|
260
|
-
if ((pattern.type === 'ObjectPattern' || pattern.type === 'ArrayPattern') && pattern.lazy) {
|
|
261
|
-
const lazy_name = pattern.metadata?.lazy_id || generate_lazy_id(context);
|
|
262
|
-
if (!pattern.metadata?.lazy_id) {
|
|
263
|
-
pattern.metadata = { ...pattern.metadata, lazy_id: lazy_name };
|
|
266
|
+
/** @type {() => any} */
|
|
267
|
+
const build_self = () => b.member(build_parent(), b.literal(index), true);
|
|
268
|
+
|
|
269
|
+
if (actual.type === 'Identifier') {
|
|
270
|
+
lazy_bindings.set(actual.name, { source_name, read: build_self });
|
|
271
|
+
} else if (actual.type === 'ObjectPattern' || actual.type === 'ArrayPattern') {
|
|
272
|
+
collect_lazy_bindings_at(actual, source_name, build_self, lazy_bindings);
|
|
264
273
|
}
|
|
265
|
-
collect_lazy_bindings(pattern, lazy_name, lazy_bindings);
|
|
266
274
|
}
|
|
267
275
|
}
|
|
268
|
-
|
|
269
|
-
// VariableDeclaration lazy patterns already have their `lazy_id` assigned
|
|
270
|
-
// by `preallocate_lazy_ids` (run once over the whole program by the target
|
|
271
|
-
// transforms), so `collect_lazy_bindings_from_statements` handles them
|
|
272
|
-
// alongside the expression-statement assignment form.
|
|
273
|
-
collect_lazy_bindings_from_statements(body, lazy_bindings);
|
|
274
|
-
|
|
275
|
-
return lazy_bindings;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
/**
|
|
@@ -288,61 +288,164 @@ export function collect_lazy_bindings_from_statements(statements, lazy_bindings)
|
|
|
288
288
|
for (const stmt of statements || []) {
|
|
289
289
|
if (stmt.type === 'VariableDeclaration') {
|
|
290
290
|
for (const declarator of stmt.declarations || []) {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
(
|
|
294
|
-
|
|
295
|
-
pattern.metadata?.lazy_id &&
|
|
296
|
-
!lazy_bindings_contains(lazy_bindings, pattern)
|
|
297
|
-
) {
|
|
298
|
-
collect_lazy_bindings(pattern, pattern.metadata.lazy_id, lazy_bindings);
|
|
299
|
-
}
|
|
291
|
+
visit_topmost_lazy_patterns(declarator.id, (lazy) => {
|
|
292
|
+
if (!lazy.metadata?.lazy_id) return;
|
|
293
|
+
collect_lazy_bindings(lazy, lazy.metadata.lazy_id, lazy_bindings);
|
|
294
|
+
});
|
|
300
295
|
}
|
|
301
296
|
} else if (
|
|
302
297
|
stmt.type === 'ExpressionStatement' &&
|
|
303
298
|
stmt.expression?.type === 'AssignmentExpression' &&
|
|
304
|
-
stmt.expression.operator === '='
|
|
305
|
-
(stmt.expression.left?.type === 'ObjectPattern' ||
|
|
306
|
-
stmt.expression.left?.type === 'ArrayPattern') &&
|
|
307
|
-
stmt.expression.left.lazy &&
|
|
308
|
-
stmt.expression.left.metadata?.lazy_id
|
|
299
|
+
stmt.expression.operator === '='
|
|
309
300
|
) {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
);
|
|
301
|
+
visit_topmost_lazy_patterns(stmt.expression.left, (lazy) => {
|
|
302
|
+
if (!lazy.metadata?.lazy_id) return;
|
|
303
|
+
collect_lazy_bindings(lazy, lazy.metadata.lazy_id, lazy_bindings);
|
|
304
|
+
});
|
|
315
305
|
}
|
|
316
306
|
}
|
|
317
307
|
}
|
|
318
308
|
|
|
319
309
|
/**
|
|
320
|
-
*
|
|
310
|
+
* Walk a destructure pattern tree, calling `visit` on every *topmost-lazy*
|
|
311
|
+
* descendant — a lazy `ObjectPattern` / `ArrayPattern` with no lazy ancestor
|
|
312
|
+
* within the same pattern tree. Descends through `AssignmentPattern`,
|
|
313
|
+
* `RestElement`, and non-lazy `ObjectPattern` / `ArrayPattern`. Stops at lazy
|
|
314
|
+
* patterns: their inner leaves are reached via accessor chains rooted at the
|
|
315
|
+
* lazy pattern's synthesized id, not by further descent here.
|
|
316
|
+
*
|
|
321
317
|
* @param {any} pattern
|
|
322
|
-
* @
|
|
318
|
+
* @param {(node: any) => void} visit
|
|
323
319
|
*/
|
|
324
|
-
function
|
|
320
|
+
function visit_topmost_lazy_patterns(pattern, visit) {
|
|
321
|
+
if (!pattern || typeof pattern !== 'object') return;
|
|
322
|
+
if (pattern.type === 'AssignmentPattern') {
|
|
323
|
+
visit_topmost_lazy_patterns(pattern.left, visit);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
if (pattern.type === 'RestElement') {
|
|
327
|
+
visit_topmost_lazy_patterns(pattern.argument, visit);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (pattern.type !== 'ObjectPattern' && pattern.type !== 'ArrayPattern') return;
|
|
331
|
+
|
|
332
|
+
if (pattern.lazy) {
|
|
333
|
+
visit(pattern);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
325
337
|
if (pattern.type === 'ObjectPattern') {
|
|
326
338
|
for (const prop of pattern.properties || []) {
|
|
327
|
-
if (prop.type === 'RestElement')
|
|
328
|
-
|
|
329
|
-
const actual = value?.type === 'AssignmentPattern' ? value.left : value;
|
|
330
|
-
if (actual?.type === 'Identifier' && lazy_bindings.has(actual.name)) return true;
|
|
339
|
+
if (prop.type === 'RestElement') visit_topmost_lazy_patterns(prop.argument, visit);
|
|
340
|
+
else visit_topmost_lazy_patterns(prop.value, visit);
|
|
331
341
|
}
|
|
332
|
-
} else
|
|
342
|
+
} else {
|
|
333
343
|
for (const element of pattern.elements || []) {
|
|
334
|
-
if (
|
|
335
|
-
const actual = element.type === 'AssignmentPattern' ? element.left : element;
|
|
336
|
-
if (actual?.type === 'Identifier' && lazy_bindings.has(actual.name)) return true;
|
|
344
|
+
if (element) visit_topmost_lazy_patterns(element, visit);
|
|
337
345
|
}
|
|
338
346
|
}
|
|
339
|
-
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Build the replacement identifier for a lazy pattern. When `is_top` is true
|
|
351
|
+
* (the pattern is itself a function parameter) we attach the original
|
|
352
|
+
* `typeAnnotation`, synthesize an object-shaped annotation for untyped object
|
|
353
|
+
* params so TypeScript sees prop names, and register source-mapping info.
|
|
354
|
+
* Nested replacements (inside a non-lazy outer destructure) can't carry an
|
|
355
|
+
* inline type annotation — that's not valid syntax — so they get a plain
|
|
356
|
+
* identifier with just source-range info.
|
|
357
|
+
*
|
|
358
|
+
* @param {any} pattern
|
|
359
|
+
* @param {boolean} is_top
|
|
360
|
+
* @returns {any}
|
|
361
|
+
*/
|
|
362
|
+
function build_lazy_id_for_pattern(pattern, is_top) {
|
|
363
|
+
const pattern_range = get_lazy_pattern_mapping_range(pattern);
|
|
364
|
+
const lazy_id = pattern_range
|
|
365
|
+
? create_generated_identifier(
|
|
366
|
+
pattern.metadata.lazy_id,
|
|
367
|
+
pattern_range,
|
|
368
|
+
undefined,
|
|
369
|
+
pattern_range.source_length,
|
|
370
|
+
)
|
|
371
|
+
: create_generated_identifier(pattern.metadata.lazy_id);
|
|
372
|
+
if (!is_top) return lazy_id;
|
|
373
|
+
if (pattern.typeAnnotation) {
|
|
374
|
+
lazy_id.typeAnnotation = pattern.typeAnnotation;
|
|
375
|
+
} else {
|
|
376
|
+
const type_annotation = create_lazy_object_type_annotation(pattern);
|
|
377
|
+
if (type_annotation) lazy_id.typeAnnotation = type_annotation;
|
|
378
|
+
}
|
|
379
|
+
set_lazy_param_binding_mappings(lazy_id, pattern);
|
|
380
|
+
return lazy_id;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Walk a destructure pattern tree and replace every topmost-lazy pattern with
|
|
385
|
+
* its synthesized id identifier. Non-lazy outer patterns are preserved so a
|
|
386
|
+
* source like `{ pair: &[a, b] }` becomes `{ pair: __lazy0 }`. The `is_top`
|
|
387
|
+
* flag is true only when the caller is invoking on a position that itself
|
|
388
|
+
* binds a single param (so a directly-lazy pattern can carry param-level type
|
|
389
|
+
* info); recursive descent into child patterns passes `false`.
|
|
390
|
+
*
|
|
391
|
+
* @param {any} pattern
|
|
392
|
+
* @param {boolean} [is_top]
|
|
393
|
+
* @returns {any}
|
|
394
|
+
*/
|
|
395
|
+
function replace_lazy_in_pattern(pattern, is_top = true) {
|
|
396
|
+
if (!pattern || typeof pattern !== 'object') return pattern;
|
|
397
|
+
|
|
398
|
+
if (pattern.type === 'AssignmentPattern') {
|
|
399
|
+
const new_left = replace_lazy_in_pattern(pattern.left, is_top);
|
|
400
|
+
return new_left === pattern.left ? pattern : { ...pattern, left: new_left };
|
|
401
|
+
}
|
|
402
|
+
if (pattern.type === 'RestElement') {
|
|
403
|
+
const new_arg = replace_lazy_in_pattern(pattern.argument, false);
|
|
404
|
+
return new_arg === pattern.argument ? pattern : { ...pattern, argument: new_arg };
|
|
405
|
+
}
|
|
406
|
+
if (pattern.type !== 'ObjectPattern' && pattern.type !== 'ArrayPattern') return pattern;
|
|
407
|
+
|
|
408
|
+
if (pattern.lazy && pattern.metadata?.lazy_id) {
|
|
409
|
+
return build_lazy_id_for_pattern(pattern, is_top);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (pattern.type === 'ObjectPattern') {
|
|
413
|
+
let changed = false;
|
|
414
|
+
const new_properties = (pattern.properties || []).map((/** @type {any} */ prop) => {
|
|
415
|
+
if (prop.type === 'RestElement') {
|
|
416
|
+
const new_arg = replace_lazy_in_pattern(prop.argument, false);
|
|
417
|
+
if (new_arg === prop.argument) return prop;
|
|
418
|
+
changed = true;
|
|
419
|
+
return { ...prop, argument: new_arg };
|
|
420
|
+
}
|
|
421
|
+
const new_value = replace_lazy_in_pattern(prop.value, false);
|
|
422
|
+
if (new_value === prop.value) return prop;
|
|
423
|
+
changed = true;
|
|
424
|
+
return { ...prop, value: new_value };
|
|
425
|
+
});
|
|
426
|
+
return changed ? { ...pattern, properties: new_properties } : pattern;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
let changed = false;
|
|
430
|
+
const new_elements = (pattern.elements || []).map((/** @type {any} */ element) => {
|
|
431
|
+
if (!element) return element;
|
|
432
|
+
const new_element = replace_lazy_in_pattern(element, false);
|
|
433
|
+
if (new_element !== element) changed = true;
|
|
434
|
+
return new_element;
|
|
435
|
+
});
|
|
436
|
+
return changed ? { ...pattern, elements: new_elements } : pattern;
|
|
340
437
|
}
|
|
341
438
|
|
|
342
439
|
/**
|
|
343
440
|
* Walk the AST and pre-allocate `lazy_id` metadata on every lazy destructuring
|
|
344
441
|
* pattern: function/component params, variable declarator ids, and statement-level
|
|
345
|
-
* assignment LHS.
|
|
442
|
+
* assignment LHS. Walks into non-lazy outer patterns to find nested lazy ones,
|
|
443
|
+
* e.g. `{ pair: &[a, b] }` allocates an id for the inner `&[a, b]`. Idempotent:
|
|
444
|
+
* skips patterns that already have a `lazy_id`.
|
|
445
|
+
*
|
|
446
|
+
* Also stamps `metadata.has_lazy_descendants = true` on every function-like
|
|
447
|
+
* node whose subtree contains any lazy pattern, so `apply_lazy_transforms`
|
|
448
|
+
* can take a constant-time early-return path for purely non-lazy functions.
|
|
346
449
|
*
|
|
347
450
|
* @param {any} root
|
|
348
451
|
* @param {LazyContext} context
|
|
@@ -350,32 +453,29 @@ function lazy_bindings_contains(lazy_bindings, pattern) {
|
|
|
350
453
|
export function preallocate_lazy_ids(root, context) {
|
|
351
454
|
/** @param {any} pattern */
|
|
352
455
|
const assign_id = (pattern) => {
|
|
353
|
-
|
|
354
|
-
(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
) {
|
|
358
|
-
pattern.metadata = {
|
|
359
|
-
...pattern.metadata,
|
|
360
|
-
lazy_id: generate_lazy_id(context),
|
|
361
|
-
};
|
|
362
|
-
}
|
|
456
|
+
visit_topmost_lazy_patterns(pattern, (lazy) => {
|
|
457
|
+
if (lazy.metadata?.lazy_id) return;
|
|
458
|
+
lazy.metadata = { ...lazy.metadata, lazy_id: generate_lazy_id(context) };
|
|
459
|
+
});
|
|
363
460
|
};
|
|
364
461
|
|
|
365
|
-
/**
|
|
462
|
+
/**
|
|
463
|
+
* @param {any} node
|
|
464
|
+
* @returns {boolean} true if `node`'s subtree contains any lazy pattern.
|
|
465
|
+
*/
|
|
366
466
|
const visit = (node) => {
|
|
367
|
-
if (!node || typeof node !== 'object') return;
|
|
467
|
+
if (!node || typeof node !== 'object') return false;
|
|
368
468
|
if (Array.isArray(node)) {
|
|
369
|
-
|
|
370
|
-
|
|
469
|
+
let found = false;
|
|
470
|
+
for (const child of node) {
|
|
471
|
+
if (visit(child)) found = true;
|
|
472
|
+
}
|
|
473
|
+
return found;
|
|
371
474
|
}
|
|
372
475
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
node.type === 'ArrowFunctionExpression' ||
|
|
377
|
-
node.type === 'Component'
|
|
378
|
-
) {
|
|
476
|
+
const is_function_like = is_function_or_component_node(node);
|
|
477
|
+
|
|
478
|
+
if (is_function_like) {
|
|
379
479
|
for (const param of node.params || []) {
|
|
380
480
|
assign_id(param?.type === 'AssignmentPattern' ? param.left : param);
|
|
381
481
|
}
|
|
@@ -393,10 +493,19 @@ export function preallocate_lazy_ids(root, context) {
|
|
|
393
493
|
assign_id(node.expression.left);
|
|
394
494
|
}
|
|
395
495
|
|
|
496
|
+
let found =
|
|
497
|
+
(node.type === 'ObjectPattern' || node.type === 'ArrayPattern') && node.lazy === true;
|
|
498
|
+
|
|
396
499
|
for (const key of Object.keys(node)) {
|
|
397
500
|
if (key === 'loc' || key === 'start' || key === 'end' || key === 'metadata') continue;
|
|
398
|
-
visit(node[key]);
|
|
501
|
+
if (visit(node[key])) found = true;
|
|
399
502
|
}
|
|
503
|
+
|
|
504
|
+
if (is_function_like && found) {
|
|
505
|
+
node.metadata = { ...node.metadata, has_lazy_descendants: true };
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
return found;
|
|
400
509
|
};
|
|
401
510
|
|
|
402
511
|
visit(root);
|
|
@@ -439,15 +548,11 @@ export function apply_lazy_transforms(node, lazy_bindings) {
|
|
|
439
548
|
const own_bindings = new Map();
|
|
440
549
|
let had_lazy_param = false;
|
|
441
550
|
for (const param of node.params || []) {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
(pattern?.type === 'ObjectPattern' || pattern?.type === 'ArrayPattern') &&
|
|
445
|
-
pattern.lazy &&
|
|
446
|
-
pattern.metadata?.lazy_id
|
|
447
|
-
) {
|
|
551
|
+
visit_topmost_lazy_patterns(param, (lazy) => {
|
|
552
|
+
if (!lazy.metadata?.lazy_id) return;
|
|
448
553
|
had_lazy_param = true;
|
|
449
|
-
collect_lazy_bindings(
|
|
450
|
-
}
|
|
554
|
+
collect_lazy_bindings(lazy, lazy.metadata.lazy_id, own_bindings);
|
|
555
|
+
});
|
|
451
556
|
}
|
|
452
557
|
|
|
453
558
|
// Own bindings override any outer binding with the same name.
|
|
@@ -456,10 +561,20 @@ export function apply_lazy_transforms(node, lazy_bindings) {
|
|
|
456
561
|
? new Map([...outer_minus_shadow, ...own_bindings])
|
|
457
562
|
: outer_minus_shadow;
|
|
458
563
|
|
|
459
|
-
if (
|
|
564
|
+
if (
|
|
565
|
+
inner_bindings.size === 0 &&
|
|
566
|
+
!params_changed &&
|
|
567
|
+
!had_lazy_param &&
|
|
568
|
+
!node.metadata?.has_lazy_descendants
|
|
569
|
+
) {
|
|
570
|
+
return node;
|
|
571
|
+
}
|
|
460
572
|
|
|
461
|
-
|
|
462
|
-
|
|
573
|
+
// Past the early-return: either we have active lazy bindings, lazy
|
|
574
|
+
// params to replace, defaults referencing outer lazy, or the body
|
|
575
|
+
// contains lazy descendants the BlockStatement handler will collect.
|
|
576
|
+
// In every case the body needs to be walked.
|
|
577
|
+
const new_body = apply_lazy_transforms(node.body, inner_bindings);
|
|
463
578
|
|
|
464
579
|
const final_params_src = params_changed ? new_params : node.params;
|
|
465
580
|
const final_params = had_lazy_param ? replace_lazy_params(final_params_src) : final_params_src;
|
|
@@ -599,6 +714,30 @@ export function apply_lazy_transforms(node, lazy_bindings) {
|
|
|
599
714
|
return b.const(lazy_id, init);
|
|
600
715
|
}
|
|
601
716
|
|
|
717
|
+
// Non-lazy outer assignment whose LHS contains nested lazy patterns:
|
|
718
|
+
// `{ pair: &[a, b] } = obj` → `{ pair: __lazy0 } = obj`. JS reference
|
|
719
|
+
// semantics carry writes from `__lazy0[0] = x` back into `obj.pair[0]`.
|
|
720
|
+
if (
|
|
721
|
+
node.type === 'ExpressionStatement' &&
|
|
722
|
+
node.expression?.type === 'AssignmentExpression' &&
|
|
723
|
+
node.expression.operator === '=' &&
|
|
724
|
+
(node.expression.left?.type === 'ObjectPattern' ||
|
|
725
|
+
node.expression.left?.type === 'ArrayPattern') &&
|
|
726
|
+
!node.expression.left.lazy
|
|
727
|
+
) {
|
|
728
|
+
const new_left = replace_lazy_in_pattern(node.expression.left);
|
|
729
|
+
if (new_left !== node.expression.left) {
|
|
730
|
+
return {
|
|
731
|
+
...node,
|
|
732
|
+
expression: {
|
|
733
|
+
...node.expression,
|
|
734
|
+
left: new_left,
|
|
735
|
+
right: apply_lazy_transforms(node.expression.right, lazy_bindings),
|
|
736
|
+
},
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
602
741
|
// AssignmentExpression / UpdateExpression whose target is a lazy identifier.
|
|
603
742
|
if (
|
|
604
743
|
node.type === 'AssignmentExpression' &&
|
|
@@ -633,6 +772,23 @@ export function apply_lazy_transforms(node, lazy_bindings) {
|
|
|
633
772
|
};
|
|
634
773
|
}
|
|
635
774
|
|
|
775
|
+
// Non-lazy outer declarator whose id contains nested lazy patterns:
|
|
776
|
+
// `let { pair: &[a, b] } = data` → `let { pair: __lazy0 } = data`.
|
|
777
|
+
if (
|
|
778
|
+
node.type === 'VariableDeclarator' &&
|
|
779
|
+
(node.id?.type === 'ObjectPattern' || node.id?.type === 'ArrayPattern') &&
|
|
780
|
+
!node.id.lazy
|
|
781
|
+
) {
|
|
782
|
+
const new_id = replace_lazy_in_pattern(node.id);
|
|
783
|
+
if (new_id !== node.id) {
|
|
784
|
+
return {
|
|
785
|
+
...node,
|
|
786
|
+
id: new_id,
|
|
787
|
+
init: apply_lazy_transforms(node.init, lazy_bindings),
|
|
788
|
+
};
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
636
792
|
// Shorthand object properties `{ name }` → `{ name: __lazy0.name }`.
|
|
637
793
|
if (node.type === 'Property' && node.shorthand && node.value?.type === 'Identifier') {
|
|
638
794
|
const binding = lazy_bindings.get(node.value.name);
|
|
@@ -758,38 +914,19 @@ function remove_shadowed(lazy_bindings, shadowed) {
|
|
|
758
914
|
|
|
759
915
|
/**
|
|
760
916
|
* Replace any lazy `&{}` / `&[]` patterns in a parameter list with their
|
|
761
|
-
* generated lazy identifiers
|
|
917
|
+
* generated lazy identifiers, including patterns nested inside non-lazy outer
|
|
918
|
+
* patterns. For `({ pair: &[a, b] })` returns `({ pair: __lazy0 })`. Leaves
|
|
919
|
+
* params without any lazy descendants untouched.
|
|
762
920
|
*
|
|
763
921
|
* @param {any[]} params
|
|
764
922
|
* @returns {any[]}
|
|
765
923
|
*/
|
|
766
924
|
export function replace_lazy_params(params) {
|
|
767
925
|
return params.map((param) => {
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
pattern.lazy &&
|
|
772
|
-
pattern.metadata?.lazy_id
|
|
773
|
-
) {
|
|
774
|
-
const pattern_range = get_lazy_pattern_mapping_range(pattern);
|
|
775
|
-
const lazy_id = pattern_range
|
|
776
|
-
? create_generated_identifier(
|
|
777
|
-
pattern.metadata.lazy_id,
|
|
778
|
-
pattern_range,
|
|
779
|
-
undefined,
|
|
780
|
-
pattern_range.source_length,
|
|
781
|
-
)
|
|
782
|
-
: create_generated_identifier(pattern.metadata.lazy_id);
|
|
783
|
-
if (pattern.typeAnnotation) {
|
|
784
|
-
lazy_id.typeAnnotation = pattern.typeAnnotation;
|
|
785
|
-
} else {
|
|
786
|
-
const type_annotation = create_lazy_object_type_annotation(pattern);
|
|
787
|
-
if (type_annotation) lazy_id.typeAnnotation = type_annotation;
|
|
788
|
-
}
|
|
789
|
-
set_lazy_param_binding_mappings(lazy_id, pattern);
|
|
790
|
-
if (param.type === 'AssignmentPattern') return { ...param, left: lazy_id };
|
|
791
|
-
return lazy_id;
|
|
926
|
+
if (param.type === 'AssignmentPattern') {
|
|
927
|
+
const new_left = replace_lazy_in_pattern(param.left);
|
|
928
|
+
return new_left === param.left ? param : { ...param, left: new_left };
|
|
792
929
|
}
|
|
793
|
-
return param;
|
|
930
|
+
return replace_lazy_in_pattern(param);
|
|
794
931
|
});
|
|
795
932
|
}
|
package/src/utils/ast.js
CHANGED
|
@@ -32,6 +32,19 @@ export function is_function_node(node) {
|
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @param {AST.Node} node
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
export function is_function_or_component_node(node) {
|
|
40
|
+
return (
|
|
41
|
+
node.type === 'FunctionDeclaration' ||
|
|
42
|
+
node.type === 'FunctionExpression' ||
|
|
43
|
+
node.type === 'ArrowFunctionExpression' ||
|
|
44
|
+
node.type === 'Component'
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
35
48
|
/**
|
|
36
49
|
* @param {AST.Node} node
|
|
37
50
|
* @returns {boolean}
|
package/src/utils/dom.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
const VOID_ELEMENT_NAMES = [
|
|
2
|
+
'area',
|
|
3
|
+
'base',
|
|
4
|
+
'br',
|
|
5
|
+
'col',
|
|
6
|
+
'command',
|
|
7
|
+
'embed',
|
|
8
|
+
'hr',
|
|
9
|
+
'img',
|
|
10
|
+
'input',
|
|
11
|
+
'keygen',
|
|
12
|
+
'link',
|
|
13
|
+
'meta',
|
|
14
|
+
'param',
|
|
15
|
+
'source',
|
|
16
|
+
'track',
|
|
17
|
+
'wbr',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns true if name is a void element
|
|
22
|
+
* @param {string} name
|
|
23
|
+
* @returns {boolean}
|
|
24
|
+
*/
|
|
25
|
+
export function is_void_element(name) {
|
|
26
|
+
return VOID_ELEMENT_NAMES.includes(name) || name.toLowerCase() === '!doctype';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const RESERVED_WORDS = [
|
|
30
|
+
'arguments',
|
|
31
|
+
'await',
|
|
32
|
+
'break',
|
|
33
|
+
'case',
|
|
34
|
+
'catch',
|
|
35
|
+
'class',
|
|
36
|
+
'const',
|
|
37
|
+
'continue',
|
|
38
|
+
'debugger',
|
|
39
|
+
'default',
|
|
40
|
+
'delete',
|
|
41
|
+
'do',
|
|
42
|
+
'else',
|
|
43
|
+
'enum',
|
|
44
|
+
'eval',
|
|
45
|
+
'export',
|
|
46
|
+
'extends',
|
|
47
|
+
'false',
|
|
48
|
+
'finally',
|
|
49
|
+
'for',
|
|
50
|
+
'function',
|
|
51
|
+
'if',
|
|
52
|
+
'implements',
|
|
53
|
+
'import',
|
|
54
|
+
'in',
|
|
55
|
+
'instanceof',
|
|
56
|
+
'interface',
|
|
57
|
+
'let',
|
|
58
|
+
'new',
|
|
59
|
+
'null',
|
|
60
|
+
'package',
|
|
61
|
+
'private',
|
|
62
|
+
'protected',
|
|
63
|
+
'public',
|
|
64
|
+
'return',
|
|
65
|
+
'static',
|
|
66
|
+
'super',
|
|
67
|
+
'switch',
|
|
68
|
+
'this',
|
|
69
|
+
'throw',
|
|
70
|
+
'true',
|
|
71
|
+
'try',
|
|
72
|
+
'typeof',
|
|
73
|
+
'var',
|
|
74
|
+
'void',
|
|
75
|
+
'while',
|
|
76
|
+
'with',
|
|
77
|
+
'yield',
|
|
78
|
+
];
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Returns true if word is a reserved JS keyword
|
|
82
|
+
* @param {string} word
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
export function is_reserved(word) {
|
|
86
|
+
return RESERVED_WORDS.includes(word);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Attributes that are boolean, i.e. they are present or not present.
|
|
91
|
+
*/
|
|
92
|
+
const DOM_BOOLEAN_ATTRIBUTES = [
|
|
93
|
+
'allowfullscreen',
|
|
94
|
+
'async',
|
|
95
|
+
'autofocus',
|
|
96
|
+
'autoplay',
|
|
97
|
+
'checked',
|
|
98
|
+
'controls',
|
|
99
|
+
'default',
|
|
100
|
+
'disabled',
|
|
101
|
+
'formnovalidate',
|
|
102
|
+
'hidden',
|
|
103
|
+
'indeterminate',
|
|
104
|
+
'inert',
|
|
105
|
+
'ismap',
|
|
106
|
+
'loop',
|
|
107
|
+
'multiple',
|
|
108
|
+
'muted',
|
|
109
|
+
'nomodule',
|
|
110
|
+
'novalidate',
|
|
111
|
+
'open',
|
|
112
|
+
'playsinline',
|
|
113
|
+
'readonly',
|
|
114
|
+
'required',
|
|
115
|
+
'reversed',
|
|
116
|
+
'seamless',
|
|
117
|
+
'selected',
|
|
118
|
+
'webkitdirectory',
|
|
119
|
+
'defer',
|
|
120
|
+
'disablepictureinpicture',
|
|
121
|
+
'disableremoteplayback',
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Returns true if name is a boolean DOM attribute
|
|
126
|
+
* @param {string} name
|
|
127
|
+
* @returns {boolean}
|
|
128
|
+
*/
|
|
129
|
+
export function is_boolean_attribute(name) {
|
|
130
|
+
return DOM_BOOLEAN_ATTRIBUTES.includes(name);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const DOM_PROPERTIES = [
|
|
134
|
+
...DOM_BOOLEAN_ATTRIBUTES,
|
|
135
|
+
'formNoValidate',
|
|
136
|
+
'isMap',
|
|
137
|
+
'noModule',
|
|
138
|
+
'playsInline',
|
|
139
|
+
'readOnly',
|
|
140
|
+
'value',
|
|
141
|
+
'volume',
|
|
142
|
+
'defaultValue',
|
|
143
|
+
'defaultChecked',
|
|
144
|
+
'srcObject',
|
|
145
|
+
'noValidate',
|
|
146
|
+
'allowFullscreen',
|
|
147
|
+
'disablePictureInPicture',
|
|
148
|
+
'disableRemotePlayback',
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Returns true if name is a DOM property
|
|
153
|
+
* @param {string} name
|
|
154
|
+
* @returns {boolean}
|
|
155
|
+
*/
|
|
156
|
+
export function is_dom_property(name) {
|
|
157
|
+
return DOM_PROPERTIES.includes(name);
|
|
158
|
+
}
|
package/src/utils.js
CHANGED
|
@@ -4,162 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
export { simple_hash, strong_hash } from './utils/hashing.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
'command',
|
|
14
|
-
'embed',
|
|
15
|
-
'hr',
|
|
16
|
-
'img',
|
|
17
|
-
'input',
|
|
18
|
-
'keygen',
|
|
19
|
-
'link',
|
|
20
|
-
'meta',
|
|
21
|
-
'param',
|
|
22
|
-
'source',
|
|
23
|
-
'track',
|
|
24
|
-
'wbr',
|
|
25
|
-
];
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Returns true if name is a void element
|
|
29
|
-
* @param {string} name
|
|
30
|
-
* @returns {boolean}
|
|
31
|
-
*/
|
|
32
|
-
export function is_void_element(name) {
|
|
33
|
-
return VOID_ELEMENT_NAMES.includes(name) || name.toLowerCase() === '!doctype';
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const RESERVED_WORDS = [
|
|
37
|
-
'arguments',
|
|
38
|
-
'await',
|
|
39
|
-
'break',
|
|
40
|
-
'case',
|
|
41
|
-
'catch',
|
|
42
|
-
'class',
|
|
43
|
-
'const',
|
|
44
|
-
'continue',
|
|
45
|
-
'debugger',
|
|
46
|
-
'default',
|
|
47
|
-
'delete',
|
|
48
|
-
'do',
|
|
49
|
-
'else',
|
|
50
|
-
'enum',
|
|
51
|
-
'eval',
|
|
52
|
-
'export',
|
|
53
|
-
'extends',
|
|
54
|
-
'false',
|
|
55
|
-
'finally',
|
|
56
|
-
'for',
|
|
57
|
-
'function',
|
|
58
|
-
'if',
|
|
59
|
-
'implements',
|
|
60
|
-
'import',
|
|
61
|
-
'in',
|
|
62
|
-
'instanceof',
|
|
63
|
-
'interface',
|
|
64
|
-
'let',
|
|
65
|
-
'new',
|
|
66
|
-
'null',
|
|
67
|
-
'package',
|
|
68
|
-
'private',
|
|
69
|
-
'protected',
|
|
70
|
-
'public',
|
|
71
|
-
'return',
|
|
72
|
-
'static',
|
|
73
|
-
'super',
|
|
74
|
-
'switch',
|
|
75
|
-
'this',
|
|
76
|
-
'throw',
|
|
77
|
-
'true',
|
|
78
|
-
'try',
|
|
79
|
-
'typeof',
|
|
80
|
-
'var',
|
|
81
|
-
'void',
|
|
82
|
-
'while',
|
|
83
|
-
'with',
|
|
84
|
-
'yield',
|
|
85
|
-
];
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Returns true if word is a reserved JS keyword
|
|
89
|
-
* @param {string} word
|
|
90
|
-
* @returns {boolean}
|
|
91
|
-
*/
|
|
92
|
-
export function is_reserved(word) {
|
|
93
|
-
return RESERVED_WORDS.includes(word);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Attributes that are boolean, i.e. they are present or not present.
|
|
98
|
-
*/
|
|
99
|
-
const DOM_BOOLEAN_ATTRIBUTES = [
|
|
100
|
-
'allowfullscreen',
|
|
101
|
-
'async',
|
|
102
|
-
'autofocus',
|
|
103
|
-
'autoplay',
|
|
104
|
-
'checked',
|
|
105
|
-
'controls',
|
|
106
|
-
'default',
|
|
107
|
-
'disabled',
|
|
108
|
-
'formnovalidate',
|
|
109
|
-
'hidden',
|
|
110
|
-
'indeterminate',
|
|
111
|
-
'inert',
|
|
112
|
-
'ismap',
|
|
113
|
-
'loop',
|
|
114
|
-
'multiple',
|
|
115
|
-
'muted',
|
|
116
|
-
'nomodule',
|
|
117
|
-
'novalidate',
|
|
118
|
-
'open',
|
|
119
|
-
'playsinline',
|
|
120
|
-
'readonly',
|
|
121
|
-
'required',
|
|
122
|
-
'reversed',
|
|
123
|
-
'seamless',
|
|
124
|
-
'selected',
|
|
125
|
-
'webkitdirectory',
|
|
126
|
-
'defer',
|
|
127
|
-
'disablepictureinpicture',
|
|
128
|
-
'disableremoteplayback',
|
|
129
|
-
];
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Returns true if name is a boolean DOM attribute
|
|
133
|
-
* @param {string} name
|
|
134
|
-
* @returns {boolean}
|
|
135
|
-
*/
|
|
136
|
-
export function is_boolean_attribute(name) {
|
|
137
|
-
return DOM_BOOLEAN_ATTRIBUTES.includes(name);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const DOM_PROPERTIES = [
|
|
141
|
-
...DOM_BOOLEAN_ATTRIBUTES,
|
|
142
|
-
'formNoValidate',
|
|
143
|
-
'isMap',
|
|
144
|
-
'noModule',
|
|
145
|
-
'playsInline',
|
|
146
|
-
'readOnly',
|
|
147
|
-
'value',
|
|
148
|
-
'volume',
|
|
149
|
-
'defaultValue',
|
|
150
|
-
'defaultChecked',
|
|
151
|
-
'srcObject',
|
|
152
|
-
'noValidate',
|
|
153
|
-
'allowFullscreen',
|
|
154
|
-
'disablePictureInPicture',
|
|
155
|
-
'disableRemotePlayback',
|
|
156
|
-
];
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Returns true if name is a DOM property
|
|
160
|
-
* @param {string} name
|
|
161
|
-
* @returns {boolean}
|
|
162
|
-
*/
|
|
163
|
-
export function is_dom_property(name) {
|
|
164
|
-
return DOM_PROPERTIES.includes(name);
|
|
165
|
-
}
|
|
7
|
+
export {
|
|
8
|
+
is_boolean_attribute,
|
|
9
|
+
is_dom_property,
|
|
10
|
+
is_reserved,
|
|
11
|
+
is_void_element,
|
|
12
|
+
} from './utils/dom.js';
|
package/types/index.d.ts
CHANGED
|
@@ -92,6 +92,7 @@ interface FunctionMetaData extends BaseNodeMetaData {
|
|
|
92
92
|
is_component?: boolean;
|
|
93
93
|
is_method?: boolean;
|
|
94
94
|
tracked?: boolean;
|
|
95
|
+
has_lazy_descendants?: boolean;
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
// Strip parent, loc, and range from TSESTree nodes to match @sveltejs/acorn-typescript output
|
package/types/jsx-platform.d.ts
CHANGED
|
@@ -306,8 +306,10 @@ export interface JsxPlatform {
|
|
|
306
306
|
* Module to import the `map_iterable` runtime helper (and the
|
|
307
307
|
* `IterationValue` type) from when compiling `for ... of` bodies whose
|
|
308
308
|
* source can be any `Iterable` — not just an array. React and Preact
|
|
309
|
-
* use `'@tsrx/
|
|
310
|
-
*
|
|
309
|
+
* use target-owned paths like `'@tsrx/react/runtime/iterable'` and
|
|
310
|
+
* `'@tsrx/preact/runtime/iterable'`, which re-export from
|
|
311
|
+
* `'@tsrx/core/runtime/iterable'`. Solid and Vue lower for-of via their
|
|
312
|
+
* own iteration components and leave this unset.
|
|
311
313
|
*/
|
|
312
314
|
forOfIterableHelper?: string;
|
|
313
315
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function event_name_from_capture(event_name: string): string;
|
|
2
|
+
export function get_attribute_event_name(
|
|
3
|
+
name: string,
|
|
4
|
+
handler: EventListener | { customName?: string },
|
|
5
|
+
): string;
|
|
6
|
+
export function get_original_event_name(name: string): string;
|
|
7
|
+
export function is_capture_event(event_name: string): boolean;
|
|
8
|
+
export function is_event_attribute(attr: string): boolean;
|
|
9
|
+
export function is_non_delegated(event_name: string): boolean;
|
|
10
|
+
export function is_passive_event(name: string): boolean;
|
|
11
|
+
export function normalize_event_name(name: string): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const get_descriptor: typeof Object.getOwnPropertyDescriptor;
|
|
2
|
+
export const get_descriptors: typeof Object.getOwnPropertyDescriptors;
|
|
3
|
+
export const array_from: typeof Array.from;
|
|
4
|
+
export const is_array: typeof Array.isArray;
|
|
5
|
+
export const define_property: typeof Object.defineProperty;
|
|
6
|
+
export const get_prototype_of: typeof Object.getPrototypeOf;
|
|
7
|
+
export const object_values: typeof Object.values;
|
|
8
|
+
export const object_entries: typeof Object.entries;
|
|
9
|
+
export const object_keys: typeof Object.keys;
|
|
10
|
+
export const get_own_property_symbols: typeof Object.getOwnPropertySymbols;
|
|
11
|
+
export const structured_clone: typeof structuredClone;
|
|
12
|
+
export const object_prototype: typeof Object.prototype;
|
|
13
|
+
export const array_prototype: typeof Array.prototype;
|
|
14
|
+
export const has_own_property: typeof Object.prototype.hasOwnProperty;
|
|
15
|
+
|
|
16
|
+
export function has_prototype_accessor(value: object, key: PropertyKey): boolean;
|
|
17
|
+
export function array_slice(array_like: ArrayLike<any>, ...args: number[]): any[];
|
|
File without changes
|
|
File without changes
|