@tsrx/core 0.1.58 → 0.1.60

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.58",
6
+ "version": "0.1.60",
7
7
  "type": "module",
8
8
  "repository": {
9
9
  "type": "git",
@@ -78,12 +78,14 @@
78
78
  "esrap": "^2.3.2",
79
79
  "is-reference": "^3.0.3",
80
80
  "magic-string": "^0.30.18",
81
- "zimmerframe": "^1.1.2"
81
+ "zimmerframe": "^1.1.2",
82
+ "@tsrx/runtime": "0.1.1"
82
83
  },
83
84
  "devDependencies": {
84
85
  "@types/node": "^24.3.0",
85
86
  "@typescript-eslint/types": "^8.40.0",
86
87
  "@volar/language-core": "~2.4.28",
88
+ "@volar/source-map": "~2.4.28",
87
89
  "typescript": "^5.9.3",
88
90
  "vite": "^8.1.5",
89
91
  "vscode-languageserver-types": "^3.17.5"
@@ -15,6 +15,8 @@ const BACKWARD = 1;
15
15
  // since the code is synchronous, this is safe
16
16
  /** @type {string} */
17
17
  let css_hash;
18
+ /** @type {string} */
19
+ let css_region_hash;
18
20
  /** @type {StyleClasses} */
19
21
  let style_identifier_classes;
20
22
  /** @type {TopScopedClasses} */
@@ -300,6 +302,7 @@ function apply_selector(relative_selectors, rule, element, direction) {
300
302
  start: selector.start,
301
303
  end: selector.end,
302
304
  selector: selector,
305
+ regionHash: css_region_hash,
303
306
  });
304
307
  }
305
308
  }
@@ -1107,10 +1110,12 @@ function rule_has_animation(rule) {
1107
1110
  * @param {AST.TSRXElementNode} element
1108
1111
  * @param {StyleClasses} styleClasses
1109
1112
  * @param {TopScopedClasses} topScopedClasses
1113
+ * @param {string} [regionHash]
1110
1114
  * @return {void}
1111
1115
  */
1112
- export function prune_css(css, element, styleClasses, topScopedClasses) {
1116
+ export function prune_css(css, element, styleClasses, topScopedClasses, regionHash = css.hash) {
1113
1117
  css_hash = css.hash;
1118
+ css_region_hash = regionHash;
1114
1119
  style_identifier_classes = styleClasses;
1115
1120
  top_scoped_classes = topScopedClasses;
1116
1121
 
@@ -1152,6 +1157,7 @@ export function prune_css(css, element, styleClasses, topScopedClasses) {
1152
1157
  start: class_selector.start,
1153
1158
  end: class_selector.end,
1154
1159
  selector: class_selector,
1160
+ regionHash: css_region_hash,
1155
1161
  });
1156
1162
  }
1157
1163
  }
@@ -29,6 +29,14 @@ export function is_triple_slash_directive(comment) {
29
29
  return /^\/\s*<(reference|amd-module|amd-dependency)/.test(value);
30
30
  }
31
31
 
32
+ /**
33
+ * @param {AST.CommentWithLocation} comment
34
+ * @returns {boolean}
35
+ */
36
+ export function is_jsdoc_comment(comment) {
37
+ return comment.type === 'Block' && comment.value.startsWith('*');
38
+ }
39
+
32
40
  /**
33
41
  * Check if a comment is a JSDoc comment with TypeScript annotations
34
42
  * Examples: block comments containing `@type`, `@typedef`, `@param`, `@returns`, etc.
@@ -36,10 +44,7 @@ export function is_triple_slash_directive(comment) {
36
44
  * @returns {boolean}
37
45
  */
