@esportsplus/reactivity 0.23.0 → 0.23.1

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.
Files changed (39) hide show
  1. package/build/transformer/detector.js +38 -0
  2. package/build/transformer/{core/index.d.ts → index.d.ts} +1 -1
  3. package/build/transformer/plugins/esbuild.js +3 -2
  4. package/build/transformer/plugins/tsc.js +1 -1
  5. package/build/transformer/plugins/vite.js +2 -2
  6. package/build/transformer/transforms/auto-dispose.js +119 -0
  7. package/build/transformer/{core/transforms → transforms}/reactive-array.d.ts +1 -1
  8. package/build/transformer/transforms/reactive-array.js +93 -0
  9. package/build/transformer/{core/transforms → transforms}/reactive-object.d.ts +1 -1
  10. package/build/transformer/transforms/reactive-object.js +164 -0
  11. package/build/transformer/{core/transforms → transforms}/reactive-primitives.d.ts +1 -1
  12. package/build/transformer/transforms/reactive-primitives.js +335 -0
  13. package/build/transformer/{core/transforms → transforms}/utilities.d.ts +1 -2
  14. package/build/transformer/transforms/utilities.js +73 -0
  15. package/package.json +8 -12
  16. package/src/transformer/detector.ts +65 -0
  17. package/src/transformer/{core/index.ts → index.ts} +1 -5
  18. package/src/transformer/plugins/esbuild.ts +3 -2
  19. package/src/transformer/plugins/tsc.ts +1 -1
  20. package/src/transformer/plugins/vite.ts +2 -4
  21. package/src/transformer/transforms/auto-dispose.ts +191 -0
  22. package/src/transformer/transforms/reactive-array.ts +143 -0
  23. package/src/transformer/{core/transforms → transforms}/reactive-object.ts +101 -92
  24. package/src/transformer/transforms/reactive-primitives.ts +461 -0
  25. package/src/transformer/transforms/utilities.ts +119 -0
  26. package/build/transformer/core/detector.js +0 -6
  27. package/build/transformer/core/transforms/auto-dispose.js +0 -116
  28. package/build/transformer/core/transforms/reactive-array.js +0 -89
  29. package/build/transformer/core/transforms/reactive-object.js +0 -155
  30. package/build/transformer/core/transforms/reactive-primitives.js +0 -325
  31. package/build/transformer/core/transforms/utilities.js +0 -57
  32. package/src/transformer/core/detector.ts +0 -12
  33. package/src/transformer/core/transforms/auto-dispose.ts +0 -194
  34. package/src/transformer/core/transforms/reactive-array.ts +0 -140
  35. package/src/transformer/core/transforms/reactive-primitives.ts +0 -459
  36. package/src/transformer/core/transforms/utilities.ts +0 -95
  37. /package/build/transformer/{core/detector.d.ts → detector.d.ts} +0 -0
  38. /package/build/transformer/{core/index.js → index.js} +0 -0
  39. /package/build/transformer/{core/transforms → transforms}/auto-dispose.d.ts +0 -0
