@esportsplus/reactivity 0.24.4 → 0.25.0
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/build/reactive/index.d.ts +9 -4
- package/build/transformer/index.d.ts +6 -4
- package/build/transformer/index.js +39 -106
- package/build/transformer/plugins/vite.js +4 -7
- package/build/transformer/transforms/array.d.ts +2 -2
- package/build/transformer/transforms/array.js +22 -20
- package/build/transformer/transforms/object.d.ts +3 -9
- package/build/transformer/transforms/object.js +99 -88
- package/build/transformer/transforms/primitives.d.ts +3 -3
- package/build/transformer/transforms/primitives.js +184 -131
- package/build/transformer/transforms/utilities.d.ts +8 -0
- package/build/transformer/transforms/utilities.js +27 -0
- package/build/types.d.ts +7 -6
- package/package.json +1 -1
- package/src/reactive/index.ts +24 -6
- package/src/transformer/index.ts +45 -187
- package/src/transformer/plugins/vite.ts +5 -13
- package/src/transformer/transforms/array.ts +31 -35
- package/src/transformer/transforms/object.ts +137 -292
- package/src/transformer/transforms/primitives.ts +236 -230
- package/src/transformer/transforms/utilities.ts +45 -0
- package/src/types.ts +9 -8
- package/test/vite.config.ts +1 -1
- package/build/transformer/factory.d.ts +0 -13
- package/build/transformer/factory.js +0 -36
- package/src/transformer/factory.ts +0 -141
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { uid } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import {
|
|
2
|
+
import { addMissingImports, applyReplacements } from './utilities.js';
|
|
3
3
|
import { ts } from '@esportsplus/typescript';
|
|
4
4
|
function classifyReactiveArg(arg) {
|
|
5
5
|
if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
|
|
@@ -22,14 +22,14 @@ function findBinding(bindings, name, node) {
|
|
|
22
22
|
function findEnclosingScope(node) {
|
|
23
23
|
let current = node.parent;
|
|
24
24
|
while (current) {
|
|
25
|
-
if (ts.
|
|
26
|
-
ts.
|
|
27
|
-
ts.isForInStatement(current) ||
|
|
28
|
-
ts.isForOfStatement(current) ||
|
|
29
|
-
ts.isForStatement(current) ||
|
|
25
|
+
if (ts.isBlock(current) ||
|
|
26
|
+
ts.isSourceFile(current) ||
|
|
30
27
|
ts.isFunctionDeclaration(current) ||
|
|
31
28
|
ts.isFunctionExpression(current) ||
|
|
32
|
-
ts.
|
|
29
|
+
ts.isArrowFunction(current) ||
|
|
30
|
+
ts.isForStatement(current) ||
|
|
31
|
+
ts.isForInStatement(current) ||
|
|
32
|
+
ts.isForOfStatement(current)) {
|
|
33
33
|
return current;
|
|
34
34
|
}
|
|
35
35
|
current = current.parent;
|
|
@@ -38,72 +38,64 @@ function findEnclosingScope(node) {
|
|
|
38
38
|
}
|
|
39
39
|
function getCompoundOperator(kind) {
|
|
40
40
|
if (kind === ts.SyntaxKind.PlusEqualsToken) {
|
|
41
|
-
return
|
|
41
|
+
return '+';
|
|
42
42
|
}
|
|
43
43
|
else if (kind === ts.SyntaxKind.MinusEqualsToken) {
|
|
44
|
-
return
|
|
44
|
+
return '-';
|
|
45
45
|
}
|
|
46
46
|
else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
|
|
47
|
-
return
|
|
47
|
+
return '*';
|
|
48
48
|
}
|
|
49
49
|
else if (kind === ts.SyntaxKind.SlashEqualsToken) {
|
|
50
|
-
return
|
|
50
|
+
return '/';
|
|
51
51
|
}
|
|
52
52
|
else if (kind === ts.SyntaxKind.PercentEqualsToken) {
|
|
53
|
-
return
|
|
53
|
+
return '%';
|
|
54
54
|
}
|
|
55
55
|
else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
|
|
56
|
-
return
|
|
56
|
+
return '**';
|
|
57
57
|
}
|
|
58
58
|
else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
|
|
59
|
-
return
|
|
59
|
+
return '&';
|
|
60
60
|
}
|
|
61
61
|
else if (kind === ts.SyntaxKind.BarEqualsToken) {
|
|
62
|
-
return
|
|
62
|
+
return '|';
|
|
63
63
|
}
|
|
64
64
|
else if (kind === ts.SyntaxKind.CaretEqualsToken) {
|
|
65
|
-
return
|
|
65
|
+
return '^';
|
|
66
66
|
}
|
|
67
67
|
else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
|
|
68
|
-
return
|
|
68
|
+
return '<<';
|
|
69
69
|
}
|
|
70
70
|
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
|
|
71
|
-
return
|
|
71
|
+
return '>>';
|
|
72
72
|
}
|
|
73
73
|
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
|
|
74
|
-
return
|
|
74
|
+
return '>>>';
|
|
75
75
|
}
|
|
76
76
|
else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
|
|
77
|
-
return
|
|
77
|
+
return '&&';
|
|
78
78
|
}
|
|
79
79
|
else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
|
|
80
|
-
return
|
|
80
|
+
return '||';
|
|
81
81
|
}
|
|
82
82
|
else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
83
|
-
return
|
|
83
|
+
return '??';
|
|
84
84
|
}
|
|
85
85
|
else {
|
|
86
|
-
return
|
|
86
|
+
return '+';
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
97
|
-
kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
98
|
-
kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
99
|
-
return 'compound';
|
|
89
|
+
function isInComputedRange(ranges, start, end) {
|
|
90
|
+
for (let i = 0, n = ranges.length; i < n; i++) {
|
|
91
|
+
let r = ranges[i];
|
|
92
|
+
if (start >= r.start && end <= r.end) {
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
100
95
|
}
|
|
101
96
|
return false;
|
|
102
97
|
}
|
|
103
98
|
function isInDeclarationInit(node) {
|
|
104
|
-
if (!node || !node.parent) {
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
99
|
let parent = node.parent;
|
|
108
100
|
if (ts.isVariableDeclaration(parent) && parent.initializer === node) {
|
|
109
101
|
return true;
|
|
@@ -121,14 +113,41 @@ function isInScope(reference, binding) {
|
|
|
121
113
|
return false;
|
|
122
114
|
}
|
|
123
115
|
function isReactiveReassignment(node) {
|
|
124
|
-
let
|
|
125
|
-
if (ts.
|
|
126
|
-
ts.
|
|
127
|
-
right
|
|
116
|
+
let parent = node.parent;
|
|
117
|
+
if (ts.isBinaryExpression(parent) &&
|
|
118
|
+
parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
119
|
+
parent.right === node &&
|
|
120
|
+
ts.isCallExpression(node) &&
|
|
121
|
+
ts.isIdentifier(node.expression) &&
|
|
122
|
+
node.expression.text === 'reactive') {
|
|
128
123
|
return true;
|
|
129
124
|
}
|
|
130
125
|
return false;
|
|
131
126
|
}
|
|
127
|
+
function isWriteContext(node) {
|
|
128
|
+
let parent = node.parent;
|
|
129
|
+
if (ts.isBinaryExpression(parent) && parent.left === node) {
|
|
130
|
+
let op = parent.operatorToken.kind;
|
|
131
|
+
if (op === ts.SyntaxKind.EqualsToken) {
|
|
132
|
+
return 'simple';
|
|
133
|
+
}
|
|
134
|
+
if (op >= ts.SyntaxKind.PlusEqualsToken && op <= ts.SyntaxKind.CaretEqualsToken) {
|
|
135
|
+
return 'compound';
|
|
136
|
+
}
|
|
137
|
+
if (op === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
138
|
+
op === ts.SyntaxKind.BarBarEqualsToken ||
|
|
139
|
+
op === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
140
|
+
return 'compound';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (ts.isPostfixUnaryExpression(parent) || ts.isPrefixUnaryExpression(parent)) {
|
|
144
|
+
let op = parent.operator;
|
|
145
|
+
if (op === ts.SyntaxKind.PlusPlusToken || op === ts.SyntaxKind.MinusMinusToken) {
|
|
146
|
+
return 'increment';
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
132
151
|
function visit(ctx, node) {
|
|
133
152
|
if (ts.isImportDeclaration(node) &&
|
|
134
153
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
@@ -151,132 +170,166 @@ function visit(ctx, node) {
|
|
|
151
170
|
let arg = node.arguments[0], classification = classifyReactiveArg(arg);
|
|
152
171
|
if (classification) {
|
|
153
172
|
let varName = null;
|
|
154
|
-
if (
|
|
173
|
+
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
155
174
|
varName = node.parent.name.text;
|
|
156
175
|
}
|
|
157
|
-
else if (node.parent &&
|
|
158
|
-
ts.isBinaryExpression(node.parent) &&
|
|
176
|
+
else if (ts.isBinaryExpression(node.parent) &&
|
|
159
177
|
node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
160
178
|
ts.isIdentifier(node.parent.left)) {
|
|
161
179
|
varName = node.parent.left.text;
|
|
162
180
|
}
|
|
163
181
|
if (varName) {
|
|
164
182
|
let scope = findEnclosingScope(node);
|
|
165
|
-
ctx.bindings.set(varName, classification);
|
|
166
183
|
ctx.scopedBindings.push({ name: varName, scope, type: classification });
|
|
184
|
+
ctx.bindings.set(varName, classification);
|
|
167
185
|
}
|
|
168
186
|
if (classification === 'computed') {
|
|
187
|
+
ctx.computedArgRanges.push({
|
|
188
|
+
end: arg.end,
|
|
189
|
+
start: arg.getStart(ctx.sourceFile)
|
|
190
|
+
});
|
|
191
|
+
let argCtx = {
|
|
192
|
+
argStart: arg.getStart(ctx.sourceFile),
|
|
193
|
+
innerReplacements: [],
|
|
194
|
+
neededImports: ctx.neededImports,
|
|
195
|
+
scopedBindings: ctx.scopedBindings,
|
|
196
|
+
sourceFile: ctx.sourceFile
|
|
197
|
+
};
|
|
198
|
+
visitArg(argCtx, arg);
|
|
199
|
+
let argText = applyReplacements(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
|
|
200
|
+
ctx.replacements.push({
|
|
201
|
+
end: node.end,
|
|
202
|
+
newText: `computed(${argText})`,
|
|
203
|
+
start: node.pos
|
|
204
|
+
});
|
|
169
205
|
ctx.neededImports.add('computed');
|
|
170
|
-
let transformedArg = ts.visitEachChild(arg, n => visitComputedArg(ctx, n), ctx.context);
|
|
171
|
-
return createComputedCall(ctx.factory, ctx.ns.reactivity, transformedArg);
|
|
172
206
|
}
|
|
173
207
|
else {
|
|
208
|
+
let argText = arg.getText(ctx.sourceFile);
|
|
209
|
+
ctx.replacements.push({
|
|
210
|
+
end: node.end,
|
|
211
|
+
newText: `signal(${argText})`,
|
|
212
|
+
start: node.pos
|
|
213
|
+
});
|
|
174
214
|
ctx.neededImports.add('signal');
|
|
175
|
-
return createSignalCall(ctx.factory, ctx.ns.reactivity, arg);
|
|
176
215
|
}
|
|
177
216
|
}
|
|
178
217
|
}
|
|
179
|
-
if (ts.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (binding && binding.type !== 'computed' && !isReactiveReassignment(node)) {
|
|
184
|
-
ctx.neededImports.add('set');
|
|
185
|
-
let factory = ctx.factory, name = node.left.text, signalIdent = factory.createIdentifier(name), transformedRight = ts.visitEachChild(node.right, n => visit(ctx, n), ctx.context);
|
|
186
|
-
if (assignType === 'simple') {
|
|
187
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, transformedRight);
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
let op = getCompoundOperator(node.operatorToken.kind), valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
191
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, op, transformedRight));
|
|
192
|
-
}
|
|
193
|
-
}
|
|
218
|
+
if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
|
|
219
|
+
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
220
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
221
|
+
return;
|
|
194
222
|
}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
|
|
200
|
-
if (binding && binding.type !== 'computed') {
|
|
201
|
-
ctx.neededImports.add('set');
|
|
202
|
-
let delta = op === ts.SyntaxKind.PlusPlusToken ? ts.SyntaxKind.PlusToken : ts.SyntaxKind.MinusToken, factory = ctx.factory, name = node.operand.text, signalIdent = factory.createIdentifier(name), valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
203
|
-
if (node.parent && ts.isExpressionStatement(node.parent)) {
|
|
204
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1)));
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
return createCommaExpr(factory, createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))), factory.createPropertyAccessExpression(factory.createIdentifier(name), 'value'));
|
|
208
|
-
}
|
|
209
|
-
}
|
|
223
|
+
let nodeStart = node.getStart(ctx.sourceFile);
|
|
224
|
+
if (isInComputedRange(ctx.computedArgRanges, nodeStart, node.end)) {
|
|
225
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
226
|
+
return;
|
|
210
227
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
228
|
+
let binding = findBinding(ctx.scopedBindings, node.text, node), name = node.text;
|
|
229
|
+
if (binding) {
|
|
230
|
+
if (!isReactiveReassignment(node.parent) &&
|
|
231
|
+
!(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
|
|
232
|
+
let writeCtx = isWriteContext(node);
|
|
233
|
+
if (writeCtx) {
|
|
234
|
+
if (binding.type !== 'computed') {
|
|
235
|
+
ctx.neededImports.add('set');
|
|
236
|
+
let parent = node.parent;
|
|
237
|
+
if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
|
|
238
|
+
let valueText = parent.right.getText(ctx.sourceFile);
|
|
239
|
+
ctx.replacements.push({
|
|
240
|
+
end: parent.end,
|
|
241
|
+
newText: `set(${name}, ${valueText})`,
|
|
242
|
+
start: parent.pos
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
|
|
246
|
+
let op = getCompoundOperator(parent.operatorToken.kind), valueText = parent.right.getText(ctx.sourceFile);
|
|
247
|
+
ctx.replacements.push({
|
|
248
|
+
end: parent.end,
|
|
249
|
+
newText: `set(${name}, ${name}.value ${op} ${valueText})`,
|
|
250
|
+
start: parent.pos
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
else if (writeCtx === 'increment') {
|
|
254
|
+
let isPrefix = ts.isPrefixUnaryExpression(parent), op = parent.operator, delta = op === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
|
|
255
|
+
if (ts.isExpressionStatement(parent.parent)) {
|
|
256
|
+
ctx.replacements.push({
|
|
257
|
+
end: parent.end,
|
|
258
|
+
newText: `set(${name}, ${name}.value ${delta})`,
|
|
259
|
+
start: parent.pos
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
else if (isPrefix) {
|
|
263
|
+
ctx.replacements.push({
|
|
264
|
+
end: parent.end,
|
|
265
|
+
newText: `(set(${name}, ${name}.value ${delta}), ${name}.value)`,
|
|
266
|
+
start: parent.pos
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
let tmp = uid('tmp');
|
|
271
|
+
ctx.replacements.push({
|
|
272
|
+
end: parent.end,
|
|
273
|
+
newText: `((${tmp}) => (set(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
|
|
274
|
+
start: parent.pos
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
221
279
|
}
|
|
222
280
|
else {
|
|
223
|
-
|
|
281
|
+
ctx.neededImports.add('read');
|
|
282
|
+
ctx.replacements.push({
|
|
283
|
+
end: node.end,
|
|
284
|
+
newText: `read(${name})`,
|
|
285
|
+
start: node.pos
|
|
286
|
+
});
|
|
224
287
|
}
|
|
225
288
|
}
|
|
226
289
|
}
|
|
227
290
|
}
|
|
228
|
-
|
|
229
|
-
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
230
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
231
|
-
}
|
|
232
|
-
if (ts.isBinaryExpression(node.parent) && node.parent.left === node) {
|
|
233
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
234
|
-
}
|
|
235
|
-
if ((ts.isPrefixUnaryExpression(node.parent) || ts.isPostfixUnaryExpression(node.parent)) &&
|
|
236
|
-
node.parent.operand === node) {
|
|
237
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
238
|
-
}
|
|
239
|
-
if (ts.isTypeOfExpression(node.parent) && node.parent.expression === node) {
|
|
240
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
241
|
-
}
|
|
242
|
-
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
243
|
-
if (binding) {
|
|
244
|
-
ctx.neededImports.add('read');
|
|
245
|
-
return createReadCall(ctx.factory, ctx.ns.reactivity, ctx.factory.createIdentifier(node.text));
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
291
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
249
292
|
}
|
|
250
|
-
function
|
|
251
|
-
if (ts.isIdentifier(node)
|
|
293
|
+
function visitArg(ctx, node) {
|
|
294
|
+
if (ts.isIdentifier(node)) {
|
|
252
295
|
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
253
|
-
|
|
296
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
297
|
+
return;
|
|
254
298
|
}
|
|
255
299
|
if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
|
|
256
|
-
|
|
300
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
301
|
+
return;
|
|
257
302
|
}
|
|
258
303
|
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
259
304
|
if (binding) {
|
|
260
305
|
ctx.neededImports.add('read');
|
|
261
|
-
|
|
306
|
+
ctx.innerReplacements.push({
|
|
307
|
+
end: node.end - ctx.argStart,
|
|
308
|
+
newText: `read(${node.text})`,
|
|
309
|
+
start: node.getStart(ctx.sourceFile) - ctx.argStart
|
|
310
|
+
});
|
|
262
311
|
}
|
|
263
312
|
}
|
|
264
|
-
|
|
313
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
265
314
|
}
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
ns,
|
|
276
|
-
scopedBindings: []
|
|
277
|
-
};
|
|
278
|
-
return ts.visitNode(sourceFile, n => visit(ctx, n));
|
|
279
|
-
};
|
|
315
|
+
const transformReactivePrimitives = (sourceFile, bindings) => {
|
|
316
|
+
let code = sourceFile.getFullText(), ctx = {
|
|
317
|
+
bindings,
|
|
318
|
+
computedArgRanges: [],
|
|
319
|
+
hasReactiveImport: false,
|
|
320
|
+
neededImports: new Set(),
|
|
321
|
+
replacements: [],
|
|
322
|
+
scopedBindings: [],
|
|
323
|
+
sourceFile
|
|
280
324
|
};
|
|
325
|
+
visit(ctx, sourceFile);
|
|
326
|
+
if (ctx.replacements.length === 0) {
|
|
327
|
+
return code;
|
|
328
|
+
}
|
|
329
|
+
let result = applyReplacements(code, ctx.replacements);
|
|
330
|
+
if (ctx.neededImports.size > 0) {
|
|
331
|
+
result = addMissingImports(result, ctx.neededImports);
|
|
332
|
+
}
|
|
333
|
+
return result;
|
|
281
334
|
};
|
|
282
|
-
export {
|
|
335
|
+
export { transformReactivePrimitives };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { applyReplacements, type Replacement } from '@esportsplus/typescript/transformer';
|
|
2
|
+
type ExtraImport = {
|
|
3
|
+
module: string;
|
|
4
|
+
specifier: string;
|
|
5
|
+
};
|
|
6
|
+
declare const addMissingImports: (code: string, needed: Set<string>, extraImports?: ExtraImport[]) => string;
|
|
7
|
+
export { addMissingImports, applyReplacements };
|
|
8
|
+
export type { ExtraImport, Replacement };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { addImport, applyReplacements } from '@esportsplus/typescript/transformer';
|
|
2
|
+
const addMissingImports = (code, needed, extraImports) => {
|
|
3
|
+
let extraSpecifiers = new Set(), reactivitySpecifiers = [];
|
|
4
|
+
if (extraImports) {
|
|
5
|
+
for (let i = 0, n = extraImports.length; i < n; i++) {
|
|
6
|
+
extraSpecifiers.add(extraImports[i].specifier);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
for (let imp of needed) {
|
|
10
|
+
if (!extraSpecifiers.has(imp)) {
|
|
11
|
+
reactivitySpecifiers.push(imp);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (reactivitySpecifiers.length > 0) {
|
|
15
|
+
code = addImport(code, '@esportsplus/reactivity', reactivitySpecifiers);
|
|
16
|
+
}
|
|
17
|
+
if (extraImports) {
|
|
18
|
+
for (let i = 0, n = extraImports.length; i < n; i++) {
|
|
19
|
+
let extra = extraImports[i];
|
|
20
|
+
if (needed.has(extra.specifier)) {
|
|
21
|
+
code = addImport(code, extra.module, [extra.specifier]);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return code;
|
|
26
|
+
};
|
|
27
|
+
export { addMissingImports, applyReplacements };
|
package/build/types.d.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
|
|
2
2
|
import { ReactiveArray, ReactiveObject } from './reactive/index.js';
|
|
3
|
+
import { ts } from '@esportsplus/typescript';
|
|
3
4
|
type BindingType = 'array' | 'computed' | 'object' | 'signal';
|
|
4
5
|
type Bindings = Map<string, BindingType>;
|
|
5
|
-
interface Namespaces {
|
|
6
|
-
array: string;
|
|
7
|
-
constants: string;
|
|
8
|
-
reactivity: string;
|
|
9
|
-
}
|
|
10
6
|
interface Computed<T> {
|
|
11
7
|
cleanup: VoidFunction | VoidFunction[] | null;
|
|
12
8
|
deps: Link | null;
|
|
@@ -35,4 +31,9 @@ type Signal<T> = {
|
|
|
35
31
|
type: typeof SIGNAL;
|
|
36
32
|
value: T;
|
|
37
33
|
};
|
|
38
|
-
|
|
34
|
+
interface TransformResult {
|
|
35
|
+
code: string;
|
|
36
|
+
sourceFile: ts.SourceFile;
|
|
37
|
+
transformed: boolean;
|
|
38
|
+
}
|
|
39
|
+
export type { BindingType, Bindings, Computed, Link, ReactiveArray, ReactiveObject, Signal, TransformResult };
|
package/package.json
CHANGED
package/src/reactive/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Prettify } from '@esportsplus/utilities';
|
|
1
2
|
import { REACTIVE_OBJECT } from '~/constants';
|
|
2
3
|
import { ReactiveArray } from './array';
|
|
3
4
|
|
|
@@ -5,10 +6,28 @@ import { ReactiveArray } from './array';
|
|
|
5
6
|
// Branded type to prevent assignment to computed values
|
|
6
7
|
declare const READONLY: unique symbol;
|
|
7
8
|
|
|
8
|
-
type
|
|
9
|
-
[
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
type Infer<T> =
|
|
10
|
+
T extends (...args: unknown[]) => Promise<infer R>
|
|
11
|
+
? R | undefined
|
|
12
|
+
: T extends (...args: any[]) => infer R
|
|
13
|
+
? R
|
|
14
|
+
: T extends (infer U)[]
|
|
15
|
+
? ReactiveArray<U>
|
|
16
|
+
: T extends ReactiveObject<any>
|
|
17
|
+
? T
|
|
18
|
+
: T extends Record<PropertyKey, unknown>
|
|
19
|
+
? { [K in keyof T]: T[K] }
|
|
20
|
+
: T;
|
|
21
|
+
|
|
22
|
+
type ReactiveObject<T> =
|
|
23
|
+
T extends Record<PropertyKey, unknown>
|
|
24
|
+
? Prettify<{ [K in keyof T]: Infer<T[K]> } & {
|
|
25
|
+
[REACTIVE_OBJECT]: true;
|
|
26
|
+
dispose: VoidFunction
|
|
27
|
+
}>
|
|
28
|
+
: T extends (infer U)[]
|
|
29
|
+
? ReactiveArray<U>
|
|
30
|
+
: never;
|
|
12
31
|
|
|
13
32
|
type ReactiveObjectGuard<T> = T extends { dispose: any } ? { never: '[ dispose ] is a reserved key' } : T;
|
|
14
33
|
|
|
@@ -20,8 +39,7 @@ function reactive<T extends Record<PropertyKey, any>>(_input: ReactiveObjectGuar
|
|
|
20
39
|
// Array literal → existing ReactiveArray behavior
|
|
21
40
|
function reactive<T>(_input: T[]): ReactiveArray<T>;
|
|
22
41
|
// Everything else → passthrough type (allows assignment)
|
|
23
|
-
function reactive<T>(_input: T): T
|
|
24
|
-
function reactive(_input: unknown): unknown {
|
|
42
|
+
function reactive<T>(_input: T): T {
|
|
25
43
|
throw new Error(
|
|
26
44
|
'@esportsplus/reactivity: reactive() called at runtime. ' +
|
|
27
45
|
'Ensure vite-plugin-reactivity-compile is configured.'
|