@tsrx/core 0.1.49 → 0.1.51

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.
@@ -46,9 +46,10 @@ export function has_prototype_accessor(value, key) {
46
46
 
47
47
  /**
48
48
  * Slice helper for arrays and array-like values.
49
- * @param {ArrayLike<any>} array_like
49
+ * @template T
50
+ * @param {ArrayLike<T>} array_like
50
51
  * @param {...number} args
51
- * @returns {any[]}
52
+ * @returns {T[]}
52
53
  */
53
54
  export function array_slice(array_like, ...args) {
54
55
  return is_array(array_like)
@@ -93,38 +94,46 @@ export function iterable_array_from(iterable, index = 0) {
93
94
  /**
94
95
  * Creates a shallow forwarding object without one prop. Values are exposed through
95
96
  * getters so compiler-emitted reactive prop accessors are not snapshotted.
96
- * @param {Record<PropertyKey, any> | null | undefined} props
97
- * @param {PropertyKey} exclude_prop
98
- * @returns {Record<PropertyKey, any>}
97
+ *
98
+ * @template {object} [T=Record<PropertyKey, unknown>]
99
+ * @template {PropertyKey} [K=PropertyKey]
100
+ * @param {T | null | undefined} props
101
+ * @param {K} exclude_prop
102
+ * @returns {Omit<T, K>} the forwarding object; `{}` when `props` is nullish
99
103
  */
100
104
  export function exclude_prop_from_object(props, exclude_prop) {
101
- /** @type {Record<PropertyKey, any>} */
105
+ /** @type {Record<PropertyKey, unknown>} */
102
106
  const next = {};
103
- if (props == null) return next;
104
107
 
105
- for (const prop of Reflect.ownKeys(props)) {
106
- if (prop === exclude_prop) continue;
108
+ if (props != null) {
109
+ const source = /** @type {Record<PropertyKey, unknown>} */ (props);
107
110
 
108
- const descriptor = get_descriptor(props, prop);
109
- if (!descriptor?.enumerable) continue;
111
+ for (const prop of Reflect.ownKeys(source)) {
112
+ if (prop === exclude_prop) continue;
110
113
 
111
- /** @type {PropertyDescriptor} */
112
- const forwarding_descriptor = {
113
- enumerable: true,
114
- configurable: true,
115
- get() {
116
- return props[prop];
117
- },
118
- };
114
+ const descriptor = get_descriptor(source, prop);
115
+ if (!descriptor?.enumerable) continue;
119
116
 
120
- if (descriptor.writable === true || typeof descriptor.set === 'function') {
121
- forwarding_descriptor.set = (value) => {
122
- props[prop] = value;
117
+ /** @type {PropertyDescriptor} */
118
+ const forwarding_descriptor = {
119
+ enumerable: true,
120
+ configurable: true,
121
+ get() {
122
+ return source[prop];
123
+ },
123
124
  };
124
- }
125
125
 
126
- define_property(next, prop, forwarding_descriptor);
126
+ if (descriptor.writable === true || typeof descriptor.set === 'function') {
127
+ forwarding_descriptor.set = (value) => {
128
+ source[prop] = value;
129
+ };
130
+ }
131
+
132
+ define_property(next, prop, forwarding_descriptor);
133
+ }
127
134
  }
128
135
 
129
- return next;
136
+ // The forwarding object is assembled key by key, which no incremental type
137
+ // can describe; it mirrors `props` minus `exclude_prop` by construction.
138
+ return /** @type {Omit<T, K>} */ (next);
130
139
  }
@@ -1,3 +1,5 @@
1
+ /** @import { MergeableRef, RefProp, RefValue, SpreadProps } from '../../types/runtime/ref' */
2
+
1
3
  import {
2
4
  has_own_property,
3
5
  get_descriptor,
@@ -15,8 +17,9 @@ const REF_VALUE = Symbol();
15
17
  * any of the supported syntaxes. It does not process spreads, that is delegated to
16
18
  * `normalize_spread_props`.
17
19
  *
18
- * @param {...((node: any) => void | (() => void)) | { current: any } | { value: any } | null | undefined} refs
19
- * @returns {(node: any) => (() => void)}
20
+ * @template [T=Element]
21
+ * @param {...MergeableRef<T>} refs
22
+ * @returns {(node: T | null) => (() => void)}
20
23
  */
21
24
  export function mergeRefs(...refs) {
22
25
  return (node) => {
@@ -32,14 +35,14 @@ export function mergeRefs(...refs) {
32
35
  cleanups.push(() => ref(null));
33
36
  }
34
37
  } else if (is_ref_object(ref, 'current')) {
35
- /** @type {{ current: any }} */ (ref).current = node;
38
+ ref.current = node;
36
39
  cleanups.push(() => {
37
- /** @type {{ current: any }} */ (ref).current = null;
40
+ ref.current = null;
38
41
  });
39
42
  } else if (is_ref_object(ref, 'value')) {
40
- /** @type {{ value: any }} */ (ref).value = node;
43
+ ref.value = node;
41
44
  cleanups.push(() => {
42
- /** @type {{ value: any }} */ (ref).value = null;
45
+ ref.value = null;
43
46
  });
44
47
  }
45
48
  }
@@ -51,18 +54,31 @@ export function mergeRefs(...refs) {
51
54
 
52
55
  export { is_ref_prop as isRefProp };
53
56
 
57
+ /**
58
+ * A ref value that is a function is a callback ref — the bare-element branch of
59
+ * `RefValue` is never callable.
60
+ *
61
+ * @template T
62
+ * @param {RefValue<T>} value
63
+ * @returns {value is (node: T | null) => void | (() => void)}
64
+ */
65
+ function is_ref_callback(value) {
66
+ return typeof value === 'function';
67
+ }
68
+
54
69
  /**
55
70
  * @param {unknown} value
56
- * @returns {boolean}
71
+ * @returns {value is RefProp<Element>}
57
72
  */
58
73
  function is_ref_prop(value) {
59
74
  return typeof value === 'function' && REF_VALUE in value;
60
75
  }
61
76
 
62
77
  /**
63
- * @param {any} ref_value
64
- * @param {any} node
65
- * @param {(value: any) => void} [set_ref_value]
78
+ * @template [T=Element]
79
+ * @param {RefValue<T>} ref_value
80
+ * @param {T | null} node
81
+ * @param {(value: T | null) => void} [set_ref_value]
66
82
  * @returns {void | (() => void)}
67
83
  */
68
84
  export function apply_ref_value(ref_value, node, set_ref_value) {
@@ -73,7 +89,7 @@ export function apply_ref_value(ref_value, node, set_ref_value) {
73
89
  const cleanup = apply_ref_value(item, node);
74
90
  if (typeof cleanup === 'function') {
75
91
  cleanups.push(cleanup);
76
- } else if (typeof item === 'function' && node !== null) {
92
+ } else if (is_ref_callback(item) && node !== null) {
77
93
  cleanups.push(() => item(null));
78
94
  }
79
95
  }
@@ -85,7 +101,7 @@ export function apply_ref_value(ref_value, node, set_ref_value) {
85
101
  return;
86
102
  }
87
103
 
88
- if (typeof ref_value === 'function') {
104
+ if (is_ref_callback(ref_value)) {
89
105
  return ref_value(node);
90
106
  }
91
107
 
@@ -111,13 +127,14 @@ export function apply_ref_value(ref_value, node, set_ref_value) {
111
127
  }
112
128
 
113
129
  /**
114
- * @param {() => any} get_ref_value
115
- * @param {(value: any) => void} [set_ref_value]
116
- * @returns {(node: any) => void | (() => void)}
130
+ * @template [T=Element]
131
+ * @param {() => RefValue<T>} get_ref_value
132
+ * @param {(value: T | null) => void} [set_ref_value]
133
+ * @returns {RefProp<T>}
117
134
  */
118
135
  export function create_ref_prop(get_ref_value, set_ref_value) {
119
136
  /**
120
- * @param {any} node
137
+ * @param {T | null} node
121
138
  * @returns {void | (() => void)}
122
139
  */
123
140
  function ref_prop_callback(node) {
@@ -140,8 +157,9 @@ export function create_ref_prop(get_ref_value, set_ref_value) {
140
157
  }
141
158
 
142
159
  /**
143
- * @param {...any} refs
144
- * @returns {any}
160
+ * @template [T=Element]
161
+ * @param {...RefValue<T>} refs
162
+ * @returns {RefValue<T>} the single surviving ref, or a callback applying all
145
163
  */
146
164
  export function merge_ref_props(...refs) {
147
165
  const filtered = refs.filter((ref) => ref != null);
@@ -155,7 +173,7 @@ export function merge_ref_props(...refs) {
155
173
  }
156
174
 
157
175
  /**
158
- * @param {any} node
176
+ * @param {T | null} node
159
177
  * @returns {void | (() => void)}
160
178
  */
161
179
  function merged_ref_prop(node) {
@@ -166,7 +184,7 @@ export function merge_ref_props(...refs) {
166
184
  const cleanup = apply_ref_value(ref, node);
167
185
  if (typeof cleanup === 'function') {
168
186
  cleanups.push(cleanup);
169
- } else if (typeof ref === 'function' && node !== null) {
187
+ } else if (is_ref_callback(ref) && node !== null) {
170
188
  cleanups.push(() => ref(null));
171
189
  }
172
190
  }
@@ -182,36 +200,38 @@ export function merge_ref_props(...refs) {
182
200
  }
183
201
 
184
202
  /**
185
- * @param {Record<string | symbol, any> | null | undefined} props
186
- * @param {...any} outer_refs
187
- * @returns {Record<string | symbol, any> | null | undefined}
203
+ * @param {object | null | undefined} props a props bag; `object` rather than an
204
+ * index signature so an interface-typed bag is accepted
205
+ * @param {...RefValue<Element>} outer_refs
206
+ * @returns {SpreadProps | null | undefined}
188
207
  */
189
208
  export function normalize_spread_props(props, ...outer_refs) {
190
209
  if (props == null) {
191
210
  return props;
192
211
  }
193
212
 
194
- /** @type {any[]} */
213
+ const source = /** @type {SpreadProps} */ (props);
214
+ /** @type {Array<RefValue<Element>>} */
195
215
  const refs = [];
196
- /** @type {Record<string | symbol, any>} */
197
- let next = {};
216
+ /** @type {SpreadProps} */
217
+ const next = {};
198
218
  let changed = false;
199
219
  let existing_ref;
200
220
 
201
- for (const key of Reflect.ownKeys(props)) {
202
- const descriptor = get_descriptor(props, key);
221
+ for (const key of Reflect.ownKeys(source)) {
222
+ const descriptor = get_descriptor(source, key);
203
223
  if (!descriptor?.enumerable) {
204
224
  continue;
205
225
  }
206
226
 
207
- const value = /** @type {any} */ (props)[key];
227
+ const value = source[key];
208
228
 
209
229
  if (key === 'ref') {
210
230
  if (is_ref_prop(value)) {
211
231
  refs.push(value);
212
232
  changed = true;
213
233
  } else {
214
- existing_ref = value;
234
+ existing_ref = /** @type {RefValue<Element>} */ (value);
215
235
  }
216
236
  continue;
217
237
  }
@@ -226,7 +246,7 @@ export function normalize_spread_props(props, ...outer_refs) {
226
246
  }
227
247
 
228
248
  if (!changed && outer_refs.length === 0) {
229
- return props;
249
+ return source;
230
250
  }
231
251
 
232
252
  const merged_ref = merge_ref_props(existing_ref, ...refs, ...outer_refs);
@@ -243,9 +263,9 @@ export function normalize_spread_props(props, ...outer_refs) {
243
263
  * attribute but is non-enumerable so `{...normalized}` does not also pass it as
244
264
  * a DOM prop.
245
265
  *
246
- * @param {Record<string | symbol, any> | null | undefined} props
247
- * @param {...any} outer_refs
248
- * @returns {Record<string | symbol, any> | null | undefined}
266
+ * @param {object | null | undefined} props
267
+ * @param {...RefValue<Element>} outer_refs
268
+ * @returns {SpreadProps | null | undefined}
249
269
  */
250
270
  export function normalize_spread_props_for_ref_attr(props, ...outer_refs) {
251
271
  const next = normalize_spread_props(props, ...outer_refs);
@@ -266,9 +286,10 @@ export function normalize_spread_props_for_ref_attr(props, ...outer_refs) {
266
286
  }
267
287
 
268
288
  /**
289
+ * @template {'current' | 'value'} K
269
290
  * @param {object} value
270
- * @param {'current' | 'value'} key
271
- * @returns {boolean}
291
+ * @param {K} key
292
+ * @returns {value is Record<K, unknown>}
272
293
  */
273
294
  function is_ref_object(value, key) {
274
295
  if (is_dom_node(value)) {
package/src/scope.js CHANGED
@@ -5,7 +5,8 @@
5
5
  ScopeRootInterface,
6
6
  Context,
7
7
  ScopeConstructorInterface,
8
- ScopeConstructorParameters
8
+ ScopeConstructorParameters,
9
+ ScopeState
9
10
  } from '../types/index';
10
11
  @import * as AST from 'estree';
11
12
  */
@@ -27,14 +28,12 @@ import * as b from './utils/builders.js';
27
28
  * @returns {{ scope: ScopeInterface, scopes: Map<AST.Node, ScopeInterface> }} Scope information
28
29
  */
29
30
  export function create_scopes(ast, root, parent, error_options) {
30
- /** @typedef {{ scope: ScopeInterface }} State */
31
-
32
31
  /** @type {Map<AST.Node, ScopeInterface>} */
33
32
  const scopes = new Map();
34
33
  const scope = new Scope(root, parent, false, error_options);
35
34
  scopes.set(ast, scope);
36
35
 
37
- /** @type {State} */
36
+ /** @type {ScopeState} */
38
37
  const state = { scope };
39
38
  /** @type {Array<[ScopeInterface, { node: AST.Identifier, path: AST.Node[] }]>} */
40
39
  const references = [];
@@ -57,7 +56,7 @@ export function create_scopes(ast, root, parent, error_options) {
57
56
  /**
58
57
  * Create a block scope
59
58
  * @param {AST.Node} node - AST node
60
- * @param {Context<AST.Node, State>} context - Visitor context
59
+ * @param {Context<AST.Node, ScopeState>} context - Visitor context
61
60
  */
62
61
  const create_block_scope = (node, { state, next }) => {
63
62
  const scope = state.scope.child(true);
@@ -1,32 +1,18 @@
1
1
  /**
2
2
  * @import { PostProcessingChanges, LineOffsets } from '../types/index.js';
3
3
  * @import * as AST from 'estree';
4
- * @import { CodeMapping } from '../types/index.js';
4
+ * @import {
5
+ * CodeMapping,
6
+ * CodePosition,
7
+ * CodeToGeneratedMap,
8
+ * GeneratedToSourceMap,
9
+ * SourceLineGeneratedMap,
10
+ * SourceLineGeneratedPosition,
11
+ * } from '../types/index.js';
5
12
  * @import { CodeMapping as VolarCodeMapping } from '@volar/language-core';
6
13
  * @import { RawSourceMap } from 'source-map';
7
14
  */
8
15
 
9
- /**
10
- * @typedef {{
11
- * line: number,
12
- * column: number,
13
- * end_line: number,
14
- * end_column: number,
15
- * code: string,
16
- * metadata: {
17
- * css?: AST.Node['metadata']['css']
18
- * },
19
- * }} CodePosition
20
- * @typedef {{
21
- * column: number,
22
- * position: CodePosition,
23
- * }} SourceLineGeneratedPosition
24
- */
25
-
26
- /** @typedef {Map<string, CodePosition[]>} CodeToGeneratedMap */
27
- /** @typedef {Map<string, {line: number, column: number}[]>} GeneratedToSourceMap */
28
- /** @typedef {Map<number, SourceLineGeneratedPosition[]>} SourceLineGeneratedMap */
29
-
30
16
  import { decode } from '@jridgewell/sourcemap-codec';
31
17
 
32
18
  /** @type {VolarCodeMapping['data']} */
@@ -1,4 +1,5 @@
1
1
  /** @import * as AST from 'estree' */
2
+ /** @import * as ESRap from 'esrap' */
2
3
 
3
4
  /**
4
5
  * Add TSRX import-phase support to an esrap TS/TSX visitor set. esrap 2.3
@@ -6,7 +7,7 @@
6
7
  * `phase` field yet, so delegating would silently turn a deferred import into
7
8
  * an eager one.
8
9
  *
9
- * @template {Record<string, any>} T
10
+ * @template {ESRap.Visitors} T
10
11
  * @param {T} visitors
11
12
  * @returns {T}
12
13
  */
@@ -24,11 +25,10 @@ export function with_deferred_imports(visitors) {
24
25
  ...visitors,
25
26
  /**
26
27
  * @param {AST.ImportDeclaration} node
27
- * @param {import('esrap').Context} context
28
+ * @param {ESRap.Context} context
28
29
  */
29
30
  ImportDeclaration(node, context) {
30
- const import_node = /** @type {AST.ImportDeclaration & { phase?: 'defer' | null }} */ (node);
31
- if (import_node.phase !== 'defer') {
31
+ if (node.phase !== 'defer') {
32
32
  print_import_declaration(node, context);
33
33
  return;
34
34
  }
@@ -48,10 +48,7 @@ export function with_deferred_imports(visitors) {
48
48
  context.write(' from ');
49
49
  context.visit(node.source);
50
50
 
51
- const attributes =
52
- /** @type {Array<{ key: AST.Identifier | AST.Literal, value: AST.Literal }>} */ (
53
- /** @type {any} */ (node).attributes ?? /** @type {any} */ (node).assertions ?? []
54
- );
51
+ const attributes = node.attributes ?? node.assertions ?? [];
55
52
  if (attributes.length > 0) {
56
53
  context.write(' with { ');
57
54
  for (let index = 0; index < attributes.length; index++) {
@@ -68,11 +65,10 @@ export function with_deferred_imports(visitors) {
68
65
  },
69
66
  /**
70
67
  * @param {AST.ImportExpression} node
71
- * @param {import('esrap').Context} context
68
+ * @param {ESRap.Context} context
72
69
  */
73
70
  ImportExpression(node, context) {
74
- const import_node = /** @type {AST.ImportExpression & { phase?: 'defer' | null }} */ (node);
75
- if (import_node.phase !== 'defer') {
71
+ if (node.phase !== 'defer') {
76
72
  print_import_expression(node, context);
77
73
  return;
78
74
  }
@@ -81,9 +77,7 @@ export function with_deferred_imports(visitors) {
81
77
  context.write('import.defer(');
82
78
  context.visit(node.source);
83
79
 
84
- const options =
85
- node.options ??
86
- /** @type {AST.Expression | undefined} */ (/** @type {any} */ (node).arguments?.[0]);
80
+ const options = node.options ?? node.arguments?.[0];
87
81
  if (options) {
88
82
  context.write(', ');
89
83
  context.visit(options);
@@ -108,7 +108,10 @@ function clone_jsx_member_expression(name, source_node) {
108
108
  export function add_extra_source_mappings_from_matching_expression(generated, source) {
109
109
  if (!generated || !source || generated.type !== source.type) return;
110
110
 
111
- if (generated.type === 'Identifier' || generated.type === 'PrivateIdentifier') {
111
+ if (
112
+ (generated.type === 'Identifier' && source.type === 'Identifier') ||
113
+ (generated.type === 'PrivateIdentifier' && source.type === 'PrivateIdentifier')
114
+ ) {
112
115
  if (!has_location(source)) return;
113
116
  generated.metadata ??= { path: [] };
114
117
  generated.metadata.extra_source_mappings ??= [];
@@ -66,6 +66,7 @@ import {
66
66
  is_function_node,
67
67
  is_function_or_class_node as is_function_or_class_boundary,
68
68
  is_template_directive as is_jsx_control_flow_expression,
69
+ node_children,
69
70
  } from '../../utils/ast.js';
70
71
 
71
72
  const TEMPLATE_FRAGMENT_ERROR =
@@ -1046,19 +1047,6 @@ function inject_dynamic_import(program, transform_context) {
1046
1047
  );
1047
1048
  }
1048
1049
 
1049
- /**
1050
- * The children a node carries, as nodes. Node types differ in whether they
1051
- * have a `children` slot at all (`JSXCodeBlock` does not) and in what it may
1052
- * hold, so this reads it uniformly instead of forcing every caller to narrow.
1053
- *
1054
- * @param {AST.Node} node
1055
- * @returns {AST.Node[]}
1056
- */
1057
- function node_children(node) {
1058
- const children = /** @type {AST.TraversableAstNode} */ (node).children;
1059
- return Array.isArray(children) ? children.filter(is_ast_node) : [];
1060
- }
1061
-
1062
1050
  /**
1063
1051
  * Attach selector-location metadata used by editor definitions/hover before
1064
1052
  * the shared scoping pass mutates class attributes with the component hash.