@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 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",
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/index.d.ts",
36
- "default": "./src/runtime/index.js"
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.6",
71
- "@tsrx/react": "0.2.6",
72
- "@tsrx/solid": "0.1.6",
73
- "@tsrx/vue": "0.1.6"
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,10 @@
1
+ export {
2
+ event_name_from_capture,
3
+ get_attribute_event_name,
4
+ get_original_event_name,
5
+ is_capture_event,
6
+ is_event_attribute,
7
+ is_non_delegated,
8
+ is_passive_event,
9
+ normalize_event_name,
10
+ } from '../utils/events.js';
@@ -0,0 +1 @@
1
+ export { simple_hash, strong_hash } from '../utils/hashing.js';
@@ -0,0 +1,3 @@
1
+ export { is_boolean_attribute } from '../utils/dom.js';
2
+ export { escape, escape_script } from '../utils/escaping.js';
3
+ export { normalize_css_property_name } from '../utils/normalize_css_property_name.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
- final_params,
683
- final_body,
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(final_params, final_body, is_async_component, component.typeParameters);
665
+ fn = b.arrow(params, body_block, is_async_component, component.typeParameters);
689
666
  } else {
690
- fn = b.function(null, final_params, final_body, is_async_component, component.typeParameters);
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 (is_function_like_node(node)) {
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 (!is_function_like_node(argument)) {
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 (is_function_like_node(node)) {
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),
@@ -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` and `@tsrx/solid`. Walks an AST and rewrites
9
- * references to names introduced by `&{ ... }` / `&[ ... ]` destructuring
10
- * patterns into member-expression accesses on a generated source identifier.
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
- * 3. For each function/component scope, collect bindings with
18
- * `collectLazyBindingsFromComponent(params, body, context)` and pass the
19
- * resulting map into `applyLazyTransforms(body, map)`.
20
- * 4. If a component declares lazy params, pass its params through
21
- * `replaceLazyParams(params)` before emitting.
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]`. Handles nested
200
- * `AssignmentPattern` (default values); skips `RestElement`.
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
- const key = prop.key;
214
- const computed = prop.computed || key.type !== 'Identifier';
215
- lazy_bindings.set(actual.name, {
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
- if (actual.type === 'Identifier') {
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
- * Collect lazy bindings from a component's params and top-level body declarations.
247
- * Mutates each lazy pattern's `metadata.lazy_id` in place (idempotent if already set).
248
- *
249
- * @param {any[]} params
250
- * @param {any[]} body
251
- * @param {LazyContext} context
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
- const pattern = declarator.id;
292
- if (
293
- (pattern?.type === 'ObjectPattern' || pattern?.type === 'ArrayPattern') &&
294
- pattern.lazy &&
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
- collect_lazy_bindings(
311
- stmt.expression.left,
312
- stmt.expression.left.metadata.lazy_id,
313
- lazy_bindings,
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
- * @param {Map<string, LazyBinding>} lazy_bindings
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
- * @returns {boolean}
318
+ * @param {(node: any) => void} visit
323
319
  */
324
- function lazy_bindings_contains(lazy_bindings, pattern) {
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') continue;
328
- const value = prop.value;
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 if (pattern.type === 'ArrayPattern') {
342
+ } else {
333
343
  for (const element of pattern.elements || []) {
334
- if (!element || element.type === 'RestElement') continue;
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
- return false;
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. Idempotent: skips patterns that already have a `lazy_id`.
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
- if (
354
- (pattern?.type === 'ObjectPattern' || pattern?.type === 'ArrayPattern') &&
355
- pattern.lazy &&
356
- !pattern.metadata?.lazy_id
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
- /** @param {any} node */
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
- for (const child of node) visit(child);
370
- return;
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
- if (
374
- node.type === 'FunctionDeclaration' ||
375
- node.type === 'FunctionExpression' ||
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
- const pattern = param?.type === 'AssignmentPattern' ? param.left : param;
443
- if (
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(pattern, pattern.metadata.lazy_id, own_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 (inner_bindings.size === 0 && !params_changed && !had_lazy_param) return node;
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
- const new_body =
462
- inner_bindings.size > 0 ? apply_lazy_transforms(node.body, inner_bindings) : node.body;
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. Leaves non-lazy params untouched.
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
- const pattern = param.type === 'AssignmentPattern' ? param.left : param;
769
- if (
770
- (pattern.type === 'ObjectPattern' || pattern.type === 'ArrayPattern') &&
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}
@@ -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
- const VOID_ELEMENT_NAMES = [
9
- 'area',
10
- 'base',
11
- 'br',
12
- 'col',
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
@@ -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/core/runtime'`. Solid and Vue lower for-of via their own
310
- * iteration components and leave this unset.
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,2 @@
1
+ export function simple_hash(str: string): string;
2
+ export function strong_hash(str: string): string;
@@ -0,0 +1,4 @@
1
+ export function escape<V>(value: V, is_attr?: boolean): string;
2
+ export function escape_script(str: string): string;
3
+ export function is_boolean_attribute(name: string): boolean;
4
+ export function normalize_css_property_name(str: 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