@@ -1,194 +0,0 @@
1
- import { uid } from '@esportsplus/typescript/transformer';
2
- import ts from 'typescript';
3
- import { applyReplacements, Replacement } from './utilities';
4
-
5
-
6
- const TRAILING_SEMICOLON = /;$/;
7
-
8
-
9
- interface Disposable {
10
- name: string;
11
- type: 'effect' | 'reactive';
12
- }
13
-
14
- interface FunctionEdit {
15
- cleanupBodyEnd: number;
16
- cleanupBodyStart: number;
17
- disposeCode: string;
18
- effectsToCapture: { end: number; name: string; start: number }[];
19
- }
20
-
21
- interface VisitResult {
22
- disposables: Disposable[];
23
- effectsToCapture: { end: number; name: string; start: number }[];
24
- returnStatement: ts.ReturnStatement | null;
25
- }
26
-
27
-
28
- function visitFunctionBody(
29
- body: ts.Block,
30
- parentBody: ts.Node
31
- ): VisitResult {
32
- let disposables: Disposable[] = [],
33
- effectsToCapture: { end: number; name: string; start: number }[] = [],
34
- returnStatement: ts.ReturnStatement | null = null;
35
-
36
- function visit(n: ts.Node): void {
37
- if (
38
- ts.isVariableDeclaration(n) &&
39
- ts.isIdentifier(n.name) &&
40
- n.initializer &&
41
- ts.isCallExpression(n.initializer) &&
42
- ts.isIdentifier(n.initializer.expression) &&
43
- n.initializer.expression.text === 'reactive'
44
- ) {
45
- disposables.push({ name: n.name.text, type: 'reactive' });
46
- }
47
-
48
- if (
49
- ts.isCallExpression(n) &&
50
- ts.isIdentifier(n.expression) &&
51
- n.expression.text === 'effect'
52
- ) {
53
- if (ts.isVariableDeclaration(n.parent) && ts.isIdentifier(n.parent.name)) {
54
- disposables.push({ name: n.parent.name.text, type: 'effect' });
55
- }
56
- else if (ts.isExpressionStatement(n.parent)) {
57
- let name = uid('effect');
58
-
59
- effectsToCapture.push({
60
- end: n.parent.end,
61
- name,
62
- start: n.parent.pos
63
- });
64
-
65
- disposables.push({ name, type: 'effect' });
66
- }
67
- }
68
-
69
- if (
70
- ts.isReturnStatement(n) &&
71
- n.expression &&
72
- (ts.isArrowFunction(n.expression) || ts.isFunctionExpression(n.expression)) &&
73
- n.parent === parentBody
74
- ) {
75
- returnStatement = n;
76
- }
77
-
78
- ts.forEachChild(n, visit);
79
- }
80
-
81
- visit(body);
82
-
83
- return { disposables, effectsToCapture, returnStatement };
84
- }
85
-
86
- function processFunction(
87
- node: ts.ArrowFunction | ts.FunctionDeclaration | ts.FunctionExpression,
88
- sourceFile: ts.SourceFile,
89
- edits: FunctionEdit[]
90
- ): void {
91
- if (!node.body || !ts.isBlock(node.body)) {
92
- return;
93
- }
94
-
95
- let result = visitFunctionBody(node.body, node.body),
96
- disposables = result.disposables,
97
- effectsToCapture = result.effectsToCapture,
98
- returnStatement = result.returnStatement;
99
-
100
- if (disposables.length === 0 || !returnStatement || !returnStatement.expression) {
101
- return;
102
- }
103
-
104
- let cleanupFn = returnStatement.expression as ts.ArrowFunction | ts.FunctionExpression;
105
-
106
- if (!cleanupFn.body) {
107
- return;
108
- }
109
-
110
- let disposeStatements: string[] = [];
111
-
112
- for (let i = disposables.length - 1; i >= 0; i--) {
113
- let d = disposables[i];
114
-
115
- if (d.type === 'reactive') {
116
- disposeStatements.push(`${d.name}.dispose();`);
117
- }
118
- else {
119
- disposeStatements.push(`${d.name}();`);
120
- }
121
- }
122
-
123
- let disposeCode = disposeStatements.join('\n');
124
-
125
- if (ts.isBlock(cleanupFn.body)) {
126
- edits.push({
127
- cleanupBodyEnd: cleanupFn.body.statements[0]?.pos ?? cleanupFn.body.end - 1,
128
- cleanupBodyStart: cleanupFn.body.pos + 1,
129
- disposeCode,
130
- effectsToCapture
131
- });
132
- }
133
- else {
134
- edits.push({
135
- cleanupBodyEnd: cleanupFn.body.end,
136
- cleanupBodyStart: cleanupFn.body.pos,
137
- disposeCode: `{ ${disposeCode}\n return ${cleanupFn.body.getText(sourceFile)}; }`,
138
- effectsToCapture
139
- });
140
- }
141
- }
142
-
143
-
144
- const injectAutoDispose = (sourceFile: ts.SourceFile): string => {
145
- let code = sourceFile.getFullText(),
146
- edits: FunctionEdit[] = [];
147
-
148
- function visit(node: ts.Node): void {
149
- if (
150
- ts.isFunctionDeclaration(node) ||
151
- ts.isFunctionExpression(node) ||
152
- ts.isArrowFunction(node)
153
- ) {
154
- processFunction(node, sourceFile, edits);
155
- }
156
-
157
- ts.forEachChild(node, visit);
158
- }
159
-
160
- visit(sourceFile);
161
-
162
- if (edits.length === 0) {
163
- return code;
164
- }
165
-
166
- let replacements: Replacement[] = [];
167
-
168
- for (let i = 0, n = edits.length; i < n; i++) {
169
- let edit = edits[i],
170
- effects = edit.effectsToCapture;
171
-
172
- for (let j = 0, m = effects.length; j < m; j++) {
173
- let effect = effects[j],
174
- original = code.substring(effect.start, effect.end).trim();
175
-
176
- replacements.push({
177
- end: effect.end,
178
- newText: `const ${effect.name} = ${original.replace(TRAILING_SEMICOLON, '')}`,
179
- start: effect.start
180
- });
181
- }
182
-
183
- replacements.push({
184
- end: edit.cleanupBodyEnd,
185
- newText: `\n${edit.disposeCode}`,
186
- start: edit.cleanupBodyStart
187
- });
188
- }
189
-
190
- return applyReplacements(code, replacements);
191
- };
192
-
193
-
194
- export { injectAutoDispose };
@@ -1,140 +0,0 @@
1
- import type { Bindings } from '~/types';
2
- import ts from 'typescript';
3
- import { applyReplacements, Replacement } from './utilities';
4
-
5
-
6
- function getPropertyPath(node: ts.PropertyAccessExpression): string | null {
7
- let current: ts.Node = node,
8
- parts: string[] = [];
9
-
10
- while (ts.isPropertyAccessExpression(current)) {
11
- parts.unshift(current.name.text);
12
- current = current.expression;
13
- }
14
-
15
- if (ts.isIdentifier(current)) {
16
- parts.unshift(current.text);
17
- return parts.join('.');
18
- }
19
-
20
- return null;
21
- }
22
-
23
- function getExpressionName(node: ts.Expression): string | null {
24
- if (ts.isIdentifier(node)) {
25
- return node.text;
26
- }
27
-
28
- if (ts.isPropertyAccessExpression(node)) {
29
- return getPropertyPath(node);
30
- }
31
-
32
- return null;
33
- }
34
-
35
- function isAssignmentTarget(node: ts.Node): boolean {
36
- let parent = node.parent;
37
-
38
- if (
39
- (ts.isBinaryExpression(parent) && parent.left === node) ||
40
- ts.isPostfixUnaryExpression(parent) ||
41
- ts.isPrefixUnaryExpression(parent)
42
- ) {
43
- return true;
44
- }
45
-
46
- return false;
47
- }
48
-
49
-
50
- const transformReactiveArrays = (
51
- sourceFile: ts.SourceFile,
52
- bindings: Bindings
53
- ): string => {
54
- let code = sourceFile.getFullText(),
55
- replacements: Replacement[] = [];
56
-
57
- // Single-pass visitor: collect bindings and find replacements together
58
- function visit(node: ts.Node): void {
59
- // Collect array bindings from variable declarations
60
- if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
61
- if (ts.isIdentifier(node.initializer) && bindings.get(node.initializer.text) === 'array') {
62
- bindings.set(node.name.text, 'array');
63
- }
64
-
65
- if (ts.isPropertyAccessExpression(node.initializer)) {
66
- let path = getPropertyPath(node.initializer);
67
-
68
- if (path && bindings.get(path) === 'array') {
69
- bindings.set(node.name.text, 'array');
70
- }
71
- }
72
- }
73
-
74
- // Collect array bindings from function parameters
75
- if ((ts.isFunctionDeclaration(node) || ts.isArrowFunction(node)) && node.parameters) {
76
- for (let i = 0, n = node.parameters.length; i < n; i++) {
77
- let param = node.parameters[i];
78
-
79
- if (
80
- (ts.isIdentifier(param.name) && param.type) &&
81
- ts.isTypeReferenceNode(param.type) &&
82
- ts.isIdentifier(param.type.typeName) &&
83
- param.type.typeName.text === 'ReactiveArray'
84
- ) {
85
- bindings.set(param.name.text, 'array');
86
- }
87
- }
88
- }
89
-
90
- // Find .length access replacements
91
- if (
92
- ts.isPropertyAccessExpression(node) &&
93
- node.name.text === 'length' &&
94
- !isAssignmentTarget(node)
95
- ) {
96
- let objName = getExpressionName(node.expression);
97
-
98
- if (objName && bindings.get(objName) === 'array') {
99
- let objText = node.expression.getText(sourceFile);
100
-
101
- replacements.push({
102
- end: node.end,
103
- newText: `${objText}.$length()`,
104
- start: node.pos
105
- });
106
- }
107
- }
108
-
109
- // Find array[i] = value replacements
110
- if (
111
- ts.isBinaryExpression(node) &&
112
- node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
113
- ts.isElementAccessExpression(node.left)
114
- ) {
115
- let elemAccess = node.left,
116
- objName = getExpressionName(elemAccess.expression);
117
-
118
- if (objName && bindings.get(objName) === 'array') {
119
- let indexText = elemAccess.argumentExpression.getText(sourceFile),
120
- objText = elemAccess.expression.getText(sourceFile),
121
- valueText = node.right.getText(sourceFile);
122
-
123
- replacements.push({
124
- end: node.end,
125
- newText: `${objText}.$set(${indexText}, ${valueText})`,
126
- start: node.pos
127
- });
128
- }
129
- }
130
-
131
- ts.forEachChild(node, visit);
132
- }
133
-
134
- visit(sourceFile);
135
-
136
- return applyReplacements(code, replacements);
137
- };
138
-
139
-
140
- export { transformReactiveArrays };