@tsrx/core 0.1.58 → 0.1.59
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 +4 -2
- package/src/comment-utils.js +39 -4
- package/src/runtime/iterable.js +1 -112
- package/src/runtime/language-helpers.js +1 -139
- package/src/runtime/ref.js +1 -319
- package/src/transform/jsx/helpers.js +98 -13
- package/src/transform/jsx/index.js +13 -3
- package/src/transform/segments.js +130 -16
- package/types/index.d.ts +15 -0
- package/types/jsx-platform.d.ts +19 -1
- package/types/runtime/iterable.d.ts +1 -13
- package/types/runtime/language-helpers.d.ts +1 -30
- package/types/runtime/ref.d.ts +1 -88
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.59",
|
|
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"
|
package/src/comment-utils.js
CHANGED
|
@@ -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
|
|
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
|
package/src/runtime/iterable.js
CHANGED
|
@@ -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
|
-
|
|
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';
|
package/src/runtime/ref.js
CHANGED
|
@@ -1,319 +1 @@
|
|
|
1
|
-
|
|
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';
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
/** @import * as ESRap from 'esrap' */
|
|
3
3
|
|
|
4
4
|
import tsx from 'esrap/languages/tsx';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
should_preserve_comment,
|
|
7
|
+
should_preserve_jsx_tooling_comment,
|
|
8
|
+
is_file_level_pragma,
|
|
9
|
+
format_comment,
|
|
10
|
+
} from '../../comment-utils.js';
|
|
11
|
+
import { has_location } from '../../utils/ast.js';
|
|
6
12
|
import { with_deferred_imports } from '../imports.js';
|
|
7
13
|
|
|
8
14
|
/**
|
|
@@ -69,25 +75,64 @@ export function set_node_path_metadata(node, path) {
|
|
|
69
75
|
* (structural tokens carry one-character source locations). typeOnly/volar
|
|
70
76
|
* prints opt in — their maps are consumed positionally by the language
|
|
71
77
|
* tooling and never shipped; build prints stay sparse.
|
|
72
|
-
* @param {AST.CommentWithLocation[]} [comments] Source comments
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
78
|
+
* @param {AST.CommentWithLocation[]} [comments] Source comments. In type-only
|
|
79
|
+
* output, file-wide pragmas lead the program while documentation and scoped
|
|
80
|
+
* annotations stay with their declarations, members, or statements. A sparse
|
|
81
|
+
* print with explicitly supplied comments retains its existing leading-pragma
|
|
82
|
+
* behavior. Ordinary build callers supply no comments.
|
|
77
83
|
*/
|
|
78
84
|
export function tsx_with_ts_locations(boundary_tokens = false, comments = undefined) {
|
|
79
85
|
const base = with_deferred_imports(tsx({ boundaryTokens: boundary_tokens }));
|
|
80
86
|
const { _: base_visitor, ...base_visitors } = base;
|
|
87
|
+
const preserve_comments = comments !== undefined;
|
|
88
|
+
const preserve_owner_comments = boundary_tokens && preserve_comments;
|
|
89
|
+
/** @type {Set<string> | null} */
|
|
90
|
+
const emitted_comments = preserve_comments ? new Set() : null;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {AST.CommentWithLocation} comment
|
|
94
|
+
* @param {ESRap.Context} context
|
|
95
|
+
*/
|
|
96
|
+
const write_preserved_comment = (comment, context) => {
|
|
97
|
+
if (
|
|
98
|
+
!emitted_comments ||
|
|
99
|
+
!(preserve_owner_comments
|
|
100
|
+
? should_preserve_jsx_tooling_comment(comment)
|
|
101
|
+
: should_preserve_comment(comment))
|
|
102
|
+
) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const key = `${comment.start}:${comment.end}:${comment.type}:${comment.value}`;
|
|
106
|
+
if (emitted_comments.has(key)) return;
|
|
107
|
+
emitted_comments.add(key);
|
|
108
|
+
if (comment.loc) context.location(comment.loc.start.line, comment.loc.start.column);
|
|
109
|
+
context.write(format_comment(comment));
|
|
110
|
+
if (comment.loc) context.location(comment.loc.end.line, comment.loc.end.column);
|
|
111
|
+
context.newline();
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* @param {AST.Node} node
|
|
116
|
+
* @param {ESRap.Context} context
|
|
117
|
+
*/
|
|
118
|
+
const write_leading_comments = (node, context) => {
|
|
119
|
+
if (!node.leadingComments || !is_comment_owner(node)) return;
|
|
120
|
+
for (const comment of node.leadingComments) {
|
|
121
|
+
if (has_location(comment)) write_preserved_comment(comment, context);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
81
124
|
|
|
82
125
|
const leading_preserved = (/** @type {AST.Program} */ program) => {
|
|
83
|
-
if (!comments?.length) return [];
|
|
126
|
+
if (!preserve_comments || !comments?.length) return [];
|
|
84
127
|
// Injected statements (dynamic-import/try-import prepends) carry no
|
|
85
128
|
// loc; anchor "leading" on the first statement that maps to source,
|
|
86
129
|
// else every preserved comment in the file would hoist to the top.
|
|
87
130
|
const first = program.body.find((node) => node.loc);
|
|
88
131
|
return comments.filter(
|
|
89
132
|
(comment) =>
|
|
90
|
-
|
|
133
|
+
(preserve_owner_comments
|
|
134
|
+
? is_file_level_pragma(comment)
|
|
135
|
+
: should_preserve_comment(comment)) &&
|
|
91
136
|
(first?.loc == null ||
|
|
92
137
|
(comment.loc &&
|
|
93
138
|
(comment.loc.end.line < first.loc.start.line ||
|
|
@@ -100,10 +145,7 @@ export function tsx_with_ts_locations(boundary_tokens = false, comments = undefi
|
|
|
100
145
|
const wrappers = {
|
|
101
146
|
Program: (node, context) => {
|
|
102
147
|
for (const comment of leading_preserved(node)) {
|
|
103
|
-
|
|
104
|
-
context.write(format_comment(comment));
|
|
105
|
-
if (comment.loc) context.location(comment.loc.end.line, comment.loc.end.column);
|
|
106
|
-
context.newline();
|
|
148
|
+
write_preserved_comment(comment, context);
|
|
107
149
|
}
|
|
108
150
|
/** @type {NonNullable<typeof base.Program>} */ (base.Program)(node, context);
|
|
109
151
|
},
|
|
@@ -206,8 +248,13 @@ export function tsx_with_ts_locations(boundary_tokens = false, comments = undefi
|
|
|
206
248
|
context.visit(node.body);
|
|
207
249
|
},
|
|
208
250
|
_(node, context, visit) {
|
|
251
|
+
if (preserve_owner_comments) write_leading_comments(node, context);
|
|
209
252
|
const visit_with_locations = () => {
|
|
210
|
-
if (
|
|
253
|
+
if (
|
|
254
|
+
!node.loc ||
|
|
255
|
+
(!LOCATION_WRAPPED_NODE_TYPES.has(node.type) &&
|
|
256
|
+
!(boundary_tokens && TOOLING_LOCATION_WRAPPED_NODE_TYPES.has(node.type)))
|
|
257
|
+
) {
|
|
211
258
|
visit(node);
|
|
212
259
|
return;
|
|
213
260
|
}
|
|
@@ -226,6 +273,44 @@ export function tsx_with_ts_locations(boundary_tokens = false, comments = undefi
|
|
|
226
273
|
return { ...base_visitors, ...wrappers };
|
|
227
274
|
}
|
|
228
275
|
|
|
276
|
+
/**
|
|
277
|
+
* A newline is safe before a statement/declaration or a member, but not before
|
|
278
|
+
* an arbitrary expression: `return /** @type {number} *\/ 1` must not become a
|
|
279
|
+
* bare return, and `throw` forbids a line terminator before its argument.
|
|
280
|
+
* @param {AST.Node} node
|
|
281
|
+
* @returns {boolean}
|
|
282
|
+
*/
|
|
283
|
+
function is_comment_owner(node) {
|
|
284
|
+
return (
|
|
285
|
+
node.type.endsWith('Declaration') ||
|
|
286
|
+
node.type.endsWith('Statement') ||
|
|
287
|
+
COMMENT_OWNER_NODE_TYPES.has(node.type)
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const COMMENT_OWNER_NODE_TYPES = new Set([
|
|
292
|
+
'VariableDeclarator',
|
|
293
|
+
'Property',
|
|
294
|
+
'PropertyDefinition',
|
|
295
|
+
'AccessorProperty',
|
|
296
|
+
'MethodDefinition',
|
|
297
|
+
'TSAbstractPropertyDefinition',
|
|
298
|
+
'TSAbstractAccessorProperty',
|
|
299
|
+
'TSAbstractMethodDefinition',
|
|
300
|
+
'TSPropertySignature',
|
|
301
|
+
'TSMethodSignature',
|
|
302
|
+
'TSIndexSignature',
|
|
303
|
+
'TSEnumMember',
|
|
304
|
+
'TSExportAssignment',
|
|
305
|
+
]);
|
|
306
|
+
|
|
307
|
+
const TOOLING_LOCATION_WRAPPED_NODE_TYPES = new Set([
|
|
308
|
+
'ExportNamedDeclaration',
|
|
309
|
+
'ExportDefaultDeclaration',
|
|
310
|
+
'ExportAllDeclaration',
|
|
311
|
+
'TSPropertySignature',
|
|
312
|
+
]);
|
|
313
|
+
|
|
229
314
|
// Be careful when adding visitors that are already defined in `wrappers`.
|
|
230
315
|
// JSXOpeningElement is intentionally in both places: its custom printer still
|
|
231
316
|
// needs a location marker around the whole node.
|
|
@@ -561,7 +561,17 @@ export function createJsxTransform(platform) {
|
|
|
561
561
|
* @returns {JsxTransformResult}
|
|
562
562
|
*/
|
|
563
563
|
function transform(ast, source, filename, options) {
|
|
564
|
-
const
|
|
564
|
+
const effective_platform =
|
|
565
|
+
options?.runtimeImports === 'direct' && platform.directRuntimeImports
|
|
566
|
+
? {
|
|
567
|
+
...platform,
|
|
568
|
+
imports: {
|
|
569
|
+
...platform.imports,
|
|
570
|
+
...platform.directRuntimeImports,
|
|
571
|
+
},
|
|
572
|
+
}
|
|
573
|
+
: platform;
|
|
574
|
+
const suspense_source = options?.suspenseSource ?? effective_platform.imports.suspense;
|
|
565
575
|
const collect = !!(options?.collect || options?.loose);
|
|
566
576
|
/** @type {AST.CSS.StyleSheet[]} */
|
|
567
577
|
const stylesheets = [];
|
|
@@ -570,7 +580,7 @@ export function createJsxTransform(platform) {
|
|
|
570
580
|
|
|
571
581
|
/** @type {TransformContext} */
|
|
572
582
|
const transform_context = {
|
|
573
|
-
platform,
|
|
583
|
+
platform: effective_platform,
|
|
574
584
|
local_statement_component_index: 0,
|
|
575
585
|
needs_error_boundary: false,
|
|
576
586
|
needs_suspense: false,
|
|
@@ -811,7 +821,7 @@ export function createJsxTransform(platform) {
|
|
|
811
821
|
if (platform.hooks?.injectImports) {
|
|
812
822
|
platform.hooks.injectImports(expanded, transform_context, suspense_source);
|
|
813
823
|
} else {
|
|
814
|
-
inject_try_imports(expanded, transform_context,
|
|
824
|
+
inject_try_imports(expanded, transform_context, effective_platform, suspense_source);
|
|
815
825
|
}
|
|
816
826
|
|
|
817
827
|
// Lower any `@{ … }` code blocks left in generated helper bodies before the
|
|
@@ -34,11 +34,19 @@ import {
|
|
|
34
34
|
build_line_offsets,
|
|
35
35
|
get_mapping_from_node,
|
|
36
36
|
} from '../source-map-utils.js';
|
|
37
|
-
import {
|
|
37
|
+
import { should_preserve_jsx_tooling_comment, format_comment } from '../comment-utils.js';
|
|
38
38
|
import { has_location } from '../utils/ast.js';
|
|
39
39
|
|
|
40
40
|
const LAZY_PARAM_IDENTIFIER_REGEX = /^__lazy\d+$/;
|
|
41
41
|
const RETURN_KEYWORD = 'return';
|
|
42
|
+
const EXPORT_KEYWORD = 'export';
|
|
43
|
+
const BLOCK_DECLARATION_TYPES = new Set([
|
|
44
|
+
'FunctionDeclaration',
|
|
45
|
+
'ClassDeclaration',
|
|
46
|
+
'TSInterfaceDeclaration',
|
|
47
|
+
'TSEnumDeclaration',
|
|
48
|
+
'TSModuleDeclaration',
|
|
49
|
+
]);
|
|
42
50
|
|
|
43
51
|
/**
|
|
44
52
|
* @param {string} value
|
|
@@ -418,6 +426,88 @@ export function convert_source_map_to_mappings(
|
|
|
418
426
|
return candidates[Math.min(index, candidates.length - 1)];
|
|
419
427
|
}
|
|
420
428
|
|
|
429
|
+
/**
|
|
430
|
+
* A comment's end can share a source coordinate with the next declaration,
|
|
431
|
+
* and synthetic file pragmas can share offset zero with the first export.
|
|
432
|
+
* Select the position that actually prints this node's opening text instead
|
|
433
|
+
* of treating the first source-map entry as an unambiguous boundary.
|
|
434
|
+
* @param {AST.Position} position
|
|
435
|
+
* @param {string} text
|
|
436
|
+
* @returns {number | undefined}
|
|
437
|
+
*/
|
|
438
|
+
function generated_offset_for_text(position, text) {
|
|
439
|
+
const positions = src_to_gen_map.get(`${position.line}:${position.column}`);
|
|
440
|
+
for (const generated of positions ?? []) {
|
|
441
|
+
const offset = loc_to_offset(generated.line, generated.column, gen_line_offsets);
|
|
442
|
+
if (generated_code.startsWith(text, offset)) return offset;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* @param {AST.NodeWithLocation} node
|
|
448
|
+
* @param {string} start_text
|
|
449
|
+
* @returns {CodeMapping | undefined}
|
|
450
|
+
*/
|
|
451
|
+
function declaration_mapping(node, start_text) {
|
|
452
|
+
const start = generated_offset_for_text(node.loc.start, start_text);
|
|
453
|
+
if (start === undefined) return;
|
|
454
|
+
const positions = src_to_gen_map.get(`${node.loc.end.line}:${node.loc.end.column}`);
|
|
455
|
+
for (const generated of positions ?? []) {
|
|
456
|
+
const end = loc_to_offset(generated.line, generated.column, gen_line_offsets);
|
|
457
|
+
if (end < start) continue;
|
|
458
|
+
return {
|
|
459
|
+
sourceOffsets: [node.start],
|
|
460
|
+
lengths: [node.end - node.start],
|
|
461
|
+
generatedOffsets: [start],
|
|
462
|
+
generatedLengths: [end - start],
|
|
463
|
+
data: { ...mapping_data_verify_only, customData: {} },
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** @param {AST.ExportNamedDeclaration | AST.ExportDefaultDeclaration | AST.ExportAllDeclaration} node */
|
|
469
|
+
function add_export_mapping(node) {
|
|
470
|
+
if (!has_location(node)) return;
|
|
471
|
+
const mapping = declaration_mapping(node, EXPORT_KEYWORD);
|
|
472
|
+
if (mapping) {
|
|
473
|
+
const declaration = node.type === 'ExportAllDeclaration' ? null : node.declaration;
|
|
474
|
+
const end = mapping.generatedOffsets[0] + mapping.generatedLengths[0];
|
|
475
|
+
// A semicolon-free source declaration shares its end with its last
|
|
476
|
+
// expression. The first map entry then precedes the statement's emitted
|
|
477
|
+
// semicolon. Block declarations are different: a following `;` is an
|
|
478
|
+
// empty statement, not part of TypeScript's declaration range.
|
|
479
|
+
if (
|
|
480
|
+
generated_code[end] === ';' &&
|
|
481
|
+
(!declaration || !BLOCK_DECLARATION_TYPES.has(declaration.type))
|
|
482
|
+
) {
|
|
483
|
+
mapping.generatedLengths[0]++;
|
|
484
|
+
}
|
|
485
|
+
// Full declaration queries need both endpoints in the same Volar
|
|
486
|
+
// mapping, not a linear claim over the generated body. A transformed
|
|
487
|
+
// component can contain synthetic imports, tags, and helper calls that
|
|
488
|
+
// must not acquire source locations merely because it is exported.
|
|
489
|
+
const generated_start = mapping.generatedOffsets[0];
|
|
490
|
+
const generated_end = generated_start + mapping.generatedLengths[0];
|
|
491
|
+
mapping.sourceOffsets = [node.start, node.end];
|
|
492
|
+
mapping.generatedOffsets = [generated_start, generated_end];
|
|
493
|
+
mapping.lengths = [0, 0];
|
|
494
|
+
mapping.generatedLengths = [0, 0];
|
|
495
|
+
mappings.push(mapping);
|
|
496
|
+
}
|
|
497
|
+
tokens.push({
|
|
498
|
+
source: EXPORT_KEYWORD,
|
|
499
|
+
generated: EXPORT_KEYWORD,
|
|
500
|
+
loc: {
|
|
501
|
+
start: node.loc.start,
|
|
502
|
+
end: {
|
|
503
|
+
line: node.loc.start.line,
|
|
504
|
+
column: node.loc.start.column + EXPORT_KEYWORD.length,
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
metadata: {},
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
421
511
|
/**
|
|
422
512
|
* Needed for a mapping that includes the computed brackets for diagnostics
|
|
423
513
|
* @param {AST.MethodDefinition | AST.Property} node
|
|
@@ -464,25 +554,24 @@ export function convert_source_map_to_mappings(
|
|
|
464
554
|
if (!Array.isArray(comments)) continue;
|
|
465
555
|
|
|
466
556
|
for (const comment of comments) {
|
|
467
|
-
if (!has_location(comment) || !
|
|
557
|
+
if (!has_location(comment) || !should_preserve_jsx_tooling_comment(comment)) continue;
|
|
468
558
|
|
|
469
559
|
const comment_key = `${comment.start}:${comment.end}`;
|
|
470
560
|
if (mapped_comments.has(comment_key)) continue;
|
|
471
561
|
mapped_comments.add(comment_key);
|
|
472
562
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
}
|
|
563
|
+
const text = format_comment(comment);
|
|
564
|
+
const start = generated_offset_for_text(comment.loc.start, text);
|
|
565
|
+
// A stripped or moved comment must not claim another node's shared
|
|
566
|
+
// source coordinate. The printer writes this exact formatted text.
|
|
567
|
+
if (start === undefined) continue;
|
|
568
|
+
mappings.push({
|
|
569
|
+
sourceOffsets: [comment.start],
|
|
570
|
+
lengths: [comment.end - comment.start],
|
|
571
|
+
generatedOffsets: [start],
|
|
572
|
+
generatedLengths: [text.length],
|
|
573
|
+
data: { ...mapping_data_verify_only, customData: {} },
|
|
574
|
+
});
|
|
486
575
|
}
|
|
487
576
|
}
|
|
488
577
|
}
|
|
@@ -704,6 +793,7 @@ export function convert_source_map_to_mappings(
|
|
|
704
793
|
}
|
|
705
794
|
return;
|
|
706
795
|
} else if (node.type === 'ExportNamedDeclaration') {
|
|
796
|
+
add_export_mapping(node);
|
|
707
797
|
if (node.specifiers && node.specifiers.length > 0) {
|
|
708
798
|
for (const specifier of node.specifiers) {
|
|
709
799
|
visit(specifier);
|
|
@@ -715,12 +805,14 @@ export function convert_source_map_to_mappings(
|
|
|
715
805
|
}
|
|
716
806
|
return;
|
|
717
807
|
} else if (node.type === 'ExportDefaultDeclaration') {
|
|
808
|
+
add_export_mapping(node);
|
|
718
809
|
// Visit the declaration
|
|
719
810
|
if (node.declaration) {
|
|
720
811
|
visit(/** @type {AST.Node} */ (node.declaration));
|
|
721
812
|
}
|
|
722
813
|
return;
|
|
723
814
|
} else if (node.type === 'ExportAllDeclaration') {
|
|
815
|
+
add_export_mapping(node);
|
|
724
816
|
// Nothing to visit (just source string)
|
|
725
817
|
return;
|
|
726
818
|
} else if (node.type === 'JSXOpeningElement') {
|
|
@@ -1908,6 +2000,24 @@ export function convert_source_map_to_mappings(
|
|
|
1908
2000
|
}
|
|
1909
2001
|
return;
|
|
1910
2002
|
} else if (node.type === 'TSPropertySignature') {
|
|
2003
|
+
if (has_location(node)) {
|
|
2004
|
+
const start_text = node.readonly
|
|
2005
|
+
? 'readonly'
|
|
2006
|
+
: node.computed
|
|
2007
|
+
? '['
|
|
2008
|
+
: node.key.type === 'Identifier'
|
|
2009
|
+
? node.key.name
|
|
2010
|
+
: source.slice(node.start, node.key.end);
|
|
2011
|
+
const mapping = declaration_mapping(node, start_text);
|
|
2012
|
+
if (mapping) {
|
|
2013
|
+
const end = mapping.generatedOffsets[0] + mapping.generatedLengths[0];
|
|
2014
|
+
// esrap's containing type/interface prints member separators
|
|
2015
|
+
// after the property's own end marker. TS includes that `;`
|
|
2016
|
+
// in its PropertySignature range, even when it was not authored.
|
|
2017
|
+
if (generated_code[end] === ';') mapping.generatedLengths[0]++;
|
|
2018
|
+
mappings.push(mapping);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
1911
2021
|
// Property signature in type
|
|
1912
2022
|
if (node.key) {
|
|
1913
2023
|
visit(node.key);
|
|
@@ -2354,7 +2464,11 @@ export function convert_source_map_to_mappings(
|
|
|
2354
2464
|
|
|
2355
2465
|
// Add a mapping for the very beginning of the file to handle import additions
|
|
2356
2466
|
// This ensures that code actions adding imports at the top work correctly
|
|
2357
|
-
if (
|
|
2467
|
+
if (
|
|
2468
|
+
!isImportDeclarationPresent &&
|
|
2469
|
+
mappings.length > 0 &&
|
|
2470
|
+
(mappings[0].sourceOffsets[0] > 0 || mappings[0].generatedOffsets[0] > 0)
|
|
2471
|
+
) {
|
|
2358
2472
|
mappings.unshift({
|
|
2359
2473
|
sourceOffsets: [0],
|
|
2360
2474
|
generatedOffsets: [0],
|
package/types/index.d.ts
CHANGED
|
@@ -2218,6 +2218,14 @@ export interface VolarCompileOptions extends Omit<ParseOptions, 'errors' | 'comm
|
|
|
2218
2218
|
dev?: boolean;
|
|
2219
2219
|
}
|
|
2220
2220
|
|
|
2221
|
+
/**
|
|
2222
|
+
* Selects where generated runtime helper imports resolve from. Direct mode
|
|
2223
|
+
* emits bare imports from the target's standalone runtime package; the package
|
|
2224
|
+
* that owns the generated modules must declare that runtime as a direct
|
|
2225
|
+
* production dependency.
|
|
2226
|
+
*/
|
|
2227
|
+
export type RuntimeImportMode = 'compiler' | 'direct';
|
|
2228
|
+
|
|
2221
2229
|
/**
|
|
2222
2230
|
* Common base options accepted by every TSRX target's `compile` entry point.
|
|
2223
2231
|
* Targets that need extra knobs (e.g. ripple's `mode`/`dev`/`hmr`, preact's
|
|
@@ -2227,6 +2235,13 @@ export interface VolarCompileOptions extends Omit<ParseOptions, 'errors' | 'comm
|
|
|
2227
2235
|
export interface BaseCompileOptions {
|
|
2228
2236
|
collect?: boolean;
|
|
2229
2237
|
loose?: boolean;
|
|
2238
|
+
/**
|
|
2239
|
+
* Selects where generated runtime helper imports resolve from. The default
|
|
2240
|
+
* `'compiler'` mode preserves compiler-package compatibility subpaths;
|
|
2241
|
+
* `'direct'` targets the renderer's small runtime package, which the package
|
|
2242
|
+
* owning the generated modules must declare as a direct production dependency.
|
|
2243
|
+
*/
|
|
2244
|
+
runtimeImports?: RuntimeImportMode;
|
|
2230
2245
|
}
|
|
2231
2246
|
|
|
2232
2247
|
/**
|
package/types/jsx-platform.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type * as AST from 'estree';
|
|
2
2
|
import type * as ESTreeJSX from 'estree-jsx';
|
|
3
3
|
import type { RawSourceMap } from 'source-map';
|
|
4
|
-
import type { CompileError, JsxHelperComponent, JsxHelperState } from './index';
|
|
4
|
+
import type { CompileError, JsxHelperComponent, JsxHelperState, RuntimeImportMode } from './index';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Result returned by a JSX platform transform (React, Preact, Solid).
|
|
@@ -82,6 +82,13 @@ export interface JsxTransformContext {
|
|
|
82
82
|
* Optional per-call compile options passed to a created JSX transform.
|
|
83
83
|
*/
|
|
84
84
|
export interface JsxTransformOptions {
|
|
85
|
+
/**
|
|
86
|
+
* Selects compiler-package compatibility imports or direct renderer runtime
|
|
87
|
+
* package imports. Defaults to `'compiler'`. Direct mode requires the package
|
|
88
|
+
* owning generated modules to declare the target runtime as a direct
|
|
89
|
+
* production dependency.
|
|
90
|
+
*/
|
|
91
|
+
runtimeImports?: RuntimeImportMode;
|
|
85
92
|
/**
|
|
86
93
|
* Override the import source used for `Suspense` in try-block transforms.
|
|
87
94
|
* Falls back to `platform.imports.suspense`. Preact uses this to let the
|
|
@@ -412,6 +419,17 @@ export interface JsxPlatform {
|
|
|
412
419
|
forOfIterableHelper?: string;
|
|
413
420
|
};
|
|
414
421
|
|
|
422
|
+
/**
|
|
423
|
+
* Runtime-helper import sources used when a compile call opts into direct
|
|
424
|
+
* runtime imports. Fields omitted here continue using `imports`.
|
|
425
|
+
*/
|
|
426
|
+
directRuntimeImports?: {
|
|
427
|
+
errorBoundary?: string;
|
|
428
|
+
mergeRefs?: string;
|
|
429
|
+
refProp?: string;
|
|
430
|
+
forOfIterableHelper?: string;
|
|
431
|
+
};
|
|
432
|
+
|
|
415
433
|
jsx: {
|
|
416
434
|
/**
|
|
417
435
|
* Rewrite Ripple's `class` attribute to `className` for legacy targets
|
|
@@ -1,13 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// example: IterationValue<typeof something>
|
|
3
|
-
export type IterationValue<T> = T extends readonly unknown[]
|
|
4
|
-
? T[number]
|
|
5
|
-
: T extends Iterable<infer U>
|
|
6
|
-
? U
|
|
7
|
-
: never;
|
|
8
|
-
|
|
9
|
-
export function map_iterable<T, U>(
|
|
10
|
-
value: Iterable<T>,
|
|
11
|
-
fn: (item: T, index: number, is_last: boolean) => U,
|
|
12
|
-
tail?: () => U | U[],
|
|
13
|
-
): U[];
|
|
1
|
+
export * from '@tsrx/runtime/iterable';
|
|
@@ -1,30 +1 @@
|
|
|
1
|
-
export
|
|
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<T>(array_like: ArrayLike<T>, ...args: number[]): T[];
|
|
18
|
-
export function iterable_array_from<T>(
|
|
19
|
-
iterable: Iterable<T> | Iterator<T> | ArrayLike<T>,
|
|
20
|
-
index?: number,
|
|
21
|
-
): T[];
|
|
22
|
-
/**
|
|
23
|
-
* The props bag minus one prop. Constrained to `object` rather than an index
|
|
24
|
-
* signature so an interface- or class-typed props bag is accepted; returns `{}`
|
|
25
|
-
* when `props` is nullish.
|
|
26
|
-
*/
|
|
27
|
-
export function exclude_prop_from_object<
|
|
28
|
-
T extends object = Record<PropertyKey, unknown>,
|
|
29
|
-
K extends PropertyKey = PropertyKey,
|
|
30
|
-
>(props: T | null | undefined, exclude_prop: K): Omit<T, K>;
|
|
1
|
+
export * from '@tsrx/runtime/language-helpers';
|
package/types/runtime/ref.d.ts
CHANGED
|
@@ -1,88 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
bivarianceHack(node: T | null): void | (() => void);
|
|
3
|
-
}['bivarianceHack'];
|
|
4
|
-
export type MergeableRefObject<T> = { current: T | null };
|
|
5
|
-
export type MergeableVueRef<T> = { value: T | null };
|
|
6
|
-
export type RefProp<T = unknown> = (node: T | null) => void | (() => void);
|
|
7
|
-
export type RefValue<T = Element> =
|
|
8
|
-
| ((node: T) => void | (() => void))
|
|
9
|
-
| readonly RefValue<T>[]
|
|
10
|
-
| { current: T | null }
|
|
11
|
-
| { value: T | null }
|
|
12
|
-
| T
|
|
13
|
-
| null
|
|
14
|
-
| undefined;
|
|
15
|
-
|
|
16
|
-
export type MergeableRef<T> =
|
|
17
|
-
MergeableRefCallback<T> | MergeableRefObject<T> | MergeableVueRef<T> | null | undefined;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* A props bag as it reaches a spread: keys the caller has not narrowed.
|
|
21
|
-
*
|
|
22
|
-
* Accepting bag is spelled `object` at the parameter position — an index
|
|
23
|
-
* signature would reject an interface- or class-typed props bag — while this
|
|
24
|
-
* is what the helpers hand back when they rebuild one.
|
|
25
|
-
*/
|
|
26
|
-
export type SpreadProps = Record<PropertyKey, unknown>;
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* The node a ref value points at, derived from the ref's own type in the order
|
|
30
|
-
* the runtime resolves it (see `apply_ref_value` and `is_ref_object`): a list
|
|
31
|
-
* resolves through its entries, a callback through its parameter, and a DOM
|
|
32
|
-
* node is never treated as a ref object.
|
|
33
|
-
*
|
|
34
|
-
* Inferring through `RefValue<T>` instead would read the target off the wrong
|
|
35
|
-
* union member: TypeScript prefers a structural member over a naked type
|
|
36
|
-
* parameter, so `HTMLInputElement` would match the `{ value: T | null }`
|
|
37
|
-
* (Vue ref) branch and resolve `T` to `string`.
|
|
38
|
-
*/
|
|
39
|
-
export type RefTarget<V> = [V] extends [never]
|
|
40
|
-
? never
|
|
41
|
-
: V extends null | undefined
|
|
42
|
-
? never
|
|
43
|
-
: V extends (node: infer N) => unknown
|
|
44
|
-
? N
|
|
45
|
-
: V extends readonly (infer E)[]
|
|
46
|
-
? RefTarget<E>
|
|
47
|
-
: V extends Node
|
|
48
|
-
? V
|
|
49
|
-
: V extends { current: infer C }
|
|
50
|
-
? NonNullable<C>
|
|
51
|
-
: V extends { value: infer C }
|
|
52
|
-
? NonNullable<C>
|
|
53
|
-
: V;
|
|
54
|
-
|
|
55
|
-
export function mergeRefs<T = Element>(
|
|
56
|
-
...refs: Array<MergeableRef<T>>
|
|
57
|
-
): (node: T | null) => () => void;
|
|
58
|
-
export function isRefProp(value: unknown): boolean;
|
|
59
|
-
// The inference overload comes first so an inferred call resolves the target
|
|
60
|
-
// through `RefTarget`; the `T` overload below still serves explicit type
|
|
61
|
-
// arguments, which is what every compiler-generated call passes.
|
|
62
|
-
export function create_ref_prop<V>(
|
|
63
|
-
get_ref_value: () => V,
|
|
64
|
-
set_ref_value?: (value: RefTarget<V> | null) => void,
|
|
65
|
-
): RefProp<RefTarget<V>>;
|
|
66
|
-
export function create_ref_prop<T = Element>(
|
|
67
|
-
get_ref_value: () => RefValue<T>,
|
|
68
|
-
set_ref_value?: (value: T | null) => void,
|
|
69
|
-
): RefProp<T>;
|
|
70
|
-
export function apply_ref_value<V>(
|
|
71
|
-
ref_value: V,
|
|
72
|
-
node: RefTarget<V> | null,
|
|
73
|
-
set_ref_value?: (value: RefTarget<V> | null) => void,
|
|
74
|
-
): void | (() => void);
|
|
75
|
-
export function apply_ref_value<T = Element>(
|
|
76
|
-
ref_value: RefValue<T>,
|
|
77
|
-
node: T | null,
|
|
78
|
-
set_ref_value?: (value: T | null) => void,
|
|
79
|
-
): void | (() => void);
|
|
80
|
-
export function merge_ref_props<T = Element>(...refs: Array<RefValue<T>>): RefValue<T>;
|
|
81
|
-
export function normalize_spread_props<T extends object | null | undefined>(
|
|
82
|
-
props: T,
|
|
83
|
-
...outer_refs: Array<RefValue<Element>>
|
|
84
|
-
): T | SpreadProps;
|
|
85
|
-
export function normalize_spread_props_for_ref_attr<T extends object | null | undefined>(
|
|
86
|
-
props: T,
|
|
87
|
-
...outer_refs: Array<RefValue<Element>>
|
|
88
|
-
): T | SpreadProps;
|
|
1
|
+
export * from '@tsrx/runtime/ref';
|