@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
package/src/transformer/index.ts
CHANGED
|
@@ -1,209 +1,67 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { Bindings, Namespaces } from '~/types';
|
|
3
|
-
import { createArrayTransformer } from './transforms/array';
|
|
4
|
-
import { createObjectTransformer, type GeneratedClass } from './transforms/object';
|
|
5
|
-
import { createPrimitivesTransformer } from './transforms/primitives';
|
|
1
|
+
import type { Bindings, TransformResult } from '~/types';
|
|
6
2
|
import { mightNeedTransform } from './detector';
|
|
3
|
+
import { transformReactiveArrays } from './transforms/array';
|
|
4
|
+
import { transformReactiveObjects } from './transforms/object';
|
|
5
|
+
import { transformReactivePrimitives } from './transforms/primitives';
|
|
7
6
|
import { ts } from '@esportsplus/typescript';
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
ns: Namespaces
|
|
13
|
-
): (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
|
14
|
-
return (context: ts.TransformationContext) => {
|
|
15
|
-
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
|
16
|
-
if (neededImports.size === 0) {
|
|
17
|
-
return sourceFile;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
let factory = context.factory,
|
|
21
|
-
needsArray = false,
|
|
22
|
-
needsConstants = false,
|
|
23
|
-
needsReactivity = false,
|
|
24
|
-
newStatements: ts.Statement[] = [];
|
|
25
|
-
|
|
26
|
-
for (let imp of neededImports) {
|
|
27
|
-
if (imp === 'ReactiveArray') {
|
|
28
|
-
needsArray = true;
|
|
29
|
-
}
|
|
30
|
-
else if (imp === 'REACTIVE_OBJECT') {
|
|
31
|
-
needsConstants = true;
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
needsReactivity = true;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Add namespace imports
|
|
39
|
-
if (needsReactivity) {
|
|
40
|
-
newStatements.push(
|
|
41
|
-
factory.createImportDeclaration(
|
|
42
|
-
undefined,
|
|
43
|
-
factory.createImportClause(
|
|
44
|
-
false,
|
|
45
|
-
undefined,
|
|
46
|
-
factory.createNamespaceImport(factory.createIdentifier(ns.reactivity))
|
|
47
|
-
),
|
|
48
|
-
factory.createStringLiteral('@esportsplus/reactivity')
|
|
49
|
-
)
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (needsArray) {
|
|
54
|
-
newStatements.push(
|
|
55
|
-
factory.createImportDeclaration(
|
|
56
|
-
undefined,
|
|
57
|
-
factory.createImportClause(
|
|
58
|
-
false,
|
|
59
|
-
undefined,
|
|
60
|
-
factory.createNamespaceImport(factory.createIdentifier(ns.array))
|
|
61
|
-
),
|
|
62
|
-
factory.createStringLiteral('@esportsplus/reactivity/reactive/array')
|
|
63
|
-
)
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (needsConstants) {
|
|
68
|
-
newStatements.push(
|
|
69
|
-
factory.createImportDeclaration(
|
|
70
|
-
undefined,
|
|
71
|
-
factory.createImportClause(
|
|
72
|
-
false,
|
|
73
|
-
undefined,
|
|
74
|
-
factory.createNamespaceImport(factory.createIdentifier(ns.constants))
|
|
75
|
-
),
|
|
76
|
-
factory.createStringLiteral('@esportsplus/reactivity/constants')
|
|
77
|
-
)
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Filter out original @esportsplus/reactivity imports (they lose symbol bindings)
|
|
82
|
-
let filteredStatements: ts.Statement[] = [],
|
|
83
|
-
insertIndex = 0,
|
|
84
|
-
statements = sourceFile.statements;
|
|
85
|
-
|
|
86
|
-
for (let i = 0, n = statements.length; i < n; i++) {
|
|
87
|
-
let stmt = statements[i];
|
|
88
|
-
|
|
89
|
-
if (ts.isImportDeclaration(stmt)) {
|
|
90
|
-
insertIndex = filteredStatements.length + 1;
|
|
91
|
-
|
|
92
|
-
// Skip imports from @esportsplus/reactivity packages
|
|
93
|
-
if (ts.isStringLiteral(stmt.moduleSpecifier)) {
|
|
94
|
-
let module = stmt.moduleSpecifier.text;
|
|
95
|
-
|
|
96
|
-
if (
|
|
97
|
-
module === '@esportsplus/reactivity' ||
|
|
98
|
-
module === '@esportsplus/reactivity/reactive/array' ||
|
|
99
|
-
module === '@esportsplus/reactivity/constants'
|
|
100
|
-
) {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
filteredStatements.push(stmt);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
let updatedStatements = [
|
|
110
|
-
...filteredStatements.slice(0, insertIndex),
|
|
111
|
-
...newStatements,
|
|
112
|
-
...filteredStatements.slice(insertIndex)
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
return factory.updateSourceFile(sourceFile, updatedStatements);
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function insertClassesTransformer(
|
|
121
|
-
generatedClasses: GeneratedClass[]
|
|
122
|
-
): (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => ts.SourceFile {
|
|
123
|
-
return (context: ts.TransformationContext) => {
|
|
9
|
+
const createTransformer = (): ts.TransformerFactory<ts.SourceFile> => {
|
|
10
|
+
return () => {
|
|
124
11
|
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
|
125
|
-
|
|
126
|
-
return sourceFile;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
let factory = context.factory;
|
|
130
|
-
|
|
131
|
-
// Find position after imports
|
|
132
|
-
let insertIndex = 0,
|
|
133
|
-
statements = sourceFile.statements;
|
|
12
|
+
let result = transform(sourceFile);
|
|
134
13
|
|
|
135
|
-
|
|
136
|
-
if (ts.isImportDeclaration(statements[i])) {
|
|
137
|
-
insertIndex = i + 1;
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
let classDecls = generatedClasses.map(gc => gc.classDecl),
|
|
145
|
-
updatedStatements = [
|
|
146
|
-
...statements.slice(0, insertIndex),
|
|
147
|
-
...classDecls,
|
|
148
|
-
...statements.slice(insertIndex)
|
|
149
|
-
];
|
|
150
|
-
|
|
151
|
-
return factory.updateSourceFile(sourceFile, updatedStatements);
|
|
14
|
+
return result.transformed ? result.sourceFile : sourceFile;
|
|
152
15
|
};
|
|
153
16
|
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const createTransformer = (): ts.TransformerFactory<ts.SourceFile> => {
|
|
158
|
-
return (context: ts.TransformationContext) => {
|
|
159
|
-
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
|
160
|
-
let code = sourceFile.getFullText();
|
|
161
|
-
|
|
162
|
-
if (!mightNeedTransform(code)) {
|
|
163
|
-
return sourceFile;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
let bindings: Bindings = new Map(),
|
|
167
|
-
generatedClasses: GeneratedClass[] = [],
|
|
168
|
-
neededImports = new Set<string>(),
|
|
169
|
-
ns: Namespaces = {
|
|
170
|
-
array: uid('ra'),
|
|
171
|
-
constants: uid('rc'),
|
|
172
|
-
reactivity: uid('r')
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
// Run object transformer first (generates classes, tracks array bindings)
|
|
176
|
-
let objectTransformer = createObjectTransformer(bindings, neededImports, generatedClasses, ns)(context);
|
|
17
|
+
};
|
|
177
18
|
|
|
178
|
-
|
|
19
|
+
const transform = (sourceFile: ts.SourceFile): TransformResult => {
|
|
20
|
+
let bindings: Bindings = new Map(),
|
|
21
|
+
code = sourceFile.getFullText(),
|
|
22
|
+
current = sourceFile,
|
|
23
|
+
original = code,
|
|
24
|
+
result: string;
|
|
179
25
|
|
|
180
|
-
|
|
181
|
-
|
|
26
|
+
if (!mightNeedTransform(code)) {
|
|
27
|
+
return { code, sourceFile, transformed: false };
|
|
28
|
+
}
|
|
182
29
|
|
|
183
|
-
|
|
30
|
+
// Run all transforms, only re-parse between transforms if code changed
|
|
31
|
+
result = transformReactiveObjects(current, bindings);
|
|
184
32
|
|
|
185
|
-
|
|
186
|
-
|
|
33
|
+
if (result !== code) {
|
|
34
|
+
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
35
|
+
code = result;
|
|
36
|
+
}
|
|
187
37
|
|
|
188
|
-
|
|
38
|
+
result = transformReactiveArrays(current, bindings);
|
|
189
39
|
|
|
190
|
-
|
|
191
|
-
|
|
40
|
+
if (result !== code) {
|
|
41
|
+
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
42
|
+
code = result;
|
|
43
|
+
}
|
|
192
44
|
|
|
193
|
-
|
|
45
|
+
result = transformReactivePrimitives(current, bindings);
|
|
194
46
|
|
|
195
|
-
|
|
196
|
-
|
|
47
|
+
if (result !== code) {
|
|
48
|
+
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
49
|
+
code = result;
|
|
50
|
+
}
|
|
197
51
|
|
|
198
|
-
|
|
52
|
+
if (code === original) {
|
|
53
|
+
return { code, sourceFile, transformed: false };
|
|
54
|
+
}
|
|
199
55
|
|
|
200
|
-
|
|
201
|
-
|
|
56
|
+
return {
|
|
57
|
+
code,
|
|
58
|
+
sourceFile: current,
|
|
59
|
+
transformed: true
|
|
202
60
|
};
|
|
203
61
|
};
|
|
204
62
|
|
|
205
63
|
|
|
206
|
-
export { createTransformer, mightNeedTransform };
|
|
207
|
-
export {
|
|
208
|
-
export {
|
|
209
|
-
export {
|
|
64
|
+
export { createTransformer, mightNeedTransform, transform };
|
|
65
|
+
export { transformReactiveArrays } from './transforms/array';
|
|
66
|
+
export { transformReactiveObjects } from './transforms/object';
|
|
67
|
+
export { transformReactivePrimitives } from './transforms/primitives';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import {
|
|
2
|
+
import { mightNeedTransform, transform } from '~/transformer';
|
|
3
3
|
import type { Plugin } from 'vite';
|
|
4
4
|
import { ts } from '@esportsplus/typescript';
|
|
5
5
|
|
|
@@ -19,22 +19,14 @@ export default (): Plugin => {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
|
-
let
|
|
23
|
-
|
|
24
|
-
transformer = createTransformer(),
|
|
25
|
-
result = ts.transform(sourceFile, [transformer]),
|
|
26
|
-
transformed = result.transformed[0];
|
|
22
|
+
let sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true),
|
|
23
|
+
result = transform(sourceFile);
|
|
27
24
|
|
|
28
|
-
if (transformed
|
|
29
|
-
result.dispose();
|
|
25
|
+
if (!result.transformed) {
|
|
30
26
|
return null;
|
|
31
27
|
}
|
|
32
28
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
result.dispose();
|
|
36
|
-
|
|
37
|
-
return { code: output, map: null };
|
|
29
|
+
return { code: result.code, map: null };
|
|
38
30
|
}
|
|
39
31
|
catch (error) {
|
|
40
32
|
console.error(`@esportsplus/reactivity: Error transforming ${id}:`, error);
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { Bindings } from '~/types';
|
|
2
|
-
import {
|
|
2
|
+
import { applyReplacements, Replacement } from './utilities';
|
|
3
3
|
import { ts } from '@esportsplus/typescript';
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
interface TransformContext {
|
|
7
7
|
bindings: Bindings;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
replacements: Replacement[];
|
|
9
|
+
sourceFile: ts.SourceFile;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
|
|
@@ -42,10 +42,6 @@ function getPropertyPath(node: ts.PropertyAccessExpression): string | null {
|
|
|
42
42
|
function isAssignmentTarget(node: ts.Node): boolean {
|
|
43
43
|
let parent = node.parent;
|
|
44
44
|
|
|
45
|
-
if (!parent) {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
45
|
if (
|
|
50
46
|
(ts.isBinaryExpression(parent) && parent.left === node) ||
|
|
51
47
|
ts.isPostfixUnaryExpression(parent) ||
|
|
@@ -57,8 +53,7 @@ function isAssignmentTarget(node: ts.Node): boolean {
|
|
|
57
53
|
return false;
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
function visit(ctx: TransformContext, node: ts.Node):
|
|
61
|
-
// Track array bindings from variable declarations
|
|
56
|
+
function visit(ctx: TransformContext, node: ts.Node): void {
|
|
62
57
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
63
58
|
if (ts.isIdentifier(node.initializer) && ctx.bindings.get(node.initializer.text) === 'array') {
|
|
64
59
|
ctx.bindings.set(node.name.text, 'array');
|
|
@@ -73,7 +68,6 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
73
68
|
}
|
|
74
69
|
}
|
|
75
70
|
|
|
76
|
-
// Track array bindings from function parameters with ReactiveArray type
|
|
77
71
|
if ((ts.isFunctionDeclaration(node) || ts.isArrowFunction(node)) && node.parameters) {
|
|
78
72
|
for (let i = 0, n = node.parameters.length; i < n; i++) {
|
|
79
73
|
let param = node.parameters[i];
|
|
@@ -89,7 +83,6 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
89
83
|
}
|
|
90
84
|
}
|
|
91
85
|
|
|
92
|
-
// Transform array.length → array.$length()
|
|
93
86
|
if (
|
|
94
87
|
ts.isPropertyAccessExpression(node) &&
|
|
95
88
|
node.name.text === 'length' &&
|
|
@@ -98,14 +91,16 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
98
91
|
let name = getExpressionName(node.expression);
|
|
99
92
|
|
|
100
93
|
if (name && ctx.bindings.get(name) === 'array') {
|
|
101
|
-
|
|
102
|
-
let transformedExpr = ts.visitEachChild(node.expression, n => visit(ctx, n), ctx.context) as ts.Expression;
|
|
94
|
+
let objText = node.expression.getText(ctx.sourceFile);
|
|
103
95
|
|
|
104
|
-
|
|
96
|
+
ctx.replacements.push({
|
|
97
|
+
end: node.end,
|
|
98
|
+
newText: `${objText}.$length()`,
|
|
99
|
+
start: node.pos
|
|
100
|
+
});
|
|
105
101
|
}
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
// Transform array[index] = value → array.$set(index, value)
|
|
109
104
|
if (
|
|
110
105
|
ts.isBinaryExpression(node) &&
|
|
111
106
|
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
@@ -115,33 +110,34 @@ function visit(ctx: TransformContext, node: ts.Node): ts.Node {
|
|
|
115
110
|
objName = getExpressionName(elemAccess.expression);
|
|
116
111
|
|
|
117
112
|
if (objName && ctx.bindings.get(objName) === 'array') {
|
|
118
|
-
let
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
113
|
+
let indexText = elemAccess.argumentExpression.getText(ctx.sourceFile),
|
|
114
|
+
objText = elemAccess.expression.getText(ctx.sourceFile),
|
|
115
|
+
valueText = node.right.getText(ctx.sourceFile);
|
|
116
|
+
|
|
117
|
+
ctx.replacements.push({
|
|
118
|
+
end: node.end,
|
|
119
|
+
newText: `${objText}.$set(${indexText}, ${valueText})`,
|
|
120
|
+
start: node.pos
|
|
121
|
+
});
|
|
123
122
|
}
|
|
124
123
|
}
|
|
125
124
|
|
|
126
|
-
|
|
125
|
+
ts.forEachChild(node, n => visit(ctx, n));
|
|
127
126
|
}
|
|
128
127
|
|
|
129
128
|
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
bindings,
|
|
137
|
-
context,
|
|
138
|
-
factory: context.factory
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
return ts.visitNode(sourceFile, n => visit(ctx, n)) as ts.SourceFile;
|
|
129
|
+
const transformReactiveArrays = (sourceFile: ts.SourceFile, bindings: Bindings): string => {
|
|
130
|
+
let code = sourceFile.getFullText(),
|
|
131
|
+
ctx: TransformContext = {
|
|
132
|
+
bindings,
|
|
133
|
+
replacements: [],
|
|
134
|
+
sourceFile
|
|
142
135
|
};
|
|
143
|
-
|
|
136
|
+
|
|
137
|
+
visit(ctx, sourceFile);
|
|
138
|
+
|
|
139
|
+
return applyReplacements(code, ctx.replacements);
|
|
144
140
|
};
|
|
145
141
|
|
|
146
142
|
|
|
147
|
-
export {
|
|
143
|
+
export { transformReactiveArrays };
|