@esportsplus/reactivity 0.24.5 → 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 -23
- package/build/transformer/transforms/object.d.ts +3 -9
- package/build/transformer/transforms/object.js +99 -89
- package/build/transformer/transforms/primitives.d.ts +3 -3
- package/build/transformer/transforms/primitives.js +193 -142
- 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 -40
- package/src/transformer/transforms/object.ts +137 -294
- package/src/transformer/transforms/primitives.ts +245 -242
- 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,29 +113,54 @@ 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
|
-
if (ts.isImportDeclaration(node)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
152
|
+
if (ts.isImportDeclaration(node) &&
|
|
153
|
+
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
154
|
+
node.moduleSpecifier.text.includes('@esportsplus/reactivity')) {
|
|
155
|
+
let clause = node.importClause;
|
|
156
|
+
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
157
|
+
for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
|
|
158
|
+
if (clause.namedBindings.elements[i].name.text === 'reactive') {
|
|
159
|
+
ctx.hasReactiveImport = true;
|
|
160
|
+
break;
|
|
143
161
|
}
|
|
144
162
|
}
|
|
145
163
|
}
|
|
146
|
-
return node;
|
|
147
164
|
}
|
|
148
165
|
if (ctx.hasReactiveImport &&
|
|
149
166
|
ts.isCallExpression(node) &&
|
|
@@ -153,132 +170,166 @@ function visit(ctx, node) {
|
|
|
153
170
|
let arg = node.arguments[0], classification = classifyReactiveArg(arg);
|
|
154
171
|
if (classification) {
|
|
155
172
|
let varName = null;
|
|
156
|
-
if (
|
|
173
|
+
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
157
174
|
varName = node.parent.name.text;
|
|
158
175
|
}
|
|
159
|
-
else if (node.parent &&
|
|
160
|
-
ts.isBinaryExpression(node.parent) &&
|
|
176
|
+
else if (ts.isBinaryExpression(node.parent) &&
|
|
161
177
|
node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
162
178
|
ts.isIdentifier(node.parent.left)) {
|
|
163
179
|
varName = node.parent.left.text;
|
|
164
180
|
}
|
|
165
181
|
if (varName) {
|
|
166
182
|
let scope = findEnclosingScope(node);
|
|
167
|
-
ctx.bindings.set(varName, classification);
|
|
168
183
|
ctx.scopedBindings.push({ name: varName, scope, type: classification });
|
|
184
|
+
ctx.bindings.set(varName, classification);
|
|
169
185
|
}
|
|
170
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
|
+
});
|
|
171
205
|
ctx.neededImports.add('computed');
|
|
172
|
-
let transformedArg = ts.visitEachChild(arg, n => visitComputedArg(ctx, n), ctx.context);
|
|
173
|
-
return createComputedCall(ctx.factory, ctx.ns.reactivity, transformedArg);
|
|
174
206
|
}
|
|
175
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
|
+
});
|
|
176
214
|
ctx.neededImports.add('signal');
|
|
177
|
-
return createSignalCall(ctx.factory, ctx.ns.reactivity, arg);
|
|
178
215
|
}
|
|
179
216
|
}
|
|
180
217
|
}
|
|
181
|
-
if (ts.
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (binding && binding.type !== 'computed' && !isReactiveReassignment(node)) {
|
|
186
|
-
ctx.neededImports.add('set');
|
|
187
|
-
let factory = ctx.factory, name = node.left.text, signalIdent = factory.createIdentifier(name), transformedRight = ts.visitEachChild(node.right, n => visit(ctx, n), ctx.context);
|
|
188
|
-
if (assignType === 'simple') {
|
|
189
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, transformedRight);
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
let op = getCompoundOperator(node.operatorToken.kind), valueAccess = factory.createPropertyAccessExpression(signalIdent, 'value');
|
|
193
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, op, transformedRight));
|
|
194
|
-
}
|
|
195
|
-
}
|
|
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;
|
|
196
222
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
let binding = findBinding(ctx.scopedBindings, node.operand.text, node.operand);
|
|
202
|
-
if (binding && binding.type !== 'computed') {
|
|
203
|
-
ctx.neededImports.add('set');
|
|
204
|
-
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');
|
|
205
|
-
if (node.parent && ts.isExpressionStatement(node.parent)) {
|
|
206
|
-
return createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1)));
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
return createCommaExpr(factory, createSetCall(factory, ctx.ns.reactivity, signalIdent, factory.createBinaryExpression(valueAccess, delta, factory.createNumericLiteral(1))), factory.createPropertyAccessExpression(factory.createIdentifier(name), 'value'));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
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;
|
|
212
227
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
+
}
|
|
223
279
|
}
|
|
224
280
|
else {
|
|
225
|
-
|
|
281
|
+
ctx.neededImports.add('read');
|
|
282
|
+
ctx.replacements.push({
|
|
283
|
+
end: node.end,
|
|
284
|
+
newText: `read(${name})`,
|
|
285
|
+
start: node.pos
|
|
286
|
+
});
|
|
226
287
|
}
|
|
227
288
|
}
|
|
228
289
|
}
|
|
229
290
|
}
|
|
230
|
-
|
|
231
|
-
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
232
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
233
|
-
}
|
|
234
|
-
if (ts.isBinaryExpression(node.parent) && node.parent.left === node) {
|
|
235
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
236
|
-
}
|
|
237
|
-
if ((ts.isPrefixUnaryExpression(node.parent) || ts.isPostfixUnaryExpression(node.parent)) &&
|
|
238
|
-
node.parent.operand === node) {
|
|
239
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
240
|
-
}
|
|
241
|
-
if (ts.isTypeOfExpression(node.parent) && node.parent.expression === node) {
|
|
242
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
243
|
-
}
|
|
244
|
-
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
245
|
-
if (binding) {
|
|
246
|
-
ctx.neededImports.add('read');
|
|
247
|
-
return createReadCall(ctx.factory, ctx.ns.reactivity, ctx.factory.createIdentifier(node.text));
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
return ts.visitEachChild(node, n => visit(ctx, n), ctx.context);
|
|
291
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
251
292
|
}
|
|
252
|
-
function
|
|
253
|
-
if (ts.isIdentifier(node)
|
|
293
|
+
function visitArg(ctx, node) {
|
|
294
|
+
if (ts.isIdentifier(node)) {
|
|
254
295
|
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
255
|
-
|
|
296
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
297
|
+
return;
|
|
256
298
|
}
|
|
257
299
|
if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
|
|
258
|
-
|
|
300
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
301
|
+
return;
|
|
259
302
|
}
|
|
260
303
|
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
261
304
|
if (binding) {
|
|
262
305
|
ctx.neededImports.add('read');
|
|
263
|
-
|
|
306
|
+
ctx.innerReplacements.push({
|
|
307
|
+
end: node.end - ctx.argStart,
|
|
308
|
+
newText: `read(${node.text})`,
|
|
309
|
+
start: node.getStart(ctx.sourceFile) - ctx.argStart
|
|
310
|
+
});
|
|
264
311
|
}
|
|
265
312
|
}
|
|
266
|
-
|
|
313
|
+
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
267
314
|
}
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
ns,
|
|
278
|
-
scopedBindings: []
|
|
279
|
-
};
|
|
280
|
-
return ts.visitNode(sourceFile, n => visit(ctx, n));
|
|
281
|
-
};
|
|
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
|
|
282
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;
|
|
283
334
|
};
|
|
284
|
-
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.'
|