@esportsplus/reactivity 0.25.3 → 0.25.5
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 +9 -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 +20 -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 +22 -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 +22 -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,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
|
+
let properties = [], props = arg.properties, varName = null;
|
|
86
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,7 +124,7 @@ 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) {
|
|
@@ -184,35 +143,30 @@ function visit(ctx, node) {
|
|
|
184
143
|
ctx.scopedBindings.push({ name: varName, scope, type: classification });
|
|
185
144
|
ctx.bindings.set(varName, classification);
|
|
186
145
|
}
|
|
187
|
-
if (classification ===
|
|
188
|
-
ctx.
|
|
189
|
-
|
|
190
|
-
start: arg.getStart(ctx.sourceFile)
|
|
191
|
-
});
|
|
146
|
+
if (classification === COMPILATION_TYPE_COMPUTED) {
|
|
147
|
+
let argStart = arg.getStart(ctx.sourceFile);
|
|
148
|
+
ctx.computedArgRanges.push({ end: arg.end, start: argStart });
|
|
192
149
|
let argCtx = {
|
|
193
|
-
argStart
|
|
150
|
+
argStart,
|
|
194
151
|
innerReplacements: [],
|
|
195
|
-
|
|
152
|
+
ns: ctx.ns,
|
|
196
153
|
scopedBindings: ctx.scopedBindings,
|
|
197
154
|
sourceFile: ctx.sourceFile
|
|
198
155
|
};
|
|
199
156
|
visitArg(argCtx, arg);
|
|
200
|
-
let argText =
|
|
157
|
+
let argText = c.replace(arg.getText(ctx.sourceFile), argCtx.innerReplacements);
|
|
201
158
|
ctx.replacements.push({
|
|
202
159
|
end: node.end,
|
|
203
|
-
newText:
|
|
160
|
+
newText: `${ctx.ns}.computed(${argText})`,
|
|
204
161
|
start: node.pos
|
|
205
162
|
});
|
|
206
|
-
ctx.neededImports.add('computed');
|
|
207
163
|
}
|
|
208
164
|
else {
|
|
209
|
-
let argText = arg.getText(ctx.sourceFile);
|
|
210
165
|
ctx.replacements.push({
|
|
211
166
|
end: node.end,
|
|
212
|
-
newText:
|
|
167
|
+
newText: `${ctx.ns}.signal(${arg.getText(ctx.sourceFile)})`,
|
|
213
168
|
start: node.pos
|
|
214
169
|
});
|
|
215
|
-
ctx.neededImports.add('signal');
|
|
216
170
|
}
|
|
217
171
|
}
|
|
218
172
|
}
|
|
@@ -232,46 +186,44 @@ function visit(ctx, node) {
|
|
|
232
186
|
!(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
|
|
233
187
|
let writeCtx = isWriteContext(node);
|
|
234
188
|
if (writeCtx) {
|
|
235
|
-
if (binding.type !==
|
|
236
|
-
ctx.neededImports.add('set');
|
|
189
|
+
if (binding.type !== COMPILATION_TYPE_COMPUTED) {
|
|
237
190
|
let parent = node.parent;
|
|
238
191
|
if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
|
|
239
|
-
let valueText = parent.right.getText(ctx.sourceFile);
|
|
240
192
|
ctx.replacements.push({
|
|
241
193
|
end: parent.end,
|
|
242
|
-
newText:
|
|
194
|
+
newText: `${ctx.ns}.write(${name}, ${parent.right.getText(ctx.sourceFile)})`,
|
|
243
195
|
start: parent.pos
|
|
244
196
|
});
|
|
245
197
|
}
|
|
246
198
|
else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
|
|
247
|
-
let op =
|
|
199
|
+
let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind) ?? '+';
|
|
248
200
|
ctx.replacements.push({
|
|
249
201
|
end: parent.end,
|
|
250
|
-
newText:
|
|
202
|
+
newText: `${ctx.ns}.write(${name}, ${name}.value ${op} ${parent.right.getText(ctx.sourceFile)})`,
|
|
251
203
|
start: parent.pos
|
|
252
204
|
});
|
|
253
205
|
}
|
|
254
206
|
else if (writeCtx === 'increment') {
|
|
255
|
-
let
|
|
207
|
+
let delta = parent.operator === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1', isPrefix = ts.isPrefixUnaryExpression(parent);
|
|
256
208
|
if (ts.isExpressionStatement(parent.parent)) {
|
|
257
209
|
ctx.replacements.push({
|
|
258
210
|
end: parent.end,
|
|
259
|
-
newText:
|
|
211
|
+
newText: `${ctx.ns}.write(${name}, ${name}.value ${delta})`,
|
|
260
212
|
start: parent.pos
|
|
261
213
|
});
|
|
262
214
|
}
|
|
263
215
|
else if (isPrefix) {
|
|
264
216
|
ctx.replacements.push({
|
|
265
217
|
end: parent.end,
|
|
266
|
-
newText: `(
|
|
218
|
+
newText: `(${ctx.ns}.write(${name}, ${name}.value ${delta}), ${name}.value)`,
|
|
267
219
|
start: parent.pos
|
|
268
220
|
});
|
|
269
221
|
}
|
|
270
222
|
else {
|
|
271
|
-
let tmp =
|
|
223
|
+
let tmp = `_t${ctx.tmpCounter++}`;
|
|
272
224
|
ctx.replacements.push({
|
|
273
225
|
end: parent.end,
|
|
274
|
-
newText: `((${tmp}) => (
|
|
226
|
+
newText: `((${tmp}) => (${ctx.ns}.write(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
|
|
275
227
|
start: parent.pos
|
|
276
228
|
});
|
|
277
229
|
}
|
|
@@ -279,10 +231,9 @@ function visit(ctx, node) {
|
|
|
279
231
|
}
|
|
280
232
|
}
|
|
281
233
|
else {
|
|
282
|
-
ctx.neededImports.add('read');
|
|
283
234
|
ctx.replacements.push({
|
|
284
235
|
end: node.end,
|
|
285
|
-
newText:
|
|
236
|
+
newText: `${ctx.ns}.read(${name})`,
|
|
286
237
|
start: node.pos
|
|
287
238
|
});
|
|
288
239
|
}
|
|
@@ -293,44 +244,35 @@ function visit(ctx, node) {
|
|
|
293
244
|
}
|
|
294
245
|
function visitArg(ctx, node) {
|
|
295
246
|
if (ts.isIdentifier(node) && node.parent) {
|
|
296
|
-
if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node)
|
|
247
|
+
if ((ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) ||
|
|
248
|
+
(ts.isCallExpression(node.parent) && node.parent.expression === node)) {
|
|
297
249
|
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
298
250
|
return;
|
|
299
251
|
}
|
|
300
|
-
if (
|
|
301
|
-
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
let binding = findBinding(ctx.scopedBindings, node.text, node);
|
|
305
|
-
if (binding) {
|
|
306
|
-
ctx.neededImports.add('read');
|
|
252
|
+
if (findBinding(ctx.scopedBindings, node.text, node)) {
|
|
307
253
|
ctx.innerReplacements.push({
|
|
308
254
|
end: node.end - ctx.argStart,
|
|
309
|
-
newText:
|
|
255
|
+
newText: `${ctx.ns}.read(${node.text})`,
|
|
310
256
|
start: node.getStart(ctx.sourceFile) - ctx.argStart
|
|
311
257
|
});
|
|
312
258
|
}
|
|
313
259
|
}
|
|
314
260
|
ts.forEachChild(node, n => visitArg(ctx, n));
|
|
315
261
|
}
|
|
316
|
-
|
|
262
|
+
export default (sourceFile, bindings, ns) => {
|
|
317
263
|
let code = sourceFile.getFullText(), ctx = {
|
|
318
264
|
bindings,
|
|
319
265
|
computedArgRanges: [],
|
|
320
266
|
hasReactiveImport: false,
|
|
321
|
-
|
|
267
|
+
ns,
|
|
322
268
|
replacements: [],
|
|
323
269
|
scopedBindings: [],
|
|
324
|
-
sourceFile
|
|
270
|
+
sourceFile,
|
|
271
|
+
tmpCounter: 0
|
|
325
272
|
};
|
|
326
273
|
visit(ctx, sourceFile);
|
|
327
274
|
if (ctx.replacements.length === 0) {
|
|
328
275
|
return code;
|
|
329
276
|
}
|
|
330
|
-
|
|
331
|
-
if (ctx.neededImports.size > 0) {
|
|
332
|
-
result = addMissingImports(result, ctx.neededImports);
|
|
333
|
-
}
|
|
334
|
-
return result;
|
|
277
|
+
return c.replace(code, ctx.replacements);
|
|
335
278
|
};
|
|
336
|
-
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.5",
|
|
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,22 @@
|
|
|
1
|
+
import uid from 'node_modules/@esportsplus/typescript/build/transformer/uid';
|
|
2
|
+
|
|
3
|
+
const COMPILATION_ENTRYPOINT = 'reactive';
|
|
4
|
+
|
|
5
|
+
const COMPILATION_ENTRYPOINT_REGEX = /\breactive\b/;
|
|
6
|
+
|
|
7
|
+
const COMPILATION_NAMESPACE = uid(COMPILATION_ENTRYPOINT);
|
|
8
|
+
|
|
9
|
+
const COMPILATION_TYPE_ARRAY = 'array';
|
|
10
|
+
|
|
11
|
+
const COMPILATION_TYPE_COMPUTED = 'computed';
|
|
12
|
+
|
|
13
|
+
const COMPILATION_TYPE_SIGNAL = 'signal';
|
|
14
|
+
|
|
15
|
+
|
|
1
16
|
const COMPUTED = Symbol('reactivity.computed');
|
|
2
17
|
|
|
18
|
+
const PACKAGE = '@esportsplus/reactivity';
|
|
19
|
+
|
|
3
20
|
const REACTIVE_ARRAY = Symbol('reactivity.reactive.array');
|
|
4
21
|
|
|
5
22
|
const REACTIVE_OBJECT = Symbol('reactivity.reactive.object');
|
|
@@ -30,18 +47,10 @@ const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
|
|
|
30
47
|
|
|
31
48
|
|
|
32
49
|
export {
|
|
33
|
-
COMPUTED,
|
|
34
|
-
|
|
35
|
-
REACTIVE_OBJECT,
|
|
50
|
+
COMPILATION_TYPE_ARRAY, COMPILATION_TYPE_COMPUTED, COMPILATION_ENTRYPOINT, COMPILATION_ENTRYPOINT_REGEX, COMPILATION_NAMESPACE, COMPILATION_TYPE_SIGNAL, COMPUTED,
|
|
51
|
+
PACKAGE,
|
|
52
|
+
REACTIVE_ARRAY, REACTIVE_OBJECT,
|
|
36
53
|
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
|
|
54
|
+
STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
|
|
55
|
+
STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
|
|
47
56
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { default as reactive } from './reactive';
|
|
1
|
+
export { REACTIVE_OBJECT } from './constants';
|
|
2
|
+
export { default as reactive, ReactiveArray } from './reactive';
|
|
3
3
|
export * from './system';
|
|
4
4
|
export * from './types';
|