38
46
  export function is_jsdoc_ts_annotation(comment) {
39
- if (comment.type !== 'Block') return false;
40
-
41
- // JSDoc comments start with /** which means the value starts with * after /* is stripped
42
- if (!comment.value.startsWith('*')) return false;
47
+ if (!is_jsdoc_comment(comment)) return false;
43
48
 
44
49
  // Check if it contains TypeScript-relevant tags
45
50
  const tsAnnotations = [
@@ -84,6 +89,36 @@ export function should_preserve_comment(comment) {
84
89
  );
85
90
  }
86
91
 
92
+ /**
93
+ * Declaration generators also consume documentation and custom `// @...`
94
+ * annotations from JSX-backed virtual TypeScript. Keep this broader policy
95
+ * separate from the legacy to_ts printer's semantic-comment policy.
96
+ * @param {AST.CommentWithLocation} comment
97
+ * @returns {boolean}
98
+ */
99
+ export function should_preserve_jsx_tooling_comment(comment) {
100
+ return (
101
+ should_preserve_comment(comment) ||
102
+ is_jsdoc_comment(comment) ||
103
+ (comment.type === 'Line' && comment.value.trimStart().startsWith('@'))
104
+ );
105
+ }
106
+
107
+ /**
108
+ * Only file-wide directives may move ahead of generated imports or hoists.
109
+ * In particular, @ts-ignore/@ts-expect-error and declaration JSDoc must stay
110
+ * with the statement they describe.
111
+ * @param {AST.CommentWithLocation} comment
112
+ * @returns {boolean}
113
+ */
114
+ export function is_file_level_pragma(comment) {
115
+ return (
116
+ is_jsx_pragma(comment) ||
117
+ is_triple_slash_directive(comment) ||
118
+ (comment.type === 'Line' && /^\s*@ts-(?:no)?check\b/.test(comment.value))
119
+ );
120
+ }
121
+
87
122
  /**
88
123
  * Format a comment for output
89
124
  * @param {AST.CommentWithLocation} comment
@@ -1,112 +1 @@
1
- /**
2
- * @template T
3
- * @template U
4
- * @param {Iterable<T> | Iterator<T>} iterable
5
- * @param {(item: T, index: number, is_last: boolean) => U} fn
6
- * @param {() => U | U[]} [tail]
7
- * @param {() => U | U[]} [empty]
8
- * @returns {U[]}
9
- */
10
- export function map_iterable(iterable, fn, tail, empty) {
11
- if (Array.isArray(iterable)) {
12
- return map_array(iterable, fn, tail, empty);
13
- }
14
-
15
- /** @type {Iterator<T>} */
16
- var iterator;
17
- var iterable_prop = /** @type {Iterable<T>} */ (iterable)[Symbol.iterator];
18
-
19
- if (typeof iterable_prop === 'function') {
20
- iterator = iterable_prop.call(iterable);
21
- } else if (typeof (/** @type {Iterator<T>} */ (iterable).next) === 'function') {
22
- iterator = Iterator.from(iterable);
23
- } else {
24
- throw new TypeError('The loop target has to be an Iterable');
25
- }
26
-
27
- var current = iterator.next();
28
- if (current.done) {
29
- if (!empty) {
30
- return [];
31
- }
32
- var empty_value = empty();
33
- if (Array.isArray(empty_value)) {
34
- return empty_value;
35
- }
36
- return [empty_value];
37
- }
38
-
39
- var index = 0;
40
- var result = [];
41
- while (true) {
42
- var next = iterator.next();
43
- var value = fn(current.value, index++, !!next.done);
44
- if (Array.isArray(value)) {
45
- for (var j = 0; j < value.length; j++) {
46
- result.push(value[j]);
47
- }
48
- } else {
49
- result.push(value);
50
- }
51
- if (next.done) {
52
- break;
53
- }
54
- current = next;
55
- }
56
- if (tail) {
57
- var tail_value = tail();
58
- if (Array.isArray(tail_value)) {
59
- for (var j = 0; j < tail_value.length; j++) {
60
- result.push(tail_value[j]);
61
- }
62
- } else {
63
- result.push(tail_value);
64
- }
65
- }
66
- return result;
67
- }
68
-
69
- /**
70
- * @template T
71
- * @template U
72
- * @param {Array<T>} array
73
- * @param {(item: T, index: number, is_last: boolean) => U} fn
74
- * @param {() => U | U[]} [tail]
75
- * @param {() => U | U[]} [empty]
76
- * @returns {U[]}
77
- */
78
- function map_array(array, fn, tail, empty) {
79
- var length = array.length;
80
- if (length === 0) {
81
- if (!empty) {
82
- return [];
83
- }
84
- var empty_value = empty();
85
- if (Array.isArray(empty_value)) {
86
- return empty_value;
87
- }
88
- return [empty_value];
89
- }
90
- var result = [];
91
- for (var i = 0; i < length; i++) {
92
- var value = fn(array[i], i, i === length - 1);
93
- if (Array.isArray(value)) {
94
- for (var j = 0; j < value.length; j++) {
95
- result.push(value[j]);
96
- }
97
- } else {
98
- result.push(value);
99
- }
100
- }
101
- if (tail) {
102
- var tail_value = tail();
103
- if (Array.isArray(tail_value)) {
104
- for (var j = 0; j < tail_value.length; j++) {
105
- result.push(tail_value[j]);
106
- }
107
- } else {
108
- result.push(tail_value);
109
- }
110
- }
111
- return result;
112
- }
1
+ export * from '@tsrx/runtime/iterable';
@@ -1,139 +1 @@
1
- /** @type {typeof Object.getOwnPropertyDescriptor} */
2
- export var get_descriptor = Object.getOwnPropertyDescriptor;
3
- /** @type {typeof Object.getOwnPropertyDescriptors} */
4
- export var get_descriptors = Object.getOwnPropertyDescriptors;
5
- /** @type {typeof Array.from} */
6
- export var array_from = Array.from;
7
- /** @type {typeof Array.isArray} */
8
- export var is_array = Array.isArray;
9
- /** @type {typeof Object.defineProperty} */
10
- export var define_property = Object.defineProperty;
11
- /** @type {typeof Object.getPrototypeOf} */
12
- export var get_prototype_of = Object.getPrototypeOf;
13
- /** @type {typeof Object.values} */
14
- export var object_values = Object.values;
15
- /** @type {typeof Object.entries} */
16
- export var object_entries = Object.entries;
17
- /** @type {typeof Object.keys} */
18
- export var object_keys = Object.keys;
19
- /** @type {typeof Object.getOwnPropertySymbols} */
20
- export var get_own_property_symbols = Object.getOwnPropertySymbols;
21
- /** @type {typeof structuredClone} */
22
- export var structured_clone = structuredClone;
23
- /** @type {typeof Object.prototype} */
24
- export var object_prototype = Object.prototype;
25
- /** @type {typeof Array.prototype} */
26
- export var array_prototype = Array.prototype;
27
- /** @type {typeof Object.prototype.hasOwnProperty} */
28
- export var has_own_property = object_prototype.hasOwnProperty;
29
-
30
- /**
31
- * @param {object} value
32
- * @param {PropertyKey} key
33
- * @returns {boolean}
34
- */
35
- export function has_prototype_accessor(value, key) {
36
- var proto = get_prototype_of(value);
37
- while (proto != null) {
38
- var descriptor = get_descriptor(proto, key);
39
- if (descriptor !== undefined) {
40
- return typeof descriptor.get === 'function' || typeof descriptor.set === 'function';
41
- }
42
- proto = get_prototype_of(proto);
43
- }
44
- return false;
45
- }
46
-
47
- /**
48
- * Slice helper for arrays and array-like values.
49
- * @template T
50
- * @param {ArrayLike<T>} array_like
51
- * @param {...number} args
52
- * @returns {T[]}
53
- */
54
- export function array_slice(array_like, ...args) {
55
- return is_array(array_like)
56
- ? array_like.slice(...args)
57
- : array_prototype.slice.call(array_like, ...args);
58
- }
59
-
60
- /**
61
- * Converts iterables, iterators, and array-like values to an array from an index.
62
- * @template T
63
- * @param {Iterable<T> | Iterator<T> | ArrayLike<T>} iterable
64
- * @param {number} [index]
65
- * @returns {T[]}
66
- */
67
- export function iterable_array_from(iterable, index = 0) {
68
- /** @type {Iterator<T>} */
69
- var iterator;
70
- var iterable_prop = /** @type {Iterable<T>} */ (iterable)[Symbol.iterator];
71
-
72
- if (typeof iterable_prop === 'function') {
73
- iterator = iterable_prop.call(iterable);
74
- } else if (typeof (/** @type {Iterator<T>} */ (iterable).next) === 'function') {
75
- iterator = Iterator.from(/** @type {Iterator<T>} */ (iterable));
76
- } else {
77
- return array_from(/** @type {ArrayLike<T>} */ (iterable)).slice(index);
78
- }
79
-
80
- var result = [];
81
- var i = 0;
82
- var current = iterator.next();
83
- while (!current.done) {
84
- if (i++ < index) {
85
- current = iterator.next();
86
- continue;
87
- }
88
- result.push(current.value);
89
- current = iterator.next();
90
- }
91
- return result;
92
- }
93
-
94
- /**
95
- * Creates a shallow forwarding object without one prop. Values are exposed through
96
- * getters so compiler-emitted reactive prop accessors are not snapshotted.
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
103
- */
104
- export function exclude_prop_from_object(props, exclude_prop) {
105
- /** @type {Record<PropertyKey, unknown>} */
106
- const next = {};
107
-
108
- if (props != null) {
109
- const source = /** @type {Record<PropertyKey, unknown>} */ (props);
110
-
111
- for (const prop of Reflect.ownKeys(source)) {
112
- if (prop === exclude_prop) continue;
113
-
114
- const descriptor = get_descriptor(source, prop);
115
- if (!descriptor?.enumerable) continue;
116
-
117
- /** @type {PropertyDescriptor} */
118
- const forwarding_descriptor = {
119
- enumerable: true,
120
- configurable: true,
121
- get() {
122
- return source[prop];
123
- },
124
- };
125
-
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
- }
134
- }
135
-
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);
139
- }
1
+ export * from '@tsrx/runtime/language-helpers';
@@ -1,319 +1 @@
1
- /** @import { MergeableRef, RefProp, RefValue, SpreadProps } from '../../types/runtime/ref' */
2
-
3
- import {
4
- has_own_property,
5
- get_descriptor,
6
- has_prototype_accessor,
7
- is_array,
8
- } from '@tsrx/core/runtime/language-helpers';
9
-
10
- const REF_VALUE = Symbol();
11
-
12
- /**
13
- * Merge multiple refs (function refs and ref objects) into a single
14
- * callback ref. Used by React, Preact, and Vue targets when an element has
15
- * more than one `ref` attribute.
16
- * This is a public method and also used by the compiler to unite any refs with
17
- * any of the supported syntaxes. It does not process spreads, that is delegated to
18
- * `normalize_spread_props`.
19
- *
20
- * @template [T=Element]
21
- * @param {...MergeableRef<T>} refs
22
- * @returns {(node: T | null) => (() => void)}
23
- */
24
- export function mergeRefs(...refs) {
25
- return (node) => {
26
- /** @type {Array<() => void>} */
27
- const cleanups = [];
28
- for (const ref of refs) {
29
- if (ref == null) continue;
30
- if (typeof ref === 'function') {
31
- const result = ref(node);
32
- if (typeof result === 'function') {
33
- cleanups.push(result);
34
- } else {
35
- cleanups.push(() => ref(null));
36
- }
37
- } else if (is_ref_object(ref, 'current')) {
38
- ref.current = node;
39
- cleanups.push(() => {
40
- ref.current = null;
41
- });
42
- } else if (is_ref_object(ref, 'value')) {
43
- ref.value = node;
44
- cleanups.push(() => {
45
- ref.value = null;
46
- });
47
- }
48
- }
49
- return () => {
50
- for (const cleanup of cleanups) cleanup();
51
- };
52
- };
53
- }
54
-
55
- export { is_ref_prop as isRefProp };
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
-
69
- /**
70
- * @param {unknown} value
71
- * @returns {value is RefProp<Element>}
72
- */
73
- function is_ref_prop(value) {
74
- return typeof value === 'function' && REF_VALUE in value;
75
- }
76
-
77
- /**
78
- * @template [T=Element]
79
- * @param {RefValue<T>} ref_value
80
- * @param {T | null} node
81
- * @param {(value: T | null) => void} [set_ref_value]
82
- * @returns {void | (() => void)}
83
- */
84
- export function apply_ref_value(ref_value, node, set_ref_value) {
85
- if (is_array(ref_value)) {
86
- /** @type {Array<() => void>} */
87
- const cleanups = [];
88
- for (const item of ref_value) {
89
- const cleanup = apply_ref_value(item, node);
90
- if (typeof cleanup === 'function') {
91
- cleanups.push(cleanup);
92
- } else if (is_ref_callback(item) && node !== null) {
93
- cleanups.push(() => item(null));
94
- }
95
- }
96
- if (cleanups.length > 0) {
97
- return () => {
98
- for (const cleanup of cleanups) cleanup();
99
- };
100
- }
101
- return;
102
- }
103
-
104
- if (is_ref_callback(ref_value)) {
105
- return ref_value(node);
106
- }
107
-
108
- if (ref_value && typeof ref_value === 'object') {
109
- if (is_ref_object(ref_value, 'current')) {
110
- ref_value.current = node;
111
- return () => {
112
- ref_value.current = null;
113
- };
114
- }
115
-
116
- if (is_ref_object(ref_value, 'value')) {
117
- ref_value.value = node;
118
- return () => {
119
- ref_value.value = null;
120
- };
121
- }
122
- }
123
-
124
- if (set_ref_value !== undefined) {
125
- set_ref_value(node);
126
- }
127
- }
128
-
129
- /**
130
- * @template [T=Element]
131
- * @param {() => RefValue<T>} get_ref_value
132
- * @param {(value: T | null) => void} [set_ref_value]
133
- * @returns {RefProp<T>}
134
- */
135
- export function create_ref_prop(get_ref_value, set_ref_value) {
136
- /**
137
- * @param {T | null} node
138
- * @returns {void | (() => void)}
139
- */
140
- function ref_prop_callback(node) {
141
- const ref_value = get_ref_value();
142
- const cleanup = apply_ref_value(ref_value, node, set_ref_value);
143
- if (typeof cleanup === 'function' || node === null) {
144
- return cleanup;
145
- }
146
- return () => {
147
- apply_ref_value(ref_value, null, set_ref_value);
148
- };
149
- }
150
-
151
- Object.defineProperty(ref_prop_callback, REF_VALUE, {
152
- value: 'ref_value',
153
- enumerable: false,
154
- });
155
-
156
- return ref_prop_callback;
157
- }
158
-
159
- /**
160
- * @template [T=Element]
161
- * @param {...RefValue<T>} refs
162
- * @returns {RefValue<T>} the single surviving ref, or a callback applying all
163
- */
164
- export function merge_ref_props(...refs) {
165
- const filtered = refs.filter((ref) => ref != null);
166
-
167
- if (filtered.length === 0) {
168
- return undefined;
169
- }
170
-
171
- if (filtered.length === 1) {
172
- return filtered[0];
173
- }
174
-
175
- /**
176
- * @param {T | null} node
177
- * @returns {void | (() => void)}
178
- */
179
- function merged_ref_prop(node) {
180
- /** @type {Array<() => void>} */
181
- const cleanups = [];
182
-
183
- for (const ref of filtered) {
184
- const cleanup = apply_ref_value(ref, node);
185
- if (typeof cleanup === 'function') {
186
- cleanups.push(cleanup);
187
- } else if (is_ref_callback(ref) && node !== null) {
188
- cleanups.push(() => ref(null));
189
- }
190
- }
191
-
192
- return () => {
193
- for (const cleanup of cleanups) {
194
- cleanup();
195
- }
196
- };
197
- }
198
-
199
- return merged_ref_prop;
200
- }
201
-
202
- /**
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}
207
- */
208
- export function normalize_spread_props(props, ...outer_refs) {
209
- if (props == null) {
210
- return props;
211
- }
212
-
213
- const source = /** @type {SpreadProps} */ (props);
214
- /** @type {Array<RefValue<Element>>} */
215
- const refs = [];
216
- /** @type {SpreadProps} */
217
- const next = {};
218
- let changed = false;
219
- let existing_ref;
220
-
221
- for (const key of Reflect.ownKeys(source)) {
222
- const descriptor = get_descriptor(source, key);
223
- if (!descriptor?.enumerable) {
224
- continue;
225
- }
226
-
227
- const value = source[key];
228
-
229
- if (key === 'ref') {
230
- if (is_ref_prop(value)) {
231
- refs.push(value);
232
- changed = true;
233
- } else {
234
- existing_ref = /** @type {RefValue<Element>} */ (value);
235
- }
236
- continue;
237
- }
238
-
239
- if (is_ref_prop(value)) {
240
- refs.push(value);
241
- changed = true;
242
- continue;
243
- }
244
-
245
- next[key] = value;
246
- }
247
-
248
- if (!changed && outer_refs.length === 0) {
249
- return source;
250
- }
251
-
252
- const merged_ref = merge_ref_props(existing_ref, ...refs, ...outer_refs);
253
- if (merged_ref !== undefined) {
254
- next.ref = merged_ref;
255
- }
256
-
257
- return next;
258
- }
259
-
260
- /**
261
- * Normalize spread props for targets that read refs through an explicit
262
- * `ref={normalized.ref}` attribute. The returned `ref` stays readable for that
263
- * attribute but is non-enumerable so `{...normalized}` does not also pass it as
264
- * a DOM prop.
265
- *
266
- * @param {object | null | undefined} props
267
- * @param {...RefValue<Element>} outer_refs
268
- * @returns {SpreadProps | null | undefined}
269
- */
270
- export function normalize_spread_props_for_ref_attr(props, ...outer_refs) {
271
- const next = normalize_spread_props(props, ...outer_refs);
272
- if (next == null || !has_own_property.call(next, 'ref')) {
273
- return next;
274
- }
275
-
276
- const ref = next.ref;
277
- const without_ref = { ...next };
278
- delete without_ref.ref;
279
- Object.defineProperty(without_ref, 'ref', {
280
- value: ref,
281
- enumerable: false,
282
- configurable: true,
283
- writable: true,
284
- });
285
- return without_ref;
286
- }
287
-
288
- /**
289
- * @template {'current' | 'value'} K
290
- * @param {object} value
291
- * @param {K} key
292
- * @returns {value is Record<K, unknown>}
293
- */
294
- function is_ref_object(value, key) {
295
- if (is_dom_node(value)) {
296
- return false;
297
- }
298
- if (key === 'value' && '__v_isRef' in value) {
299
- return true;
300
- }
301
- if (has_own_property.call(value, key)) {
302
- return true;
303
- }
304
- return key === 'value' && has_prototype_accessor(value, 'value');
305
- }
306
-
307
- /**
308
- * @param {object} value
309
- * @returns {boolean}
310
- */
311
- function is_dom_node(value) {
312
- return (
313
- (typeof Node !== 'undefined' && value instanceof Node) ||
314
- ('nodeType' in value &&
315
- typeof (/** @type {{ nodeType?: unknown }} */ (value).nodeType) === 'number' &&
316
- 'nodeName' in value &&
317
- typeof (/** @type {{ nodeName?: unknown }} */ (value).nodeName) === 'string')
318
- );
319
- }
1
+ export * from '@tsrx/runtime/ref';