@esportsplus/reactivity 0.25.3 → 0.25.4
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/constants.d.ts +8 -1
- package/build/constants.js +8 -1
- package/build/index.d.ts +2 -2
- package/build/index.js +2 -2
- package/build/reactive/array.js +18 -27
- package/build/reactive/index.d.ts +14 -13
- package/build/reactive/index.js +2 -1
- package/build/system.d.ts +2 -2
- package/build/system.js +10 -10
- package/build/transformer/detector.d.ts +2 -2
- package/build/transformer/detector.js +14 -14
- package/build/transformer/index.d.ts +3 -7
- package/build/transformer/index.js +21 -39
- package/build/transformer/plugins/tsc.js +7 -2
- package/build/transformer/plugins/vite.js +7 -9
- package/build/transformer/transforms/array.d.ts +3 -3
- package/build/transformer/transforms/array.js +16 -22
- package/build/transformer/transforms/object.d.ts +3 -3
- package/build/transformer/transforms/object.js +40 -56
- package/build/transformer/transforms/primitives.d.ts +3 -3
- package/build/transformer/transforms/primitives.js +54 -112
- package/build/types.d.ts +2 -2
- package/package.json +6 -6
- package/readme.md +5 -5
- package/src/constants.ts +20 -13
- package/src/index.ts +2 -2
- package/src/reactive/array.ts +18 -32
- package/src/reactive/index.ts +18 -15
- package/src/system.ts +14 -21
- package/src/transformer/detector.ts +16 -25
- package/src/transformer/index.ts +24 -46
- package/src/transformer/plugins/tsc.ts +8 -2
- package/src/transformer/plugins/vite.ts +9 -12
- package/src/transformer/transforms/array.ts +20 -37
- package/src/transformer/transforms/object.ts +54 -78
- package/src/transformer/transforms/primitives.ts +65 -140
- package/src/types.ts +5 -3
- package/test/vite.config.ts +1 -1
- package/build/transformer/transforms/utilities.d.ts +0 -8
- package/build/transformer/transforms/utilities.js +0 -27
- package/src/transformer/transforms/utilities.ts +0 -45
- package/storage/tsc-transform-analysis-2026-01-04.md +0 -125
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import { uid } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import type { Bindings } from '~/types';
|
|
3
|
-
import { addMissingImports, applyReplacements, ExtraImport, Replacement } from './utilities';
|
|
4
1
|
import { ts } from '@esportsplus/typescript';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const EXTRA_IMPORTS: ExtraImport[] = [
|
|
10
|
-
{ module: '@esportsplus/reactivity/constants', specifier: 'REACTIVE_OBJECT' },
|
|
11
|
-
{ module: '@esportsplus/reactivity/reactive/array', specifier: 'ReactiveArray' }
|
|
12
|
-
];
|
|
2
|
+
import { code as c, type Replacement } from '@esportsplus/typescript/transformer';
|
|
3
|
+
import { COMPILATION_TYPE_ARRAY, COMPILATION_TYPE_COMPUTED, COMPILATION_TYPE_SIGNAL, PACKAGE } from '~/constants';
|
|
4
|
+
import type { Bindings } from '~/types';
|
|
13
5
|
|
|
14
6
|
|
|
15
7
|
interface AnalyzedProperty {
|
|
@@ -19,19 +11,20 @@ interface AnalyzedProperty {
|
|
|
19
11
|
}
|
|
20
12
|
|
|
21
13
|
interface ReactiveObjectCall {
|
|
14
|
+
className: string;
|
|
22
15
|
end: number;
|
|
23
|
-
|
|
24
|
-
needsImports: Set<string>;
|
|
16
|
+
properties: AnalyzedProperty[];
|
|
25
17
|
start: number;
|
|
26
18
|
varName: string | null;
|
|
27
19
|
}
|
|
28
20
|
|
|
29
21
|
interface TransformContext {
|
|
30
|
-
allNeededImports: Set<string>;
|
|
31
22
|
bindings: Bindings;
|
|
32
23
|
calls: ReactiveObjectCall[];
|
|
24
|
+
classCounter: number;
|
|
33
25
|
hasReactiveImport: boolean;
|
|
34
26
|
lastImportEnd: number;
|
|
27
|
+
ns: string;
|
|
35
28
|
sourceFile: ts.SourceFile;
|
|
36
29
|
}
|
|
37
30
|
|
|
@@ -54,43 +47,53 @@ function analyzeProperty(prop: ts.ObjectLiteralElementLike, sourceFile: ts.Sourc
|
|
|
54
47
|
valueText = value.getText(sourceFile);
|
|
55
48
|
|
|
56
49
|
if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
|
|
57
|
-
return { key, type:
|
|
50
|
+
return { key, type: COMPILATION_TYPE_COMPUTED, valueText };
|
|
58
51
|
}
|
|
59
52
|
|
|
60
53
|
if (ts.isArrayLiteralExpression(value)) {
|
|
61
|
-
|
|
54
|
+
let elements = value.elements,
|
|
55
|
+
elementsText = '';
|
|
56
|
+
|
|
57
|
+
for (let i = 0, n = elements.length; i < n; i++) {
|
|
58
|
+
if (i > 0) {
|
|
59
|
+
elementsText += ', ';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
elementsText += elements[i].getText(sourceFile);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return { key, type: COMPILATION_TYPE_ARRAY, valueText: elementsText };
|
|
62
66
|
}
|
|
63
67
|
|
|
64
|
-
return { key, type:
|
|
68
|
+
return { key, type: COMPILATION_TYPE_SIGNAL, valueText };
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
function buildClassCode(className: string, properties: AnalyzedProperty[]): string {
|
|
71
|
+
function buildClassCode(className: string, properties: AnalyzedProperty[], ns: string): string {
|
|
68
72
|
let accessors: string[] = [],
|
|
69
73
|
disposeStatements: string[] = [],
|
|
70
|
-
fields: string[] = []
|
|
74
|
+
fields: string[] = [],
|
|
75
|
+
paramCounter = 0;
|
|
71
76
|
|
|
72
|
-
fields.push(`[REACTIVE_OBJECT] = true;`);
|
|
77
|
+
fields.push(`[${ns}.REACTIVE_OBJECT] = true;`);
|
|
73
78
|
|
|
74
79
|
for (let i = 0, n = properties.length; i < n; i++) {
|
|
75
80
|
let { key, type, valueText } = properties[i];
|
|
76
81
|
|
|
77
|
-
if (type ===
|
|
78
|
-
let param =
|
|
82
|
+
if (type === COMPILATION_TYPE_SIGNAL) {
|
|
83
|
+
let param = `_v${paramCounter++}`;
|
|
79
84
|
|
|
80
|
-
fields.push(`#${key} = signal(${valueText});`);
|
|
81
|
-
accessors.push(`get ${key}() { return read(this.#${key}); }`);
|
|
82
|
-
accessors.push(`set ${key}(${param}) {
|
|
85
|
+
fields.push(`#${key} = ${ns}.signal(${valueText});`);
|
|
86
|
+
accessors.push(`get ${key}() { return ${ns}.read(this.#${key}); }`);
|
|
87
|
+
accessors.push(`set ${key}(${param}) { ${ns}.write(this.#${key}, ${param}); }`);
|
|
83
88
|
}
|
|
84
|
-
else if (type ===
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
fields.push(`${key} = new ReactiveArray(${elements});`);
|
|
89
|
+
else if (type === COMPILATION_TYPE_ARRAY) {
|
|
90
|
+
fields.push(`${key} = new ${ns}.ReactiveArray(${valueText});`);
|
|
88
91
|
disposeStatements.push(`this.${key}.dispose();`);
|
|
89
92
|
}
|
|
90
|
-
else if (type ===
|
|
91
|
-
fields.push(`#${key}
|
|
92
|
-
accessors.push(`get ${key}() { return read(this.#${key} ??= computed(${valueText})); }`);
|
|
93
|
-
disposeStatements.push(`if (this.#${key}) dispose(this.#${key});`);
|
|
93
|
+
else if (type === COMPILATION_TYPE_COMPUTED) {
|
|
94
|
+
fields.push(`#${key} = null;`);
|
|
95
|
+
accessors.push(`get ${key}() { return ${ns}.read(this.#${key} ??= ${ns}.computed(${valueText})); }`);
|
|
96
|
+
disposeStatements.push(`if (this.#${key}) ${ns}.dispose(this.#${key});`);
|
|
94
97
|
}
|
|
95
98
|
}
|
|
96
99
|
|
|
@@ -112,7 +115,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
112
115
|
|
|
113
116
|
if (
|
|
114
117
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
115
|
-
node.moduleSpecifier.text.includes(
|
|
118
|
+
node.moduleSpecifier.text.includes(PACKAGE)
|
|
116
119
|
) {
|
|
117
120
|
let clause = node.importClause;
|
|
118
121
|
|
|
@@ -138,20 +141,15 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
138
141
|
let arg = node.arguments[0];
|
|
139
142
|
|
|
140
143
|
if (arg && ts.isObjectLiteralExpression(arg)) {
|
|
141
|
-
let
|
|
144
|
+
let properties: AnalyzedProperty[] = [],
|
|
145
|
+
props = arg.properties,
|
|
146
|
+
varName: string | null = null;
|
|
142
147
|
|
|
143
148
|
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
144
149
|
varName = node.parent.name.text;
|
|
145
150
|
ctx.bindings.set(varName, 'object');
|
|
146
151
|
}
|
|
147
152
|
|
|
148
|
-
let needsImports = new Set<string>(),
|
|
149
|
-
properties: AnalyzedProperty[] = [];
|
|
150
|
-
|
|
151
|
-
needsImports.add('REACTIVE_OBJECT');
|
|
152
|
-
|
|
153
|
-
let props = arg.properties;
|
|
154
|
-
|
|
155
153
|
for (let i = 0, n = props.length; i < n; i++) {
|
|
156
154
|
let prop = props[i];
|
|
157
155
|
|
|
@@ -169,31 +167,15 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
169
167
|
|
|
170
168
|
properties.push(analyzed);
|
|
171
169
|
|
|
172
|
-
if (analyzed.type ===
|
|
173
|
-
|
|
174
|
-
needsImports.add('set');
|
|
175
|
-
needsImports.add('signal');
|
|
176
|
-
}
|
|
177
|
-
else if (analyzed.type === 'array') {
|
|
178
|
-
needsImports.add('ReactiveArray');
|
|
179
|
-
|
|
180
|
-
if (varName) {
|
|
181
|
-
ctx.bindings.set(`${varName}.${analyzed.key}`, 'array');
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
else if (analyzed.type === 'computed') {
|
|
185
|
-
needsImports.add('computed');
|
|
186
|
-
needsImports.add('dispose');
|
|
187
|
-
needsImports.add('read');
|
|
170
|
+
if (analyzed.type === COMPILATION_TYPE_ARRAY && varName) {
|
|
171
|
+
ctx.bindings.set(`${varName}.${analyzed.key}`, COMPILATION_TYPE_ARRAY);
|
|
188
172
|
}
|
|
189
173
|
}
|
|
190
174
|
|
|
191
|
-
needsImports.forEach(imp => ctx.allNeededImports.add(imp));
|
|
192
|
-
|
|
193
175
|
ctx.calls.push({
|
|
176
|
+
className: `_RO${ctx.classCounter++}`,
|
|
194
177
|
end: node.end,
|
|
195
|
-
|
|
196
|
-
needsImports,
|
|
178
|
+
properties,
|
|
197
179
|
start: node.pos,
|
|
198
180
|
varName
|
|
199
181
|
});
|
|
@@ -204,14 +186,15 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
204
186
|
}
|
|
205
187
|
|
|
206
188
|
|
|
207
|
-
|
|
189
|
+
export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): string => {
|
|
208
190
|
let code = sourceFile.getFullText(),
|
|
209
191
|
ctx: TransformContext = {
|
|
210
|
-
allNeededImports: new Set<string>(),
|
|
211
192
|
bindings,
|
|
212
193
|
calls: [],
|
|
194
|
+
classCounter: 0,
|
|
213
195
|
hasReactiveImport: false,
|
|
214
196
|
lastImportEnd: 0,
|
|
197
|
+
ns,
|
|
215
198
|
sourceFile
|
|
216
199
|
};
|
|
217
200
|
|
|
@@ -221,31 +204,24 @@ const transformReactiveObjects = (sourceFile: ts.SourceFile, bindings: Bindings)
|
|
|
221
204
|
return code;
|
|
222
205
|
}
|
|
223
206
|
|
|
224
|
-
let
|
|
207
|
+
let classes = ctx.calls.map(c => buildClassCode(c.className, c.properties, ns)).join('\n'),
|
|
208
|
+
replacements: Replacement[] = [];
|
|
225
209
|
|
|
226
210
|
replacements.push({
|
|
227
211
|
end: ctx.lastImportEnd,
|
|
228
|
-
newText: code.substring(0, ctx.lastImportEnd) + '\n' +
|
|
212
|
+
newText: code.substring(0, ctx.lastImportEnd) + '\n' + classes + '\n',
|
|
229
213
|
start: 0
|
|
230
214
|
});
|
|
231
215
|
|
|
232
216
|
for (let i = 0, n = ctx.calls.length; i < n; i++) {
|
|
233
|
-
let call = ctx.calls[i]
|
|
234
|
-
classMatch = call.generatedClass.match(CLASS_NAME_REGEX);
|
|
217
|
+
let call = ctx.calls[i];
|
|
235
218
|
|
|
236
219
|
replacements.push({
|
|
237
220
|
end: call.end,
|
|
238
|
-
newText: ` new ${
|
|
221
|
+
newText: ` new ${call.className}()`,
|
|
239
222
|
start: call.start
|
|
240
223
|
});
|
|
241
224
|
}
|
|
242
225
|
|
|
243
|
-
return
|
|
244
|
-
|
|
245
|
-
ctx.allNeededImports,
|
|
246
|
-
EXTRA_IMPORTS
|
|
247
|
-
);
|
|
248
|
-
};
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
export { transformReactiveObjects };
|
|
226
|
+
return c.replace(code, replacements);
|
|
227
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { uid, type Range } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import type { BindingType, Bindings } from '~/types';
|
|
3
|
-
import { addMissingImports, applyReplacements, Replacement } from './utilities';
|
|
4
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { code as c, type Range, type Replacement } from '@esportsplus/typescript/transformer';
|
|
3
|
+
import type { BindingType, Bindings } from '~/types';
|
|
4
|
+
import { COMPILATION_TYPE_COMPUTED, COMPILATION_ENTRYPOINT, COMPILATION_TYPE_SIGNAL, PACKAGE } from '~/constants';
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
interface ArgContext {
|
|
8
8
|
argStart: number;
|
|
9
9
|
innerReplacements: Replacement[];
|
|
10
|
-
|
|
10
|
+
ns: string;
|
|
11
11
|
scopedBindings: ScopeBinding[];
|
|
12
12
|
sourceFile: ts.SourceFile;
|
|
13
13
|
}
|
|
@@ -22,23 +22,43 @@ interface TransformContext {
|
|
|
22
22
|
bindings: Bindings;
|
|
23
23
|
computedArgRanges: Range[];
|
|
24
24
|
hasReactiveImport: boolean;
|
|
25
|
-
|
|
25
|
+
ns: string;
|
|
26
26
|
replacements: Replacement[];
|
|
27
27
|
scopedBindings: ScopeBinding[];
|
|
28
28
|
sourceFile: ts.SourceFile;
|
|
29
|
+
tmpCounter: number;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
let COMPOUND_OPERATORS = new Map<ts.SyntaxKind, string>([
|
|
34
|
+
[ts.SyntaxKind.AmpersandAmpersandEqualsToken, '&&'],
|
|
35
|
+
[ts.SyntaxKind.AmpersandEqualsToken, '&'],
|
|
36
|
+
[ts.SyntaxKind.AsteriskAsteriskEqualsToken, '**'],
|
|
37
|
+
[ts.SyntaxKind.AsteriskEqualsToken, '*'],
|
|
38
|
+
[ts.SyntaxKind.BarBarEqualsToken, '||'],
|
|
39
|
+
[ts.SyntaxKind.BarEqualsToken, '|'],
|
|
40
|
+
[ts.SyntaxKind.CaretEqualsToken, '^'],
|
|
41
|
+
[ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, '>>'],
|
|
42
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, '>>>'],
|
|
43
|
+
[ts.SyntaxKind.LessThanLessThanEqualsToken, '<<'],
|
|
44
|
+
[ts.SyntaxKind.MinusEqualsToken, '-'],
|
|
45
|
+
[ts.SyntaxKind.PercentEqualsToken, '%'],
|
|
46
|
+
[ts.SyntaxKind.PlusEqualsToken, '+'],
|
|
47
|
+
[ts.SyntaxKind.QuestionQuestionEqualsToken, '??'],
|
|
48
|
+
[ts.SyntaxKind.SlashEqualsToken, '/']
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
function classifyReactiveArg(arg: ts.Expression): BindingType | null {
|
|
33
53
|
if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
|
|
34
|
-
return
|
|
54
|
+
return COMPILATION_TYPE_COMPUTED;
|
|
35
55
|
}
|
|
36
56
|
|
|
37
57
|
if (ts.isObjectLiteralExpression(arg) || ts.isArrayLiteralExpression(arg)) {
|
|
38
58
|
return null;
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
return
|
|
61
|
+
return COMPILATION_TYPE_SIGNAL;
|
|
42
62
|
}
|
|
43
63
|
|
|
44
64
|
function findBinding(bindings: ScopeBinding[], name: string, node: ts.Node): ScopeBinding | undefined {
|
|
@@ -76,57 +96,6 @@ function findEnclosingScope(node: ts.Node): ts.Node {
|
|
|
76
96
|
return node.getSourceFile();
|
|
77
97
|
}
|
|
78
98
|
|
|
79
|
-
function getCompoundOperator(kind: ts.SyntaxKind): string {
|
|
80
|
-
if (kind === ts.SyntaxKind.PlusEqualsToken) {
|
|
81
|
-
return '+';
|
|
82
|
-
}
|
|
83
|
-
else if (kind === ts.SyntaxKind.MinusEqualsToken) {
|
|
84
|
-
return '-';
|
|
85
|
-
}
|
|
86
|
-
else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
|
|
87
|
-
return '*';
|
|
88
|
-
}
|
|
89
|
-
else if (kind === ts.SyntaxKind.SlashEqualsToken) {
|
|
90
|
-
return '/';
|
|
91
|
-
}
|
|
92
|
-
else if (kind === ts.SyntaxKind.PercentEqualsToken) {
|
|
93
|
-
return '%';
|
|
94
|
-
}
|
|
95
|
-
else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
|
|
96
|
-
return '**';
|
|
97
|
-
}
|
|
98
|
-
else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
|
|
99
|
-
return '&';
|
|
100
|
-
}
|
|
101
|
-
else if (kind === ts.SyntaxKind.BarEqualsToken) {
|
|
102
|
-
return '|';
|
|
103
|
-
}
|
|
104
|
-
else if (kind === ts.SyntaxKind.CaretEqualsToken) {
|
|
105
|
-
return '^';
|
|
106
|
-
}
|
|
107
|
-
else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
|
|
108
|
-
return '<<';
|
|
109
|
-
}
|
|
110
|
-
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
|
|
111
|
-
return '>>';
|
|
112
|
-
}
|
|
113
|
-
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
|
|
114
|
-
return '>>>';
|
|
115
|
-
}
|
|
116
|
-
else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
|
|
117
|
-
return '&&';
|
|
118
|
-
}
|
|
119
|
-
else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
|
|
120
|
-
return '||';
|
|
121
|
-
}
|
|
122
|
-
else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
123
|
-
return '??';
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
return '+';
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
99
|
function isInComputedRange(ranges: Range[], start: number, end: number): boolean {
|
|
131
100
|
for (let i = 0, n = ranges.length; i < n; i++) {
|
|
132
101
|
let r = ranges[i];
|
|
@@ -142,11 +111,7 @@ function isInComputedRange(ranges: Range[], start: number, end: number): boolean
|
|
|
142
111
|
function isInDeclarationInit(node: ts.Node): boolean {
|
|
143
112
|
let parent = node.parent;
|
|
144
113
|
|
|
145
|
-
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
return false;
|
|
114
|
+
return ts.isVariableDeclaration(parent) && parent.initializer === node;
|
|
150
115
|
}
|
|
151
116
|
|
|
152
117
|
function isInScope(reference: ts.Node, binding: ScopeBinding): boolean {
|
|
@@ -172,7 +137,7 @@ function isReactiveReassignment(node: ts.Node): boolean {
|
|
|
172
137
|
parent.right === node &&
|
|
173
138
|
ts.isCallExpression(node) &&
|
|
174
139
|
ts.isIdentifier((node as ts.CallExpression).expression) &&
|
|
175
|
-
((node as ts.CallExpression).expression as ts.Identifier).text ===
|
|
140
|
+
((node as ts.CallExpression).expression as ts.Identifier).text === COMPILATION_ENTRYPOINT
|
|
176
141
|
) {
|
|
177
142
|
return true;
|
|
178
143
|
}
|
|
@@ -190,15 +155,7 @@ function isWriteContext(node: ts.Identifier): 'simple' | 'compound' | 'increment
|
|
|
190
155
|
return 'simple';
|
|
191
156
|
}
|
|
192
157
|
|
|
193
|
-
if (
|
|
194
|
-
return 'compound';
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
op === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
199
|
-
op === ts.SyntaxKind.BarBarEqualsToken ||
|
|
200
|
-
op === ts.SyntaxKind.QuestionQuestionEqualsToken
|
|
201
|
-
) {
|
|
158
|
+
if (COMPOUND_OPERATORS.has(op)) {
|
|
202
159
|
return 'compound';
|
|
203
160
|
}
|
|
204
161
|
}
|
|
@@ -218,13 +175,13 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
218
175
|
if (
|
|
219
176
|
ts.isImportDeclaration(node) &&
|
|
220
177
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
221
|
-
node.moduleSpecifier.text.includes(
|
|
178
|
+
node.moduleSpecifier.text.includes(PACKAGE)
|
|
222
179
|
) {
|
|
223
180
|
let clause = node.importClause;
|
|
224
181
|
|
|
225
182
|
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
226
183
|
for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
|
|
227
|
-
if (clause.namedBindings.elements[i].name.text ===
|
|
184
|
+
if (clause.namedBindings.elements[i].name.text === COMPILATION_ENTRYPOINT) {
|
|
228
185
|
ctx.hasReactiveImport = true;
|
|
229
186
|
break;
|
|
230
187
|
}
|
|
@@ -236,7 +193,7 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
236
193
|
ctx.hasReactiveImport &&
|
|
237
194
|
ts.isCallExpression(node) &&
|
|
238
195
|
ts.isIdentifier(node.expression) &&
|
|
239
|
-
node.expression.text ===
|
|
196
|
+
node.expression.text === COMPILATION_ENTRYPOINT &&
|
|
240
197
|
node.arguments.length > 0
|
|
241
198
|
) {
|
|
242
199
|
let arg = node.arguments[0],
|
|
@@ -264,42 +221,35 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
264
221
|
ctx.bindings.set(varName, classification);
|
|
265
222
|
}
|
|
266
223
|
|
|
267
|
-
if (classification ===
|
|
268
|
-
ctx.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
});
|
|
224
|
+
if (classification === COMPILATION_TYPE_COMPUTED) {
|
|
225
|
+
let argStart = arg.getStart(ctx.sourceFile);
|
|
226
|
+
|
|
227
|
+
ctx.computedArgRanges.push({ end: arg.end, start: argStart });
|
|
272
228
|
|
|
273
229
|
let argCtx: ArgContext = {
|
|
274
|
-
argStart
|
|
230
|
+
argStart,
|
|
275
231
|
innerReplacements: [],
|
|
276
|
-
|
|
232
|
+
ns: ctx.ns,
|
|
277
233
|
scopedBindings: ctx.scopedBindings,
|
|
278
234
|
sourceFile: ctx.sourceFile
|
|
279
235
|
};
|
|
280
236
|
|
|
281
237
|
visitArg(argCtx, arg);
|
|
282
238
|
|
|
283
|
-
let argText =
|
|
239
|
+
let argText = c.replace(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
|
|
284
240
|
|
|
285
241
|
ctx.replacements.push({
|
|
286
242
|
end: node.end,
|
|
287
|
-
newText:
|
|
243
|
+
newText: `${ctx.ns}.computed(${argText})`,
|
|
288
244
|
start: node.pos
|
|
289
245
|
});
|
|
290
|
-
|
|
291
|
-
ctx.neededImports.add('computed');
|
|
292
246
|
}
|
|
293
247
|
else {
|
|
294
|
-
let argText = arg.getText(ctx.sourceFile);
|
|
295
|
-
|
|
296
248
|
ctx.replacements.push({
|
|
297
249
|
end: node.end,
|
|
298
|
-
newText:
|
|
250
|
+
newText: `${ctx.ns}.signal(${arg.getText(ctx.sourceFile)})`,
|
|
299
251
|
start: node.pos
|
|
300
252
|
});
|
|
301
|
-
|
|
302
|
-
ctx.neededImports.add('signal');
|
|
303
253
|
}
|
|
304
254
|
}
|
|
305
255
|
}
|
|
@@ -328,55 +278,49 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
328
278
|
let writeCtx = isWriteContext(node);
|
|
329
279
|
|
|
330
280
|
if (writeCtx) {
|
|
331
|
-
if (binding.type !==
|
|
332
|
-
ctx.neededImports.add('set');
|
|
333
|
-
|
|
281
|
+
if (binding.type !== COMPILATION_TYPE_COMPUTED) {
|
|
334
282
|
let parent = node.parent;
|
|
335
283
|
|
|
336
284
|
if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
|
|
337
|
-
let valueText = parent.right.getText(ctx.sourceFile);
|
|
338
|
-
|
|
339
285
|
ctx.replacements.push({
|
|
340
286
|
end: parent.end,
|
|
341
|
-
newText:
|
|
287
|
+
newText: `${ctx.ns}.write(${name}, ${parent.right.getText(ctx.sourceFile)})`,
|
|
342
288
|
start: parent.pos
|
|
343
289
|
});
|
|
344
290
|
}
|
|
345
291
|
else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
|
|
346
|
-
let op =
|
|
347
|
-
valueText = parent.right.getText(ctx.sourceFile);
|
|
292
|
+
let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+';
|
|
348
293
|
|
|
349
294
|
ctx.replacements.push({
|
|
350
295
|
end: parent.end,
|
|
351
|
-
newText:
|
|
296
|
+
newText: `${ctx.ns}.write(${name}, ${name}.value ${op} ${parent.right.getText(ctx.sourceFile)})`,
|
|
352
297
|
start: parent.pos
|
|
353
298
|
});
|
|
354
299
|
}
|
|
355
300
|
else if (writeCtx === 'increment') {
|
|
356
|
-
let
|
|
357
|
-
|
|
358
|
-
delta = op === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
|
|
301
|
+
let delta = (parent as ts.PrefixUnaryExpression | ts.PostfixUnaryExpression).operator === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1',
|
|
302
|
+
isPrefix = ts.isPrefixUnaryExpression(parent);
|
|
359
303
|
|
|
360
304
|
if (ts.isExpressionStatement(parent.parent)) {
|
|
361
305
|
ctx.replacements.push({
|
|
362
306
|
end: parent.end,
|
|
363
|
-
newText:
|
|
307
|
+
newText: `${ctx.ns}.write(${name}, ${name}.value ${delta})`,
|
|
364
308
|
start: parent.pos
|
|
365
309
|
});
|
|
366
310
|
}
|
|
367
311
|
else if (isPrefix) {
|
|
368
312
|
ctx.replacements.push({
|
|
369
313
|
end: parent.end,
|
|
370
|
-
newText: `(
|
|
314
|
+
newText: `(${ctx.ns}.write(${name}, ${name}.value ${delta}), ${name}.value)`,
|
|
371
315
|
start: parent.pos
|
|
372
316
|
});
|
|
373
317
|
}
|
|
374
318
|
else {
|
|
375
|
-
let tmp =
|
|
319
|
+
let tmp = `_t${ctx.tmpCounter++}`;
|
|
376
320
|
|
|
377
321
|
ctx.replacements.push({
|
|
378
322
|
end: parent.end,
|
|
379
|
-
newText: `((${tmp}) => (
|
|
323
|
+
newText: `((${tmp}) => (${ctx.ns}.write(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
|
|
380
324
|
start: parent.pos
|
|
381
325
|
});
|
|
382
326
|
}
|
|
@@ -384,11 +328,9 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
384
328
|
}
|
|
385
329
|
}
|
|
386
330
|
else {
|
|
387
|
-
ctx.neededImports.add('read');
|
|
388
|
-
|
|
389
331
|
ctx.replacements.push({
|
|
390
332
|
end: node.end,
|
|
391
|
-
newText:
|
|
333
|
+
newText: `${ctx.ns}.read(${name})`,
|
|
392
334
|
start: node.pos
|
|
393
335
|
});
|
|
394
336
|
}
|
|
@@ -401,24 +343,18 @@ function visit(ctx: TransformContext, node: ts.Node): void {
|
|
|
401
343
|
|
|
402
344
|
function visitArg(ctx: ArgContext, node: ts.Node): void {
|
|
403
345
|
if (ts.isIdentifier(node) && node.parent) {
|
|
404
|
-
if (
|
|
405
|
-
ts.
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
|
|
346
|
+
if (
|
|
347
|
+
(ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) ||
|
|
348
|
+
(ts.isCallExpression(node.parent) && node.parent.expression === node)
|
|
349
|
+
) {
|
|
410
350
|
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
411
351
|
return;
|
|
412
352
|
}
|
|
413
353
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
if (binding) {
|
|
417
|
-
ctx.neededImports.add('read');
|
|
418
|
-
|
|
354
|
+
if (findBinding(ctx.scopedBindings, node.text, node)) {
|
|
419
355
|
ctx.innerReplacements.push({
|
|
420
356
|
end: node.end - ctx.argStart,
|
|
421
|
-
newText:
|
|
357
|
+
newText: `${ctx.ns}.read(${node.text})`,
|
|
422
358
|
start: node.getStart(ctx.sourceFile) - ctx.argStart
|
|
423
359
|
});
|
|
424
360
|
}
|
|
@@ -428,19 +364,17 @@ function visitArg(ctx: ArgContext, node: ts.Node): void {
|
|
|
428
364
|
}
|
|
429
365
|
|
|
430
366
|
|
|
431
|
-
|
|
432
|
-
sourceFile: ts.SourceFile,
|
|
433
|
-
bindings: Bindings
|
|
434
|
-
): string => {
|
|
367
|
+
export default (sourceFile: ts.SourceFile, bindings: Bindings, ns: string): string => {
|
|
435
368
|
let code = sourceFile.getFullText(),
|
|
436
369
|
ctx: TransformContext = {
|
|
437
370
|
bindings,
|
|
438
371
|
computedArgRanges: [],
|
|
439
372
|
hasReactiveImport: false,
|
|
440
|
-
|
|
373
|
+
ns,
|
|
441
374
|
replacements: [],
|
|
442
375
|
scopedBindings: [],
|
|
443
|
-
sourceFile
|
|
376
|
+
sourceFile,
|
|
377
|
+
tmpCounter: 0
|
|
444
378
|
};
|
|
445
379
|
|
|
446
380
|
visit(ctx, sourceFile);
|
|
@@ -449,14 +383,5 @@ const transformReactivePrimitives = (
|
|
|
449
383
|
return code;
|
|
450
384
|
}
|
|
451
385
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
if (ctx.neededImports.size > 0) {
|
|
455
|
-
result = addMissingImports(result, ctx.neededImports);
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return result;
|
|
386
|
+
return c.replace(code, ctx.replacements);
|
|
459
387
|
};
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
export { transformReactivePrimitives };
|
package/src/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
|
|
2
|
-
import { ReactiveArray, ReactiveObject } from './reactive';
|
|
3
2
|
import { ts } from '@esportsplus/typescript';
|
|
4
3
|
|
|
5
4
|
|
|
@@ -36,6 +35,10 @@ interface Link {
|
|
|
36
35
|
version: number;
|
|
37
36
|
}
|
|
38
37
|
|
|
38
|
+
// If we expose internals optimizing compiler may break api.
|
|
39
|
+
// Instead we will use this as a shim.
|
|
40
|
+
type Reactive<T> = T;
|
|
41
|
+
|
|
39
42
|
type Signal<T> = {
|
|
40
43
|
subs: Link | null;
|
|
41
44
|
subsTail: Link | null;
|
|
@@ -55,8 +58,7 @@ export type {
|
|
|
55
58
|
Bindings,
|
|
56
59
|
Computed,
|
|
57
60
|
Link,
|
|
58
|
-
|
|
59
|
-
ReactiveObject,
|
|
61
|
+
Reactive,
|
|
60
62
|
Signal,
|
|
61
63
|
TransformResult
|
|
62
64
|
};
|
package/test/vite.config.ts
CHANGED
|
@@ -1,8 +0,0 @@
|
|
|
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 };
|