@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 +4 -2
- package/src/analyze/prune.js +7 -1
- 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 +89 -19
- package/src/transform/segments.js +131 -17
- package/types/index.d.ts +19 -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.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"
|
package/src/analyze/prune.js
CHANGED
|
@@ -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
|
}
|
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';
|