@esportsplus/reactivity 0.23.1 → 0.23.3
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/transformer/index.d.ts +6 -7
- package/build/transformer/index.js +9 -18
- package/build/transformer/plugins/esbuild.d.ts +1 -3
- package/build/transformer/plugins/esbuild.js +2 -2
- package/build/transformer/plugins/vite.d.ts +1 -3
- package/build/transformer/plugins/vite.js +2 -2
- package/build/transformer/transforms/{reactive-object.js → object.js} +1 -2
- package/build/types.d.ts +1 -4
- package/package.json +2 -2
- package/readme.md +0 -24
- package/src/transformer/index.ts +10 -21
- package/src/transformer/plugins/esbuild.ts +2 -4
- package/src/transformer/plugins/vite.ts +2 -4
- package/src/transformer/transforms/{reactive-object.ts → object.ts} +1 -3
- package/src/types.ts +0 -5
- package/build/transformer/transforms/auto-dispose.d.ts +0 -3
- package/build/transformer/transforms/auto-dispose.js +0 -119
- package/src/transformer/transforms/auto-dispose.ts +0 -191
- /package/build/transformer/transforms/{reactive-array.d.ts → array.d.ts} +0 -0
- /package/build/transformer/transforms/{reactive-array.js → array.js} +0 -0
- /package/build/transformer/transforms/{reactive-object.d.ts → object.d.ts} +0 -0
- /package/build/transformer/transforms/{reactive-primitives.d.ts → primitives.d.ts} +0 -0
- /package/build/transformer/transforms/{reactive-primitives.js → primitives.js} +0 -0
- /package/src/transformer/transforms/{reactive-array.ts → array.ts} +0 -0
- /package/src/transformer/transforms/{reactive-primitives.ts → primitives.ts} +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TransformResult } from '../types.js';
|
|
2
2
|
import { mightNeedTransform } from './detector.js';
|
|
3
3
|
import ts from 'typescript';
|
|
4
|
-
declare const createTransformer: (
|
|
5
|
-
declare const transform: (sourceFile: ts.SourceFile
|
|
4
|
+
declare const createTransformer: () => ts.TransformerFactory<ts.SourceFile>;
|
|
5
|
+
declare const transform: (sourceFile: ts.SourceFile) => TransformResult;
|
|
6
6
|
export { createTransformer, mightNeedTransform, transform };
|
|
7
|
-
export {
|
|
8
|
-
export {
|
|
9
|
-
export {
|
|
10
|
-
export { transformReactivePrimitives } from './transforms/reactive-primitives.js';
|
|
7
|
+
export { transformReactiveArrays } from './transforms/array.js';
|
|
8
|
+
export { transformReactiveObjects } from './transforms/object.js';
|
|
9
|
+
export { transformReactivePrimitives } from './transforms/primitives.js';
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { injectAutoDispose } from './transforms/auto-dispose.js';
|
|
2
1
|
import { mightNeedTransform } from './detector.js';
|
|
3
|
-
import { transformReactiveArrays } from './transforms/
|
|
4
|
-
import { transformReactiveObjects } from './transforms/
|
|
5
|
-
import { transformReactivePrimitives } from './transforms/
|
|
2
|
+
import { transformReactiveArrays } from './transforms/array.js';
|
|
3
|
+
import { transformReactiveObjects } from './transforms/object.js';
|
|
4
|
+
import { transformReactivePrimitives } from './transforms/primitives.js';
|
|
6
5
|
import ts from 'typescript';
|
|
7
|
-
const createTransformer = (
|
|
6
|
+
const createTransformer = () => {
|
|
8
7
|
return () => {
|
|
9
8
|
return (sourceFile) => {
|
|
10
|
-
let result = transform(sourceFile
|
|
9
|
+
let result = transform(sourceFile);
|
|
11
10
|
return result.transformed ? result.sourceFile : sourceFile;
|
|
12
11
|
};
|
|
13
12
|
};
|
|
14
13
|
};
|
|
15
|
-
const transform = (sourceFile
|
|
14
|
+
const transform = (sourceFile) => {
|
|
16
15
|
let bindings = new Map(), code = sourceFile.getFullText(), current = sourceFile, original = code, result;
|
|
17
16
|
if (!mightNeedTransform(code)) {
|
|
18
17
|
return { code, sourceFile, transformed: false };
|
|
@@ -32,13 +31,6 @@ const transform = (sourceFile, options) => {
|
|
|
32
31
|
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
33
32
|
code = result;
|
|
34
33
|
}
|
|
35
|
-
if (options?.autoDispose) {
|
|
36
|
-
result = injectAutoDispose(current);
|
|
37
|
-
if (result !== code) {
|
|
38
|
-
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
39
|
-
code = result;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
34
|
if (code === original) {
|
|
43
35
|
return { code, sourceFile, transformed: false };
|
|
44
36
|
}
|
|
@@ -49,7 +41,6 @@ const transform = (sourceFile, options) => {
|
|
|
49
41
|
};
|
|
50
42
|
};
|
|
51
43
|
export { createTransformer, mightNeedTransform, transform };
|
|
52
|
-
export {
|
|
53
|
-
export {
|
|
54
|
-
export {
|
|
55
|
-
export { transformReactivePrimitives } from './transforms/reactive-primitives.js';
|
|
44
|
+
export { transformReactiveArrays } from './transforms/array.js';
|
|
45
|
+
export { transformReactiveObjects } from './transforms/object.js';
|
|
46
|
+
export { transformReactivePrimitives } from './transforms/primitives.js';
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import type { Plugin } from 'esbuild';
|
|
2
|
-
|
|
3
|
-
declare const _default: (options?: TransformOptions) => Plugin;
|
|
2
|
+
declare const _default: () => Plugin;
|
|
4
3
|
export default _default;
|
|
5
|
-
export type { TransformOptions as PluginOptions };
|
|
@@ -2,7 +2,7 @@ import { TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
|
2
2
|
import { mightNeedTransform, transform } from '../../transformer/index.js';
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import ts from 'typescript';
|
|
5
|
-
export default (
|
|
5
|
+
export default () => {
|
|
6
6
|
return {
|
|
7
7
|
name: '@esportsplus/reactivity/plugin-esbuild',
|
|
8
8
|
setup(build) {
|
|
@@ -12,7 +12,7 @@ export default (options) => {
|
|
|
12
12
|
return null;
|
|
13
13
|
}
|
|
14
14
|
try {
|
|
15
|
-
let sourceFile = ts.createSourceFile(args.path, code, ts.ScriptTarget.Latest, true), result = transform(sourceFile
|
|
15
|
+
let sourceFile = ts.createSourceFile(args.path, code, ts.ScriptTarget.Latest, true), result = transform(sourceFile);
|
|
16
16
|
if (!result.transformed) {
|
|
17
17
|
return null;
|
|
18
18
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
1
|
import type { Plugin } from 'vite';
|
|
2
|
-
|
|
3
|
-
declare const _default: (options?: TransformOptions) => Plugin;
|
|
2
|
+
declare const _default: () => Plugin;
|
|
4
3
|
export default _default;
|
|
5
|
-
export type { TransformOptions as PluginOptions };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
2
2
|
import { mightNeedTransform, transform } from '../../transformer/index.js';
|
|
3
3
|
import ts from 'typescript';
|
|
4
|
-
export default (
|
|
4
|
+
export default () => {
|
|
5
5
|
return {
|
|
6
6
|
enforce: 'pre',
|
|
7
7
|
name: '@esportsplus/reactivity/plugin-vite',
|
|
@@ -13,7 +13,7 @@ export default (options) => {
|
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
15
|
try {
|
|
16
|
-
let sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true), result = transform(sourceFile
|
|
16
|
+
let sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true), result = transform(sourceFile);
|
|
17
17
|
if (!result.transformed) {
|
|
18
18
|
return null;
|
|
19
19
|
}
|
|
@@ -48,14 +48,13 @@ function buildClassCode(className, properties) {
|
|
|
48
48
|
disposeStatements.push(`if (this.#${key}) dispose(this.#${key});`);
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
let disposeBody = disposeStatements.length > 0 ? disposeStatements.join('\n') : '';
|
|
52
51
|
return `
|
|
53
52
|
class ${className} {
|
|
54
53
|
${fields.join('\n')}
|
|
55
54
|
${accessors.join('\n')}
|
|
56
55
|
|
|
57
56
|
dispose() {
|
|
58
|
-
${
|
|
57
|
+
${disposeStatements.length > 0 ? disposeStatements.join('\n') : ''}
|
|
59
58
|
}
|
|
60
59
|
}
|
|
61
60
|
`;
|
package/build/types.d.ts
CHANGED
|
@@ -31,12 +31,9 @@ type Signal<T> = {
|
|
|
31
31
|
type: typeof SIGNAL;
|
|
32
32
|
value: T;
|
|
33
33
|
};
|
|
34
|
-
interface TransformOptions {
|
|
35
|
-
autoDispose?: boolean;
|
|
36
|
-
}
|
|
37
34
|
interface TransformResult {
|
|
38
35
|
code: string;
|
|
39
36
|
sourceFile: ts.SourceFile;
|
|
40
37
|
transformed: boolean;
|
|
41
38
|
}
|
|
42
|
-
export type { BindingType, Bindings, Computed, Link, ReactiveArray, ReactiveObject, Signal,
|
|
39
|
+
export type { BindingType, Bindings, Computed, Link, ReactiveArray, ReactiveObject, 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.
|
|
7
|
+
"@esportsplus/typescript": "^0.13.0",
|
|
8
8
|
"@types/node": "^25.0.3",
|
|
9
9
|
"esbuild": "^0.27.2",
|
|
10
10
|
"typescript": "^5.9.3",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"type": "module",
|
|
43
43
|
"types": "build/index.d.ts",
|
|
44
|
-
"version": "0.23.
|
|
44
|
+
"version": "0.23.3",
|
|
45
45
|
"scripts": {
|
|
46
46
|
"build": "tsc && tsc-alias",
|
|
47
47
|
"build:test": "pnpm build && vite build --config test/vite.config.ts",
|
package/readme.md
CHANGED
|
@@ -147,30 +147,6 @@ For direct TypeScript compilation using `ttsc` or `ts-patch`:
|
|
|
147
147
|
}
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
-
## Plugin Options
|
|
151
|
-
|
|
152
|
-
All plugins accept the same options:
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
interface TransformOptions {
|
|
156
|
-
// Inject automatic disposal tracking (experimental)
|
|
157
|
-
autoDispose?: boolean;
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
Example with options:
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
// vite.config.ts
|
|
165
|
-
import { plugin as reactivity } from '@esportsplus/reactivity/plugins/vite';
|
|
166
|
-
|
|
167
|
-
export default defineConfig({
|
|
168
|
-
plugins: [
|
|
169
|
-
reactivity({ autoDispose: true })
|
|
170
|
-
]
|
|
171
|
-
});
|
|
172
|
-
```
|
|
173
|
-
|
|
174
150
|
## How It Works
|
|
175
151
|
|
|
176
152
|
The transformer converts your code at compile time:
|
package/src/transformer/index.ts
CHANGED
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
import type { Bindings,
|
|
2
|
-
import { injectAutoDispose } from './transforms/auto-dispose';
|
|
1
|
+
import type { Bindings, TransformResult } from '~/types';
|
|
3
2
|
import { mightNeedTransform } from './detector';
|
|
4
|
-
import { transformReactiveArrays } from './transforms/
|
|
5
|
-
import { transformReactiveObjects } from './transforms/
|
|
6
|
-
import { transformReactivePrimitives } from './transforms/
|
|
3
|
+
import { transformReactiveArrays } from './transforms/array';
|
|
4
|
+
import { transformReactiveObjects } from './transforms/object';
|
|
5
|
+
import { transformReactivePrimitives } from './transforms/primitives';
|
|
7
6
|
import ts from 'typescript';
|
|
8
7
|
|
|
9
8
|
|
|
10
|
-
const createTransformer = (
|
|
9
|
+
const createTransformer = (): ts.TransformerFactory<ts.SourceFile> => {
|
|
11
10
|
return () => {
|
|
12
11
|
return (sourceFile: ts.SourceFile): ts.SourceFile => {
|
|
13
|
-
let result = transform(sourceFile
|
|
12
|
+
let result = transform(sourceFile);
|
|
14
13
|
|
|
15
14
|
return result.transformed ? result.sourceFile : sourceFile;
|
|
16
15
|
};
|
|
17
16
|
};
|
|
18
17
|
};
|
|
19
18
|
|
|
20
|
-
const transform = (sourceFile: ts.SourceFile
|
|
19
|
+
const transform = (sourceFile: ts.SourceFile): TransformResult => {
|
|
21
20
|
let bindings: Bindings = new Map(),
|
|
22
21
|
code = sourceFile.getFullText(),
|
|
23
22
|
current = sourceFile,
|
|
@@ -50,15 +49,6 @@ const transform = (sourceFile: ts.SourceFile, options?: TransformOptions): Trans
|
|
|
50
49
|
code = result;
|
|
51
50
|
}
|
|
52
51
|
|
|
53
|
-
if (options?.autoDispose) {
|
|
54
|
-
result = injectAutoDispose(current);
|
|
55
|
-
|
|
56
|
-
if (result !== code) {
|
|
57
|
-
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
58
|
-
code = result;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
52
|
if (code === original) {
|
|
63
53
|
return { code, sourceFile, transformed: false };
|
|
64
54
|
}
|
|
@@ -72,7 +62,6 @@ const transform = (sourceFile: ts.SourceFile, options?: TransformOptions): Trans
|
|
|
72
62
|
|
|
73
63
|
|
|
74
64
|
export { createTransformer, mightNeedTransform, transform };
|
|
75
|
-
export {
|
|
76
|
-
export {
|
|
77
|
-
export {
|
|
78
|
-
export { transformReactivePrimitives } from './transforms/reactive-primitives';
|
|
65
|
+
export { transformReactiveArrays } from './transforms/array';
|
|
66
|
+
export { transformReactiveObjects } from './transforms/object';
|
|
67
|
+
export { transformReactivePrimitives } from './transforms/primitives';
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
2
2
|
import { mightNeedTransform, transform } from '~/transformer';
|
|
3
3
|
import type { OnLoadArgs, Plugin, PluginBuild } from 'esbuild';
|
|
4
|
-
import type { TransformOptions } from '~/types';
|
|
5
4
|
import fs from 'fs';
|
|
6
5
|
import ts from 'typescript';
|
|
7
6
|
|
|
8
7
|
|
|
9
|
-
export default (
|
|
8
|
+
export default (): Plugin => {
|
|
10
9
|
return {
|
|
11
10
|
name: '@esportsplus/reactivity/plugin-esbuild',
|
|
12
11
|
|
|
@@ -25,7 +24,7 @@ export default (options?: TransformOptions): Plugin => {
|
|
|
25
24
|
ts.ScriptTarget.Latest,
|
|
26
25
|
true
|
|
27
26
|
),
|
|
28
|
-
result = transform(sourceFile
|
|
27
|
+
result = transform(sourceFile);
|
|
29
28
|
|
|
30
29
|
if (!result.transformed) {
|
|
31
30
|
return null;
|
|
@@ -44,4 +43,3 @@ export default (options?: TransformOptions): Plugin => {
|
|
|
44
43
|
}
|
|
45
44
|
};
|
|
46
45
|
};
|
|
47
|
-
export type { TransformOptions as PluginOptions };
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { TRANSFORM_PATTERN } from '@esportsplus/typescript/transformer';
|
|
2
2
|
import { mightNeedTransform, transform } from '~/transformer';
|
|
3
3
|
import type { Plugin } from 'vite';
|
|
4
|
-
import type { TransformOptions } from '~/types';
|
|
5
4
|
import ts from 'typescript';
|
|
6
5
|
|
|
7
6
|
|
|
8
|
-
export default (
|
|
7
|
+
export default (): Plugin => {
|
|
9
8
|
return {
|
|
10
9
|
enforce: 'pre',
|
|
11
10
|
name: '@esportsplus/reactivity/plugin-vite',
|
|
@@ -21,7 +20,7 @@ export default (options?: TransformOptions): Plugin => {
|
|
|
21
20
|
|
|
22
21
|
try {
|
|
23
22
|
let sourceFile = ts.createSourceFile(id, code, ts.ScriptTarget.Latest, true),
|
|
24
|
-
result = transform(sourceFile
|
|
23
|
+
result = transform(sourceFile);
|
|
25
24
|
|
|
26
25
|
if (!result.transformed) {
|
|
27
26
|
return null;
|
|
@@ -36,4 +35,3 @@ export default (options?: TransformOptions): Plugin => {
|
|
|
36
35
|
}
|
|
37
36
|
};
|
|
38
37
|
};
|
|
39
|
-
export type { TransformOptions as PluginOptions };
|
|
@@ -94,15 +94,13 @@ function buildClassCode(className: string, properties: AnalyzedProperty[]): stri
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
let disposeBody = disposeStatements.length > 0 ? disposeStatements.join('\n') : '';
|
|
98
|
-
|
|
99
97
|
return `
|
|
100
98
|
class ${className} {
|
|
101
99
|
${fields.join('\n')}
|
|
102
100
|
${accessors.join('\n')}
|
|
103
101
|
|
|
104
102
|
dispose() {
|
|
105
|
-
${
|
|
103
|
+
${disposeStatements.length > 0 ? disposeStatements.join('\n') : ''}
|
|
106
104
|
}
|
|
107
105
|
}
|
|
108
106
|
`;
|
package/src/types.ts
CHANGED
|
@@ -43,10 +43,6 @@ type Signal<T> = {
|
|
|
43
43
|
value: T;
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
interface TransformOptions {
|
|
47
|
-
autoDispose?: boolean;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
46
|
interface TransformResult {
|
|
51
47
|
code: string;
|
|
52
48
|
sourceFile: ts.SourceFile;
|
|
@@ -62,6 +58,5 @@ export type {
|
|
|
62
58
|
ReactiveArray,
|
|
63
59
|
ReactiveObject,
|
|
64
60
|
Signal,
|
|
65
|
-
TransformOptions,
|
|
66
61
|
TransformResult
|
|
67
62
|
};
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { uid, TRAILING_SEMICOLON } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import { applyReplacements } from './utilities.js';
|
|
3
|
-
import ts from 'typescript';
|
|
4
|
-
function processFunction(node, sourceFile, edits) {
|
|
5
|
-
if (!node.body || !ts.isBlock(node.body)) {
|
|
6
|
-
return;
|
|
7
|
-
}
|
|
8
|
-
let ctx = {
|
|
9
|
-
disposables: [],
|
|
10
|
-
effectsToCapture: [],
|
|
11
|
-
parentBody: node.body,
|
|
12
|
-
returnStatement: null
|
|
13
|
-
};
|
|
14
|
-
visitBody(ctx, node.body);
|
|
15
|
-
if (ctx.disposables.length === 0 || !ctx.returnStatement || !ctx.returnStatement.expression) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
let cleanupFn = ctx.returnStatement.expression;
|
|
19
|
-
if (!cleanupFn.body) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
let disposeStatements = [];
|
|
23
|
-
for (let i = ctx.disposables.length - 1; i >= 0; i--) {
|
|
24
|
-
let d = ctx.disposables[i];
|
|
25
|
-
if (d.type === 'reactive') {
|
|
26
|
-
disposeStatements.push(`${d.name}.dispose();`);
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
disposeStatements.push(`${d.name}();`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
let disposeCode = disposeStatements.join('\n');
|
|
33
|
-
if (ts.isBlock(cleanupFn.body)) {
|
|
34
|
-
edits.push({
|
|
35
|
-
cleanupBodyEnd: cleanupFn.body.statements[0]?.pos ?? cleanupFn.body.end - 1,
|
|
36
|
-
cleanupBodyStart: cleanupFn.body.pos + 1,
|
|
37
|
-
disposeCode,
|
|
38
|
-
effectsToCapture: ctx.effectsToCapture
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
else {
|
|
42
|
-
edits.push({
|
|
43
|
-
cleanupBodyEnd: cleanupFn.body.end,
|
|
44
|
-
cleanupBodyStart: cleanupFn.body.pos,
|
|
45
|
-
disposeCode: `{ ${disposeCode}\n return ${cleanupFn.body.getText(sourceFile)}; }`,
|
|
46
|
-
effectsToCapture: ctx.effectsToCapture
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
function visitBody(ctx, node) {
|
|
51
|
-
if (ts.isVariableDeclaration(node) &&
|
|
52
|
-
ts.isIdentifier(node.name) &&
|
|
53
|
-
node.initializer &&
|
|
54
|
-
ts.isCallExpression(node.initializer) &&
|
|
55
|
-
ts.isIdentifier(node.initializer.expression) &&
|
|
56
|
-
node.initializer.expression.text === 'reactive') {
|
|
57
|
-
ctx.disposables.push({ name: node.name.text, type: 'reactive' });
|
|
58
|
-
}
|
|
59
|
-
if (ts.isCallExpression(node) &&
|
|
60
|
-
ts.isIdentifier(node.expression) &&
|
|
61
|
-
node.expression.text === 'effect') {
|
|
62
|
-
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
63
|
-
ctx.disposables.push({ name: node.parent.name.text, type: 'effect' });
|
|
64
|
-
}
|
|
65
|
-
else if (ts.isExpressionStatement(node.parent)) {
|
|
66
|
-
let name = uid('effect');
|
|
67
|
-
ctx.effectsToCapture.push({
|
|
68
|
-
end: node.parent.end,
|
|
69
|
-
name,
|
|
70
|
-
start: node.parent.pos
|
|
71
|
-
});
|
|
72
|
-
ctx.disposables.push({ name, type: 'effect' });
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (ts.isReturnStatement(node) &&
|
|
76
|
-
node.expression &&
|
|
77
|
-
(ts.isArrowFunction(node.expression) || ts.isFunctionExpression(node.expression)) &&
|
|
78
|
-
node.parent === ctx.parentBody) {
|
|
79
|
-
ctx.returnStatement = node;
|
|
80
|
-
}
|
|
81
|
-
ts.forEachChild(node, n => visitBody(ctx, n));
|
|
82
|
-
}
|
|
83
|
-
function visitMain(ctx, node) {
|
|
84
|
-
if (ts.isFunctionDeclaration(node) ||
|
|
85
|
-
ts.isFunctionExpression(node) ||
|
|
86
|
-
ts.isArrowFunction(node)) {
|
|
87
|
-
processFunction(node, ctx.sourceFile, ctx.edits);
|
|
88
|
-
}
|
|
89
|
-
ts.forEachChild(node, n => visitMain(ctx, n));
|
|
90
|
-
}
|
|
91
|
-
const injectAutoDispose = (sourceFile) => {
|
|
92
|
-
let code = sourceFile.getFullText(), ctx = {
|
|
93
|
-
edits: [],
|
|
94
|
-
sourceFile
|
|
95
|
-
};
|
|
96
|
-
visitMain(ctx, sourceFile);
|
|
97
|
-
if (ctx.edits.length === 0) {
|
|
98
|
-
return code;
|
|
99
|
-
}
|
|
100
|
-
let replacements = [];
|
|
101
|
-
for (let i = 0, n = ctx.edits.length; i < n; i++) {
|
|
102
|
-
let edit = ctx.edits[i], effects = edit.effectsToCapture;
|
|
103
|
-
for (let j = 0, m = effects.length; j < m; j++) {
|
|
104
|
-
let effect = effects[j], original = code.substring(effect.start, effect.end).trim();
|
|
105
|
-
replacements.push({
|
|
106
|
-
end: effect.end,
|
|
107
|
-
newText: `const ${effect.name} = ${original.replace(TRAILING_SEMICOLON, '')}`,
|
|
108
|
-
start: effect.start
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
replacements.push({
|
|
112
|
-
end: edit.cleanupBodyEnd,
|
|
113
|
-
newText: `\n${edit.disposeCode}`,
|
|
114
|
-
start: edit.cleanupBodyStart
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
return applyReplacements(code, replacements);
|
|
118
|
-
};
|
|
119
|
-
export { injectAutoDispose };
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { uid, TRAILING_SEMICOLON } from '@esportsplus/typescript/transformer';
|
|
2
|
-
import { applyReplacements, Replacement } from './utilities';
|
|
3
|
-
import ts from 'typescript';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface BodyContext {
|
|
7
|
-
disposables: Disposable[];
|
|
8
|
-
effectsToCapture: { end: number; name: string; start: number }[];
|
|
9
|
-
parentBody: ts.Node;
|
|
10
|
-
returnStatement: ts.ReturnStatement | null;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface Disposable {
|
|
14
|
-
name: string;
|
|
15
|
-
type: 'effect' | 'reactive';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface FunctionEdit {
|
|
19
|
-
cleanupBodyEnd: number;
|
|
20
|
-
cleanupBodyStart: number;
|
|
21
|
-
disposeCode: string;
|
|
22
|
-
effectsToCapture: { end: number; name: string; start: number }[];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface MainContext {
|
|
26
|
-
edits: FunctionEdit[];
|
|
27
|
-
sourceFile: ts.SourceFile;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
function processFunction(
|
|
32
|
-
node: ts.ArrowFunction | ts.FunctionDeclaration | ts.FunctionExpression,
|
|
33
|
-
sourceFile: ts.SourceFile,
|
|
34
|
-
edits: FunctionEdit[]
|
|
35
|
-
): void {
|
|
36
|
-
if (!node.body || !ts.isBlock(node.body)) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
let ctx: BodyContext = {
|
|
41
|
-
disposables: [],
|
|
42
|
-
effectsToCapture: [],
|
|
43
|
-
parentBody: node.body,
|
|
44
|
-
returnStatement: null
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
visitBody(ctx, node.body);
|
|
48
|
-
|
|
49
|
-
if (ctx.disposables.length === 0 || !ctx.returnStatement || !ctx.returnStatement.expression) {
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
let cleanupFn = ctx.returnStatement.expression as ts.ArrowFunction | ts.FunctionExpression;
|
|
54
|
-
|
|
55
|
-
if (!cleanupFn.body) {
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let disposeStatements: string[] = [];
|
|
60
|
-
|
|
61
|
-
for (let i = ctx.disposables.length - 1; i >= 0; i--) {
|
|
62
|
-
let d = ctx.disposables[i];
|
|
63
|
-
|
|
64
|
-
if (d.type === 'reactive') {
|
|
65
|
-
disposeStatements.push(`${d.name}.dispose();`);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
disposeStatements.push(`${d.name}();`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
let disposeCode = disposeStatements.join('\n');
|
|
73
|
-
|
|
74
|
-
if (ts.isBlock(cleanupFn.body)) {
|
|
75
|
-
edits.push({
|
|
76
|
-
cleanupBodyEnd: cleanupFn.body.statements[0]?.pos ?? cleanupFn.body.end - 1,
|
|
77
|
-
cleanupBodyStart: cleanupFn.body.pos + 1,
|
|
78
|
-
disposeCode,
|
|
79
|
-
effectsToCapture: ctx.effectsToCapture
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
edits.push({
|
|
84
|
-
cleanupBodyEnd: cleanupFn.body.end,
|
|
85
|
-
cleanupBodyStart: cleanupFn.body.pos,
|
|
86
|
-
disposeCode: `{ ${disposeCode}\n return ${cleanupFn.body.getText(sourceFile)}; }`,
|
|
87
|
-
effectsToCapture: ctx.effectsToCapture
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function visitBody(ctx: BodyContext, node: ts.Node): void {
|
|
93
|
-
if (
|
|
94
|
-
ts.isVariableDeclaration(node) &&
|
|
95
|
-
ts.isIdentifier(node.name) &&
|
|
96
|
-
node.initializer &&
|
|
97
|
-
ts.isCallExpression(node.initializer) &&
|
|
98
|
-
ts.isIdentifier(node.initializer.expression) &&
|
|
99
|
-
node.initializer.expression.text === 'reactive'
|
|
100
|
-
) {
|
|
101
|
-
ctx.disposables.push({ name: node.name.text, type: 'reactive' });
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (
|
|
105
|
-
ts.isCallExpression(node) &&
|
|
106
|
-
ts.isIdentifier(node.expression) &&
|
|
107
|
-
node.expression.text === 'effect'
|
|
108
|
-
) {
|
|
109
|
-
if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
110
|
-
ctx.disposables.push({ name: node.parent.name.text, type: 'effect' });
|
|
111
|
-
}
|
|
112
|
-
else if (ts.isExpressionStatement(node.parent)) {
|
|
113
|
-
let name = uid('effect');
|
|
114
|
-
|
|
115
|
-
ctx.effectsToCapture.push({
|
|
116
|
-
end: node.parent.end,
|
|
117
|
-
name,
|
|
118
|
-
start: node.parent.pos
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
ctx.disposables.push({ name, type: 'effect' });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (
|
|
126
|
-
ts.isReturnStatement(node) &&
|
|
127
|
-
node.expression &&
|
|
128
|
-
(ts.isArrowFunction(node.expression) || ts.isFunctionExpression(node.expression)) &&
|
|
129
|
-
node.parent === ctx.parentBody
|
|
130
|
-
) {
|
|
131
|
-
ctx.returnStatement = node;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
ts.forEachChild(node, n => visitBody(ctx, n));
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function visitMain(ctx: MainContext, node: ts.Node): void {
|
|
138
|
-
if (
|
|
139
|
-
ts.isFunctionDeclaration(node) ||
|
|
140
|
-
ts.isFunctionExpression(node) ||
|
|
141
|
-
ts.isArrowFunction(node)
|
|
142
|
-
) {
|
|
143
|
-
processFunction(node, ctx.sourceFile, ctx.edits);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
ts.forEachChild(node, n => visitMain(ctx, n));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
const injectAutoDispose = (sourceFile: ts.SourceFile): string => {
|
|
151
|
-
let code = sourceFile.getFullText(),
|
|
152
|
-
ctx: MainContext = {
|
|
153
|
-
edits: [],
|
|
154
|
-
sourceFile
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
visitMain(ctx, sourceFile);
|
|
158
|
-
|
|
159
|
-
if (ctx.edits.length === 0) {
|
|
160
|
-
return code;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
let replacements: Replacement[] = [];
|
|
164
|
-
|
|
165
|
-
for (let i = 0, n = ctx.edits.length; i < n; i++) {
|
|
166
|
-
let edit = ctx.edits[i],
|
|
167
|
-
effects = edit.effectsToCapture;
|
|
168
|
-
|
|
169
|
-
for (let j = 0, m = effects.length; j < m; j++) {
|
|
170
|
-
let effect = effects[j],
|
|
171
|
-
original = code.substring(effect.start, effect.end).trim();
|
|
172
|
-
|
|
173
|
-
replacements.push({
|
|
174
|
-
end: effect.end,
|
|
175
|
-
newText: `const ${effect.name} = ${original.replace(TRAILING_SEMICOLON, '')}`,
|
|
176
|
-
start: effect.start
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
replacements.push({
|
|
181
|
-
end: edit.cleanupBodyEnd,
|
|
182
|
-
newText: `\n${edit.disposeCode}`,
|
|
183
|
-
start: edit.cleanupBodyStart
|
|
184
|
-
});
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return applyReplacements(code, replacements);
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
export { injectAutoDispose };
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|