@esportsplus/reactivity 0.25.2 → 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 -15
- package/build/reactive/index.js +2 -2
- 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 -19
- package/build/transformer/transforms/object.d.ts +3 -3
- package/build/transformer/transforms/object.js +41 -57
- package/build/transformer/transforms/primitives.d.ts +3 -3
- package/build/transformer/transforms/primitives.js +60 -117
- 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 -19
- 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 -33
- package/src/transformer/transforms/object.ts +55 -79
- package/src/transformer/transforms/primitives.ts +70 -144
- 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
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import { uid } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import { addMissingImports, applyReplacements } from './utilities.js';
|
|
3
1
|
import { ts } from '@esportsplus/typescript';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
{ module: '@esportsplus/reactivity/constants', specifier: 'REACTIVE_OBJECT' },
|
|
7
|
-
{ module: '@esportsplus/reactivity/reactive/array', specifier: 'ReactiveArray' }
|
|
8
|
-
];
|
|
2
|
+
import { code as c } from '@esportsplus/typescript/transformer';
|
|
3
|
+
import { COMPILATION_TYPE_ARRAY, COMPILATION_TYPE_COMPUTED, COMPILATION_TYPE_SIGNAL, PACKAGE } from '../../constants.js';
|
|
9
4
|
function analyzeProperty(prop, sourceFile) {
|
|
10
5
|
if (!ts.isPropertyAssignment(prop)) {
|
|
11
6
|
return null;
|
|
@@ -19,33 +14,39 @@ function analyzeProperty(prop, sourceFile) {
|
|
|
19
14
|
}
|
|
20
15
|
let value = prop.initializer, valueText = value.getText(sourceFile);
|
|
21
16
|
if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
|
|
22
|
-
return { key, type:
|
|
17
|
+
return { key, type: COMPILATION_TYPE_COMPUTED, valueText };
|
|
23
18
|
}
|
|
24
19
|
if (ts.isArrayLiteralExpression(value)) {
|
|
25
|
-
|
|
20
|
+
let elements = value.elements, elementsText = '';
|
|
21
|
+
for (let i = 0, n = elements.length; i < n; i++) {
|
|
22
|
+
if (i > 0) {
|
|
23
|
+
elementsText += ', ';
|
|
24
|
+
}
|
|
25
|
+
elementsText += elements[i].getText(sourceFile);
|
|
26
|
+
}
|
|
27
|
+
return { key, type: COMPILATION_TYPE_ARRAY, valueText: elementsText };
|
|
26
28
|
}
|
|
27
|
-
return { key, type:
|
|
29
|
+
return { key, type: COMPILATION_TYPE_SIGNAL, valueText };
|
|
28
30
|
}
|
|
29
|
-
function buildClassCode(className, properties) {
|
|
30
|
-
let accessors = [], disposeStatements = [], fields = [];
|
|
31
|
-
fields.push(`[REACTIVE_OBJECT] = true;`);
|
|
31
|
+
function buildClassCode(className, properties, ns) {
|
|
32
|
+
let accessors = [], disposeStatements = [], fields = [], paramCounter = 0;
|
|
33
|
+
fields.push(`[${ns}.REACTIVE_OBJECT] = true;`);
|
|
32
34
|
for (let i = 0, n = properties.length; i < n; i++) {
|
|
33
35
|
let { key, type, valueText } = properties[i];
|
|
34
|
-
if (type ===
|
|
35
|
-
let param =
|
|
36
|
-
fields.push(`#${key} = signal(${valueText});`);
|
|
37
|
-
accessors.push(`get ${key}() { return read(this.#${key}); }`);
|
|
38
|
-
accessors.push(`set ${key}(${param}) {
|
|
36
|
+
if (type === COMPILATION_TYPE_SIGNAL) {
|
|
37
|
+
let param = `_v${paramCounter++}`;
|
|
38
|
+
fields.push(`#${key} = ${ns}.signal(${valueText});`);
|
|
39
|
+
accessors.push(`get ${key}() { return ${ns}.read(this.#${key}); }`);
|
|
40
|
+
accessors.push(`set ${key}(${param}) { ${ns}.write(this.#${key}, ${param}); }`);
|
|
39
41
|
}
|
|
40
|
-
else if (type ===
|
|
41
|
-
|
|
42
|
-
fields.push(`${key} = new ReactiveArray(${elements});`);
|
|
42
|
+
else if (type === COMPILATION_TYPE_ARRAY) {
|
|
43
|
+
fields.push(`${key} = new ${ns}.ReactiveArray(${valueText});`);
|
|
43
44
|
disposeStatements.push(`this.${key}.dispose();`);
|
|
44
45
|
}
|
|
45
|
-
else if (type ===
|
|
46
|
-
fields.push(`#${key}
|
|
47
|
-
accessors.push(`get ${key}() { return read(this.#${key} ??= computed(${valueText})); }`);
|
|
48
|
-
disposeStatements.push(`if (this.#${key}) dispose(this.#${key});`);
|
|
46
|
+
else if (type === COMPILATION_TYPE_COMPUTED) {
|
|
47
|
+
fields.push(`#${key} = null;`);
|
|
48
|
+
accessors.push(`get ${key}() { return ${ns}.read(this.#${key} ??= ${ns}.computed(${valueText})); }`);
|
|
49
|
+
disposeStatements.push(`if (this.#${key}) ${ns}.dispose(this.#${key});`);
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
return `
|
|
@@ -63,7 +64,7 @@ function visit(ctx, node) {
|
|
|
63
64
|
if (ts.isImportDeclaration(node)) {
|
|
64
65
|
ctx.lastImportEnd = node.end;
|
|
65
66
|
if (ts.isStringLiteral(node.moduleSpecifier) &&
|
|
66
|
-
node.moduleSpecifier.text.includes(
|
|
67
|
+
node.moduleSpecifier.text.includes(PACKAGE)) {
|
|
67
68
|
let clause = node.importClause;
|
|
68
69
|
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
69
70
|
let elements = clause.namedBindings.elements;
|
|
@@ -82,14 +83,11 @@ function visit(ctx, node) {
|
|
|
82
83
|
node.expression.text === 'reactive') {
|
|
83
84
|
let arg = node.arguments[0];
|
|
84
85
|
if (arg && ts.isObjectLiteralExpression(arg)) {
|
|
85
|
-
let varName = null;
|
|
86
|
-
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
86
|
+
let properties = [], props = arg.properties, varName = null;
|
|
87
|
+
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
87
88
|
varName = node.parent.name.text;
|
|
88
89
|
ctx.bindings.set(varName, 'object');
|
|
89
90
|
}
|
|
90
|
-
let needsImports = new Set(), properties = [];
|
|
91
|
-
needsImports.add('REACTIVE_OBJECT');
|
|
92
|
-
let props = arg.properties;
|
|
93
91
|
for (let i = 0, n = props.length; i < n; i++) {
|
|
94
92
|
let prop = props[i];
|
|
95
93
|
if (ts.isSpreadAssignment(prop)) {
|
|
@@ -102,28 +100,14 @@ function visit(ctx, node) {
|
|
|
102
100
|
return;
|
|
103
101
|
}
|
|
104
102
|
properties.push(analyzed);
|
|
105
|
-
if (analyzed.type ===
|
|
106
|
-
|
|
107
|
-
needsImports.add('set');
|
|
108
|
-
needsImports.add('signal');
|
|
109
|
-
}
|
|
110
|
-
else if (analyzed.type === 'array') {
|
|
111
|
-
needsImports.add('ReactiveArray');
|
|
112
|
-
if (varName) {
|
|
113
|
-
ctx.bindings.set(`${varName}.${analyzed.key}`, 'array');
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else if (analyzed.type === 'computed') {
|
|
117
|
-
needsImports.add('computed');
|
|
118
|
-
needsImports.add('dispose');
|
|
119
|
-
needsImports.add('read');
|
|
103
|
+
if (analyzed.type === COMPILATION_TYPE_ARRAY && varName) {
|
|
104
|
+
ctx.bindings.set(`${varName}.${analyzed.key}`, COMPILATION_TYPE_ARRAY);
|
|
120
105
|
}
|
|
121
106
|
}
|
|
122
|
-
needsImports.forEach(imp => ctx.allNeededImports.add(imp));
|
|
123
107
|
ctx.calls.push({
|
|
108
|
+
className: `_RO${ctx.classCounter++}`,
|
|
124
109
|
end: node.end,
|
|
125
|
-
|
|
126
|
-
needsImports,
|
|
110
|
+
properties,
|
|
127
111
|
start: node.pos,
|
|
128
112
|
varName
|
|
129
113
|
});
|
|
@@ -131,33 +115,33 @@ function visit(ctx, node) {
|
|
|
131
115
|
}
|
|
132
116
|
ts.forEachChild(node, n => visit(ctx, n));
|
|
133
117
|
}
|
|
134
|
-
|
|
118
|
+
export default (sourceFile, bindings, ns) => {
|
|
135
119
|
let code = sourceFile.getFullText(), ctx = {
|
|
136
|
-
allNeededImports: new Set(),
|
|
137
120
|
bindings,
|
|
138
121
|
calls: [],
|
|
122
|
+
classCounter: 0,
|
|
139
123
|
hasReactiveImport: false,
|
|
140
124
|
lastImportEnd: 0,
|
|
125
|
+
ns,
|
|
141
126
|
sourceFile
|
|
142
127
|
};
|
|
143
128
|
visit(ctx, sourceFile);
|
|
144
129
|
if (ctx.calls.length === 0) {
|
|
145
130
|
return code;
|
|
146
131
|
}
|
|
147
|
-
let replacements = [];
|
|
132
|
+
let classes = ctx.calls.map(c => buildClassCode(c.className, c.properties, ns)).join('\n'), replacements = [];
|
|
148
133
|
replacements.push({
|
|
149
134
|
end: ctx.lastImportEnd,
|
|
150
|
-
newText: code.substring(0, ctx.lastImportEnd) + '\n' +
|
|
135
|
+
newText: code.substring(0, ctx.lastImportEnd) + '\n' + classes + '\n',
|
|
151
136
|
start: 0
|
|
152
137
|
});
|
|
153
138
|
for (let i = 0, n = ctx.calls.length; i < n; i++) {
|
|
154
|
-
let call = ctx.calls[i]
|
|
139
|
+
let call = ctx.calls[i];
|
|
155
140
|
replacements.push({
|
|
156
141
|
end: call.end,
|
|
157
|
-
newText: ` new ${
|
|
142
|
+
newText: ` new ${call.className}()`,
|
|
158
143
|
start: call.start
|
|
159
144
|
});
|
|
160
145
|
}
|
|
161
|
-
return
|
|
146
|
+
return c.replace(code, replacements);
|
|
162
147
|
};
|
|
163
|
-
export { transformReactiveObjects };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Bindings } from '../../types.js';
|
|
2
1
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import type { Bindings } from '../../types.js';
|
|
3
|
+
declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, ns: string) => string;
|
|
4
|
+
export default _default;
|
|
@@ -1,14 +1,31 @@
|
|
|
1
|
-
import { uid } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import { addMissingImports, applyReplacements } from './utilities.js';
|
|
3
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
+
import { code as c } from '@esportsplus/typescript/transformer';
|
|
3
|
+
import { COMPILATION_TYPE_COMPUTED, COMPILATION_ENTRYPOINT, COMPILATION_TYPE_SIGNAL, PACKAGE } from '../../constants.js';
|
|
4
|
+
let COMPOUND_OPERATORS = new Map([
|
|
5
|
+
[ts.SyntaxKind.AmpersandAmpersandEqualsToken, '&&'],
|
|
6
|
+
[ts.SyntaxKind.AmpersandEqualsToken, '&'],
|
|
7
|
+
[ts.SyntaxKind.AsteriskAsteriskEqualsToken, '**'],
|
|
8
|
+
[ts.SyntaxKind.AsteriskEqualsToken, '*'],
|
|
9
|
+
[ts.SyntaxKind.BarBarEqualsToken, '||'],
|
|
10
|
+
[ts.SyntaxKind.BarEqualsToken, '|'],
|
|
11
|
+
[ts.SyntaxKind.CaretEqualsToken, '^'],
|
|
12
|
+
[ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, '>>'],
|
|
13
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, '>>>'],
|
|
14
|
+
[ts.SyntaxKind.LessThanLessThanEqualsToken, '<<'],
|
|
15
|
+
[ts.SyntaxKind.MinusEqualsToken, '-'],
|
|
16
|
+
[ts.SyntaxKind.PercentEqualsToken, '%'],
|
|
17
|
+
[ts.SyntaxKind.PlusEqualsToken, '+'],
|
|
18
|
+
[ts.SyntaxKind.QuestionQuestionEqualsToken, '??'],
|
|
19
|
+
[ts.SyntaxKind.SlashEqualsToken, '/']
|
|
20
|
+
]);
|
|
4
21
|
function classifyReactiveArg(arg) {
|
|
5
22
|
if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
|
|
6
|
-
return
|
|
23
|
+
return COMPILATION_TYPE_COMPUTED;
|
|
7
24
|
}
|
|
8
25
|
if (ts.isObjectLiteralExpression(arg) || ts.isArrayLiteralExpression(arg)) {
|
|
9
26
|
return null;
|
|
10
27
|
}
|
|
11
|
-
return
|
|
28
|
+
return COMPILATION_TYPE_SIGNAL;
|
|
12
29
|
}
|
|
13
30
|
function findBinding(bindings, name, node) {
|
|
14
31
|
for (let i = 0, n = bindings.length; i < n; i++) {
|
|
@@ -36,56 +53,6 @@ function findEnclosingScope(node) {
|
|
|
36
53
|
}
|
|
37
54
|
return node.getSourceFile();
|
|
38
55
|
}
|
|
39
|
-
function getCompoundOperator(kind) {
|
|
40
|
-
if (kind === ts.SyntaxKind.PlusEqualsToken) {
|
|
41
|
-
return '+';
|
|
42
|
-
}
|
|
43
|
-
else if (kind === ts.SyntaxKind.MinusEqualsToken) {
|
|
44
|
-
return '-';
|
|
45
|
-
}
|
|
46
|
-
else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
|
|
47
|
-
return '*';
|
|
48
|
-
}
|
|
49
|
-
else if (kind === ts.SyntaxKind.SlashEqualsToken) {
|
|
50
|
-
return '/';
|
|
51
|
-
}
|
|
52
|
-
else if (kind === ts.SyntaxKind.PercentEqualsToken) {
|
|
53
|
-
return '%';
|
|
54
|
-
}
|
|
55
|
-
else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
|
|
56
|
-
return '**';
|
|
57
|
-
}
|
|
58
|
-
else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
|
|
59
|
-
return '&';
|
|
60
|
-
}
|
|
61
|
-
else if (kind === ts.SyntaxKind.BarEqualsToken) {
|
|
62
|
-
return '|';
|
|
63
|
-
}
|
|
64
|
-
else if (kind === ts.SyntaxKind.CaretEqualsToken) {
|
|
65
|
-
return '^';
|
|
66
|
-
}
|
|
67
|
-
else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
|
|
68
|
-
return '<<';
|
|
69
|
-
}
|
|
70
|
-
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
|
|
71
|
-
return '>>';
|
|
72
|
-
}
|
|
73
|
-
else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
|
|
74
|
-
return '>>>';
|
|
75
|
-
}
|
|
76
|
-
else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
|
|
77
|
-
return '&&';
|
|
78
|
-
}
|
|
79
|
-
else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
|
|
80
|
-
return '||';
|
|
81
|
-
}
|
|
82
|
-
else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
83
|
-
return '??';
|
|
84
|
-
}
|
|
85
|
-
else {
|
|
86
|
-
return '+';
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
56
|
function isInComputedRange(ranges, start, end) {
|
|
90
57
|
for (let i = 0, n = ranges.length; i < n; i++) {
|
|
91
58
|
let r = ranges[i];
|
|
@@ -97,10 +64,7 @@ function isInComputedRange(ranges, start, end) {
|
|
|
97
64
|
}
|
|
98
65
|
function isInDeclarationInit(node) {
|
|
99
66
|
let parent = node.parent;
|
|
100
|
-
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
return false;
|
|
67
|
+
return ts.isVariableDeclaration(parent) && parent.initializer === node;
|
|
104
68
|
}
|
|
105
69
|
function isInScope(reference, binding) {
|
|
106
70
|
let current = reference;
|
|
@@ -119,7 +83,7 @@ function isReactiveReassignment(node) {
|
|
|
119
83
|
parent.right === node &&
|
|
120
84
|
ts.isCallExpression(node) &&
|
|
121
85
|
ts.isIdentifier(node.expression) &&
|
|
122
|
-
node.expression.text ===
|
|
86
|
+
node.expression.text === COMPILATION_ENTRYPOINT) {
|
|
123
87
|
return true;
|
|
124
88
|
}
|
|
125
89
|
return false;
|
|
@@ -131,12 +95,7 @@ function isWriteContext(node) {
|
|
|
131
95
|
if (op === ts.SyntaxKind.EqualsToken) {
|
|
132
96
|
return 'simple';
|
|
133
97
|
}
|
|
134
|
-
if (
|
|
135
|
-
return 'compound';
|
|
136
|
-
}
|
|
137
|
-
if (op === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
138
|
-
op === ts.SyntaxKind.BarBarEqualsToken ||
|
|
139
|
-
op === ts.SyntaxKind.QuestionQuestionEqualsToken) {
|
|
98
|
+
if (COMPOUND_OPERATORS.has(op)) {
|
|
140
99
|
return 'compound';
|
|
141
100
|
}
|
|
142
101
|
}
|
|
@@ -151,11 +110,11 @@ function isWriteContext(node) {
|
|
|
151
110
|
function visit(ctx, node) {
|
|
152
111
|
if (ts.isImportDeclaration(node) &&
|
|
153
112
|
ts.isStringLiteral(node.moduleSpecifier) &&
|
|
154
|
-
node.moduleSpecifier.text.includes(
|
|
113
|
+
node.moduleSpecifier.text.includes(PACKAGE)) {
|
|
155
114
|
let clause = node.importClause;
|
|
156
115
|
if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
|
|
157
116
|
for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
|
|
158
|
-
if (clause.namedBindings.elements[i].name.text ===
|
|
117
|
+
if (clause.namedBindings.elements[i].name.text === COMPILATION_ENTRYPOINT) {
|
|
159
118
|
ctx.hasReactiveImport = true;
|
|
160
119
|
break;
|
|
161
120
|
}
|
|
@@ -165,15 +124,16 @@ function visit(ctx, node) {
|
|
|
165
124
|
if (ctx.hasReactiveImport &&
|
|
166
125
|
ts.isCallExpression(node) &&
|
|
167
126
|
ts.isIdentifier(node.expression) &&
|
|
168
|
-
node.expression.text ===
|
|
127
|
+
node.expression.text === COMPILATION_ENTRYPOINT &&
|
|
169
128
|
node.arguments.length > 0) {
|
|
170
129
|
let arg = node.arguments[0], classification = classifyReactiveArg(arg);
|
|
171
130
|
if (classification) {
|
|
172
131
|
let varName = null;
|
|
173
|
-
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
132
|
+
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
174
133
|
varName = node.parent.name.text;
|
|
175
134
|
}
|
|
176
|
-
else if (
|
|
135
|
+
else if (node.parent &&
|
|
136
|
+
ts.isBinaryExpression(node.parent) &&
|
|
177
137
|
node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
178
138
|
ts.isIdentifier(node.parent.left)) {
|
|
179
139
|
varName = node.parent.left.text;
|
|
@@ -183,39 +143,34 @@ function visit(ctx, node) {
|
|
|
183
143
|
ctx.scopedBindings.push({ name: varName, scope, type: classification });
|
|
184
144
|
ctx.bindings.set(varName, classification);
|
|
185
145
|
}
|
|
186
|
-
if (classification ===
|
|
187
|
-
ctx.
|
|
188
|
-
|
|
189
|
-
start: arg.getStart(ctx.sourceFile)
|
|
190
|
-
});
|
|
146
|
+
if (classification === COMPILATION_TYPE_COMPUTED) {
|
|
147
|
+
let argStart = arg.getStart(ctx.sourceFile);
|
|
148
|
+
ctx.computedArgRanges.push({ end: arg.end, start: argStart });
|
|
191
149
|
let argCtx = {
|
|
192
|
-
argStart
|
|
150
|
+
argStart,
|
|
193
151
|
innerReplacements: [],
|
|
194
|
-
|
|
152
|
+
ns: ctx.ns,
|
|
195
153
|
scopedBindings: ctx.scopedBindings,
|
|
196
154
|
sourceFile: ctx.sourceFile
|
|
197
155
|
};
|
|
198
156
|
visitArg(argCtx, arg);
|
|
199
|
-
let argText =
|
|
157
|
+
let argText = c.replace(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
|
|
200
158
|
ctx.replacements.push({
|
|
201
159
|
end: node.end,
|
|
202
|
-
newText:
|
|
160
|
+
newText: `${ctx.ns}.computed(${argText})`,
|
|
203
161
|
start: node.pos
|
|
204
162
|
});
|
|
205
|
-
ctx.neededImports.add('computed');
|
|
206
163
|
}
|
|
207
164
|
else {
|
|
208
|
-
let argText = arg.getText(ctx.sourceFile);
|
|
209
165
|
ctx.replacements.push({
|
|
210
166
|
end: node.end,
|
|
211
|
-
newText:
|
|
167
|
+
newText: `${ctx.ns}.signal(${arg.getText(ctx.sourceFile)})`,
|
|
212
168
|
start: node.pos
|
|
213
169
|
});
|
|
214
|
-
ctx.neededImports.add('signal');
|
|
215
170
|
}
|
|
216
171
|
}
|
|
217
172
|
}
|
|
218
|
-
if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
|
|
173
|
+
if (ts.isIdentifier(node) && node.parent && !isInDeclarationInit(node.parent)) {
|
|
219
174
|
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
|
|
220
175
|
ts.forEachChild(node, n => visit(ctx, n));
|
|
221
176
|
return;
|
|
@@ -226,51 +181,49 @@ function visit(ctx, node) {
|
|
|
226
181
|
return;
|
|
227
182
|
}
|
|
228
183
|
let binding = findBinding(ctx.scopedBindings, node.text, node), name = node.text;
|
|
229
|
-
if (binding) {
|
|
184
|
+
if (binding && node.parent) {
|
|
230
185
|
if (!isReactiveReassignment(node.parent) &&
|
|
231
186
|
!(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
|
|
232
187
|
let writeCtx = isWriteContext(node);
|
|
233
188
|
if (writeCtx) {
|
|
234
|
-
if (binding.type !==
|
|
235
|
-
ctx.neededImports.add('set');
|
|
189
|
+
if (binding.type !== COMPILATION_TYPE_COMPUTED) {
|
|
236
190
|
let parent = node.parent;
|
|
237
191
|
if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
|
|
238
|
-
let valueText = parent.right.getText(ctx.sourceFile);
|
|
239
192
|
ctx.replacements.push({
|
|
240
193
|
end: parent.end,
|
|
241
|
-
newText:
|
|
194
|
+
newText: `${ctx.ns}.write(${name}, ${parent.right.getText(ctx.sourceFile)})`,
|
|
242
195
|
start: parent.pos
|
|
243
196
|
});
|
|
244
197
|
}
|
|
245
198
|
else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
|
|
246
|
-
let op =
|
|
199
|
+
let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+';
|
|
247
200
|
ctx.replacements.push({
|
|
248
201
|
end: parent.end,
|
|
249
|
-
newText:
|
|
202
|
+
newText: `${ctx.ns}.write(${name}, ${name}.value ${op} ${parent.right.getText(ctx.sourceFile)})`,
|
|
250
203
|
start: parent.pos
|
|
251
204
|
});
|
|
252
205
|
}
|
|
253
206
|
else if (writeCtx === 'increment') {
|
|
254
|
-
let
|
|
207
|
+
let delta = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1', isPrefix = ts.isPrefixUnaryExpression(parent);
|
|
255
208
|
if (ts.isExpressionStatement(parent.parent)) {
|
|
256
209
|
ctx.replacements.push({
|
|
257
210
|
end: parent.end,
|
|
258
|
-
newText:
|
|
211
|
+
newText: `${ctx.ns}.write(${name}, ${name}.value ${delta})`,
|
|
259
212
|
start: parent.pos
|
|
260
213
|
});
|
|
261
214
|
}
|
|
262
215
|
else if (isPrefix) {
|
|
263
216
|
ctx.replacements.push({
|
|
264
217
|
end: parent.end,
|
|
265
|
-
newText: `(
|
|
218
|
+
newText: `(${ctx.ns}.write(${name}, ${name}.value ${delta}), ${name}.value)`,
|
|
266
219
|
start: parent.pos
|
|
267
220
|
});
|
|
268
221
|
}
|
|
269
222
|
else {
|
|
270
|
-
let tmp =
|
|
223
|
+
let tmp = `_t${ctx.tmpCounter++}`;
|
|
271
224
|
ctx.replacements.push({
|
|
272
225
|
end: parent.end,
|
|
273
|
-
newText: `((${tmp}) => (
|
|
226
|
+
newText: `((${tmp}) => (${ctx.ns}.write(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
|
|
274
227
|
start: parent.pos
|
|
275
228
|
});
|
|
276
229
|
}
|
|
@@ -278,10 +231,9 @@ function visit(ctx, node) {
|
|
|
278
231
|
}
|
|
279
232
|
}
|
|
280
233
|
else {
|
|
281
|
-
ctx.neededImports.add('read');
|
|
282
234
|
ctx.replacements.push({
|
|
283
235
|
end: node.end,
|
|
284
|
-
newText:
|
|
236
|
+
newText: `${ctx.ns}.read(${name})`,
|
|
285
237
|
start: node.pos
|
|
286
238
|
});
|
|
287
239
|
}
|
|
@@ -291,45 +243,36 @@ function visit(ctx, node) {
|
|
|
291
243
|
ts.forEachChild(node, n => visit(ctx, n));
|
|
292
244
|
}
|
|
293
245
|
function visitArg(ctx, node) {
|
|
294
|
-
if (ts.isIdentifier(node)) {
|
|
295
|
-
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node)
|
|
246
|
+
if (ts.isIdentifier(node) && node.parent) {
|
|
247
|
+
if ((ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) ||
|
|
248
|
+
(ts.isCallExpression(node.parent) && node.parent.expression === node)) {
|
|
296
249
|
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
297
250
|
return;
|
|
298
251
|
}
|
|
299
|
-
if (
|
|
300
|
-
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
304
|
-
if (binding) {
|
|
305
|
-
ctx.neededImports.add('read');
|
|
252
|
+
if (findBinding(ctx.scopedBindings, node.text, node)) {
|
|
306
253
|
ctx.innerReplacements.push({
|
|
307
254
|
end: node.end - ctx.argStart,
|
|
308
|
-
newText:
|
|
255
|
+
newText: `${ctx.ns}.read(${node.text})`,
|
|
309
256
|
start: node.getStart(ctx.sourceFile) - ctx.argStart
|
|
310
257
|
});
|
|
311
258
|
}
|
|
312
259
|
}
|
|
313
260
|
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
314
261
|
}
|
|
315
|
-
|
|
262
|
+
export default (sourceFile, bindings, ns) => {
|
|
316
263
|
let code = sourceFile.getFullText(), ctx = {
|
|
317
264
|
bindings,
|
|
318
265
|
computedArgRanges: [],
|
|
319
266
|
hasReactiveImport: false,
|
|
320
|
-
|
|
267
|
+
ns,
|
|
321
268
|
replacements: [],
|
|
322
269
|
scopedBindings: [],
|
|
323
|
-
sourceFile
|
|
270
|
+
sourceFile,
|
|
271
|
+
tmpCounter: 0
|
|
324
272
|
};
|
|
325
273
|
visit(ctx, sourceFile);
|
|
326
274
|
if (ctx.replacements.length === 0) {
|
|
327
275
|
return code;
|
|
328
276
|
}
|
|
329
|
-
|
|
330
|
-
if (ctx.neededImports.size > 0) {
|
|
331
|
-
result = addMissingImports(result, ctx.neededImports);
|
|
332
|
-
}
|
|
333
|
-
return result;
|
|
277
|
+
return c.replace(code, ctx.replacements);
|
|
334
278
|
};
|
|
335
|
-
export { transformReactivePrimitives };
|
package/build/types.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
|
|
2
|
-
import { ReactiveArray, ReactiveObject } from './reactive/index.js';
|
|
3
2
|
import { ts } from '@esportsplus/typescript';
|
|
4
3
|
type BindingType = 'array' | 'computed' | 'object' | 'signal';
|
|
5
4
|
type Bindings = Map<string, BindingType>;
|
|
@@ -25,6 +24,7 @@ interface Link {
|
|
|
25
24
|
sub: Computed<unknown>;
|
|
26
25
|
version: number;
|
|
27
26
|
}
|
|
27
|
+
type Reactive<T> = T;
|
|
28
28
|
type Signal<T> = {
|
|
29
29
|
subs: Link | null;
|
|
30
30
|
subsTail: Link | null;
|
|
@@ -36,4 +36,4 @@ interface TransformResult {
|
|
|
36
36
|
sourceFile: ts.SourceFile;
|
|
37
37
|
transformed: boolean;
|
|
38
38
|
}
|
|
39
|
-
export type { BindingType, Bindings, Computed, Link,
|
|
39
|
+
export type { BindingType, Bindings, Computed, Link, Reactive, Signal, TransformResult };
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"@esportsplus/utilities": "^0.27.2"
|
|
5
5
|
},
|
|
6
6
|
"devDependencies": {
|
|
7
|
-
"@esportsplus/typescript": "^0.17.
|
|
7
|
+
"@esportsplus/typescript": "^0.17.5",
|
|
8
8
|
"@types/node": "^25.0.3",
|
|
9
9
|
"vite": "^7.3.0"
|
|
10
10
|
},
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"types": "./build/constants.d.ts"
|
|
19
19
|
},
|
|
20
20
|
"./plugins/tsc": {
|
|
21
|
+
"types": "./build/transformer/plugins/tsc.d.ts",
|
|
21
22
|
"import": "./build/transformer/plugins/tsc.js",
|
|
22
|
-
"require": "./build/transformer/plugins/tsc.js"
|
|
23
|
-
"types": "./build/transformer/plugins/tsc.d.ts"
|
|
23
|
+
"require": "./build/transformer/plugins/tsc.js"
|
|
24
24
|
},
|
|
25
25
|
"./plugins/vite": {
|
|
26
|
-
"
|
|
27
|
-
"
|
|
26
|
+
"types": "./build/transformer/plugins/vite.d.ts",
|
|
27
|
+
"import": "./build/transformer/plugins/vite.js"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"main": "build/index.js",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"type": "module",
|
|
38
38
|
"types": "build/index.d.ts",
|
|
39
|
-
"version": "0.25.
|
|
39
|
+
"version": "0.25.4",
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc",
|
|
42
42
|
"build:test": "pnpm build && vite build --config test/vite.config.ts",
|
package/readme.md
CHANGED
|
@@ -107,7 +107,7 @@ The library requires a build-time transformer to convert `reactive()` calls into
|
|
|
107
107
|
```typescript
|
|
108
108
|
// vite.config.ts
|
|
109
109
|
import { defineConfig } from 'vite';
|
|
110
|
-
import
|
|
110
|
+
import reactivity from '@esportsplus/reactivity/plugins/vite';
|
|
111
111
|
|
|
112
112
|
export default defineConfig({
|
|
113
113
|
plugins: [
|
|
@@ -146,12 +146,12 @@ console.log(doubled);
|
|
|
146
146
|
|
|
147
147
|
**Output:**
|
|
148
148
|
```typescript
|
|
149
|
-
import { computed, read,
|
|
149
|
+
import { computed, read, signal, write } from '@esportsplus/reactivity';
|
|
150
150
|
|
|
151
151
|
let count = signal(0);
|
|
152
152
|
let doubled = computed(() => read(count) * 2);
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
write(count, 5);
|
|
155
155
|
console.log(read(doubled));
|
|
156
156
|
```
|
|
157
157
|
|
|
@@ -172,7 +172,7 @@ class ReactiveObject_1 {
|
|
|
172
172
|
#greeting = null;
|
|
173
173
|
|
|
174
174
|
get name() { return read(this.#name); }
|
|
175
|
-
set name(v) {
|
|
175
|
+
set name(v) { write(this.#name, v); }
|
|
176
176
|
get greeting() { return read(this.#greeting ??= computed(() => `Hello, ${this.name}`)); }
|
|
177
177
|
|
|
178
178
|
dispose() {
|
|
@@ -206,7 +206,7 @@ These are typically only used by the transformer output:
|
|
|
206
206
|
| `signal(value)` | Creates a raw signal |
|
|
207
207
|
| `computed(fn)` | Creates a raw computed |
|
|
208
208
|
| `read(node)` | Reads a signal or computed value |
|
|
209
|
-
| `
|
|
209
|
+
| `write(signal, value)` | Sets a signal value |
|
|
210
210
|
| `dispose(computed)` | Disposes a computed and its dependencies |
|
|
211
211
|
|
|
212
212
|
### Type Guards
|
package/src/constants.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
const COMPILATION_ENTRYPOINT = 'reactive';
|
|
2
|
+
|
|
3
|
+
const COMPILATION_ENTRYPOINT_REGEX = /\breactive\b/;
|
|
4
|
+
|
|
5
|
+
const COMPILATION_NAMESPACE = 'reactive';
|
|
6
|
+
|
|
7
|
+
const COMPILATION_TYPE_ARRAY = 'array';
|
|
8
|
+
|
|
9
|
+
const COMPILATION_TYPE_COMPUTED = 'computed';
|
|
10
|
+
|
|
11
|
+
const COMPILATION_TYPE_SIGNAL = 'signal';
|
|
12
|
+
|
|
13
|
+
|
|
1
14
|
const COMPUTED = Symbol('reactivity.computed');
|
|
2
15
|
|
|
16
|
+
const PACKAGE = '@esportsplus/reactivity';
|
|
17
|
+
|
|
3
18
|
const REACTIVE_ARRAY = Symbol('reactivity.reactive.array');
|
|
4
19
|
|
|
5
20
|
const REACTIVE_OBJECT = Symbol('reactivity.reactive.object');
|
|
@@ -30,18 +45,10 @@ const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
|
|
|
30
45
|
|
|
31
46
|
|
|
32
47
|
export {
|
|
33
|
-
COMPUTED,
|
|
34
|
-
|
|
35
|
-
REACTIVE_OBJECT,
|
|
48
|
+
COMPILATION_TYPE_ARRAY, COMPILATION_TYPE_COMPUTED, COMPILATION_ENTRYPOINT, COMPILATION_ENTRYPOINT_REGEX, COMPILATION_NAMESPACE, COMPILATION_TYPE_SIGNAL, COMPUTED,
|
|
49
|
+
PACKAGE,
|
|
50
|
+
REACTIVE_ARRAY, REACTIVE_OBJECT,
|
|
36
51
|
SIGNAL,
|
|
37
|
-
STABILIZER_IDLE,
|
|
38
|
-
STATE_NOTIFY_MASK,
|
|
39
|
-
STABILIZER_RESCHEDULE,
|
|
40
|
-
STABILIZER_RUNNING,
|
|
41
|
-
STABILIZER_SCHEDULED,
|
|
42
|
-
STATE_CHECK,
|
|
43
|
-
STATE_DIRTY,
|
|
44
|
-
STATE_IN_HEAP,
|
|
45
|
-
STATE_NONE,
|
|
46
|
-
STATE_RECOMPUTING
|
|
52
|
+
STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
|
|
53
|
+
STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
|
|
47
54
|
};
|