@esportsplus/reactivity 0.27.0 → 0.27.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/compiler/array.d.ts +1 -1
- package/build/compiler/array.js +27 -7
- package/build/compiler/index.d.ts +5 -2
- package/build/compiler/index.js +43 -35
- package/build/compiler/object.d.ts +1 -1
- package/build/compiler/object.js +85 -41
- package/build/compiler/plugins/tsc.js +2 -2
- package/build/compiler/plugins/vite.d.ts +4 -4
- package/build/compiler/plugins/vite.js +2 -1
- package/build/constants.js +1 -1
- package/build/index.d.ts +3 -2
- package/build/index.js +3 -2
- package/build/reactive/array.js +3 -2
- package/build/reactive/index.d.ts +5 -3
- package/build/reactive/index.js +26 -5
- package/build/reactive/object.d.ts +13 -0
- package/build/reactive/object.js +86 -0
- package/build/types.d.ts +2 -2
- package/package.json +3 -3
- package/src/compiler/array.ts +44 -12
- package/src/compiler/index.ts +59 -41
- package/src/compiler/object.ts +99 -48
- package/src/compiler/plugins/tsc.ts +2 -2
- package/src/compiler/plugins/vite.ts +2 -1
- package/src/constants.ts +1 -4
- package/src/index.ts +3 -2
- package/src/reactive/array.ts +4 -2
- package/src/reactive/index.ts +36 -9
- package/src/reactive/object.ts +126 -0
- package/src/types.ts +2 -2
- package/test/debug.ts +7 -0
- package/test/vite.config.ts +2 -1
- package/build/compiler/primitives.d.ts +0 -4
- package/build/compiler/primitives.js +0 -258
- package/src/compiler/primitives.ts +0 -364
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
2
|
import type { Bindings } from '../types.js';
|
|
3
|
-
declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings,
|
|
3
|
+
declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.TypeChecker) => string;
|
|
4
4
|
export default _default;
|
package/build/compiler/array.js
CHANGED
|
@@ -1,7 +1,23 @@
|
|
|
1
|
-
import { ast, code as c } from '@esportsplus/typescript/compiler';
|
|
2
1
|
import { ts } from '@esportsplus/typescript';
|
|
3
|
-
import {
|
|
2
|
+
import { ast, code as c } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { COMPILER_NAMESPACE, COMPILER_TYPES } from '../constants.js';
|
|
4
|
+
import { isReactiveCall } from './index.js';
|
|
4
5
|
function visit(ctx, node) {
|
|
6
|
+
if (ts.isCallExpression(node) && isReactiveCall(node, ctx.checker) && node.arguments.length > 0) {
|
|
7
|
+
let arg = node.arguments[0], expression = ts.isAsExpression(arg) ? arg.expression : arg;
|
|
8
|
+
if (ts.isArrayLiteralExpression(expression)) {
|
|
9
|
+
if (node.parent && ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
|
|
10
|
+
ctx.bindings.set(node.parent.name.text, COMPILER_TYPES.Array);
|
|
11
|
+
}
|
|
12
|
+
ctx.replacements.push({
|
|
13
|
+
end: node.end,
|
|
14
|
+
newText: expression.elements.length > 0
|
|
15
|
+
? ` new ${COMPILER_NAMESPACE}.ReactiveArray(...${expression.getText(ctx.sourceFile)})`
|
|
16
|
+
: ` new ${COMPILER_NAMESPACE}.ReactiveArray()`,
|
|
17
|
+
start: node.pos
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
5
21
|
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
|
|
6
22
|
if (ts.isIdentifier(node.initializer) && ctx.bindings.get(node.initializer.text) === COMPILER_TYPES.Array) {
|
|
7
23
|
ctx.bindings.set(node.name.text, COMPILER_TYPES.Array);
|
|
@@ -42,24 +58,28 @@ function visit(ctx, node) {
|
|
|
42
58
|
if (ts.isBinaryExpression(node) &&
|
|
43
59
|
node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
|
|
44
60
|
ts.isElementAccessExpression(node.left)) {
|
|
45
|
-
let
|
|
46
|
-
if (
|
|
47
|
-
let index =
|
|
61
|
+
let element = node.left, name = ast.getExpressionName(element.expression);
|
|
62
|
+
if (name && ctx.bindings.get(name) === COMPILER_TYPES.Array) {
|
|
63
|
+
let index = element.argumentExpression.getText(ctx.sourceFile), value = node.right.getText(ctx.sourceFile);
|
|
48
64
|
ctx.replacements.push({
|
|
49
65
|
end: node.end,
|
|
50
|
-
newText: `${
|
|
66
|
+
newText: `${element.expression.getText(ctx.sourceFile)}.$set(${index}, ${value})`,
|
|
51
67
|
start: node.pos
|
|
52
68
|
});
|
|
53
69
|
}
|
|
54
70
|
}
|
|
55
71
|
ts.forEachChild(node, n => visit(ctx, n));
|
|
56
72
|
}
|
|
57
|
-
export default (sourceFile, bindings,
|
|
73
|
+
export default (sourceFile, bindings, checker) => {
|
|
58
74
|
let code = sourceFile.getFullText(), ctx = {
|
|
59
75
|
bindings,
|
|
76
|
+
checker,
|
|
60
77
|
replacements: [],
|
|
61
78
|
sourceFile
|
|
62
79
|
};
|
|
63
80
|
visit(ctx, sourceFile);
|
|
81
|
+
if (ctx.replacements.length === 0) {
|
|
82
|
+
return code;
|
|
83
|
+
}
|
|
64
84
|
return c.replace(code, ctx.replacements);
|
|
65
85
|
};
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import type { PluginContext } from '@esportsplus/typescript/compiler';
|
|
1
2
|
import { ts } from '@esportsplus/typescript';
|
|
2
3
|
import type { TransformResult } from '../types.js';
|
|
3
|
-
declare const
|
|
4
|
-
|
|
4
|
+
declare const analyze: (sourceFile: ts.SourceFile, _program: ts.Program, context: PluginContext) => void;
|
|
5
|
+
declare const isReactiveCall: (node: ts.CallExpression, _checker?: ts.TypeChecker) => boolean;
|
|
6
|
+
declare const transform: (sourceFile: ts.SourceFile, program: ts.Program, context?: PluginContext) => TransformResult;
|
|
7
|
+
export { analyze, isReactiveCall, transform };
|
package/build/compiler/index.js
CHANGED
|
@@ -1,57 +1,65 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import {
|
|
3
|
-
import { COMPILER_ENTRYPOINT,
|
|
2
|
+
import { ast, imports } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { COMPILER_ENTRYPOINT, COMPILER_NAMESPACE, PACKAGE } from '../constants.js';
|
|
4
4
|
import array from './array.js';
|
|
5
5
|
import object from './object.js';
|
|
6
|
-
|
|
7
|
-
let transforms = [object, array
|
|
6
|
+
const CONTEXT_KEY = 'reactivity:analyzed';
|
|
7
|
+
let transforms = [object, array];
|
|
8
|
+
function getAnalyzedFile(context, filename) {
|
|
9
|
+
return context?.get(CONTEXT_KEY)?.get(filename);
|
|
10
|
+
}
|
|
8
11
|
function hasReactiveImport(sourceFile) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
return false;
|
|
12
|
+
return imports.find(sourceFile, PACKAGE).some(i => i.specifiers.has(COMPILER_ENTRYPOINT));
|
|
13
|
+
}
|
|
14
|
+
function isReactiveCallNode(node) {
|
|
15
|
+
return ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === COMPILER_ENTRYPOINT;
|
|
16
16
|
}
|
|
17
|
-
|
|
18
|
-
if (!
|
|
17
|
+
const analyze = (sourceFile, _program, context) => {
|
|
18
|
+
if (!hasReactiveImport(sourceFile)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
let files = context.get(CONTEXT_KEY);
|
|
22
|
+
if (!files) {
|
|
23
|
+
files = new Map();
|
|
24
|
+
context.set(CONTEXT_KEY, files);
|
|
25
|
+
}
|
|
26
|
+
files.set(sourceFile.fileName, {
|
|
27
|
+
hasReactiveImport: true
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const isReactiveCall = (node, _checker) => {
|
|
31
|
+
if (!ts.isIdentifier(node.expression)) {
|
|
19
32
|
return false;
|
|
20
33
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
used = true;
|
|
30
|
-
return;
|
|
34
|
+
return node.expression.text === COMPILER_ENTRYPOINT;
|
|
35
|
+
};
|
|
36
|
+
const transform = (sourceFile, program, context) => {
|
|
37
|
+
let bindings = new Map(), changed = false, checker = program.getTypeChecker(), code = sourceFile.getFullText(), current = sourceFile, filename = sourceFile.fileName, result;
|
|
38
|
+
let analyzed = getAnalyzedFile(context, filename);
|
|
39
|
+
if (!analyzed) {
|
|
40
|
+
if (!hasReactiveImport(sourceFile)) {
|
|
41
|
+
return { changed: false, code, sourceFile };
|
|
31
42
|
}
|
|
32
|
-
ts.forEachChild(node, visit);
|
|
33
43
|
}
|
|
34
|
-
|
|
35
|
-
return used;
|
|
36
|
-
}
|
|
37
|
-
const transform = (sourceFile, program) => {
|
|
38
|
-
let bindings = new Map(), changed = false, checker = program.getTypeChecker(), code = sourceFile.getFullText(), current = sourceFile, result;
|
|
39
|
-
if (!hasReactiveImport(sourceFile) || !hasReactiveUsage(code)) {
|
|
44
|
+
else if (!analyzed.hasReactiveImport) {
|
|
40
45
|
return { changed: false, code, sourceFile };
|
|
41
46
|
}
|
|
42
47
|
for (let i = 0, n = transforms.length; i < n; i++) {
|
|
43
|
-
result = transforms[i](current, bindings,
|
|
48
|
+
result = transforms[i](current, bindings, checker);
|
|
44
49
|
if (result !== code) {
|
|
45
|
-
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
46
50
|
code = result;
|
|
47
51
|
changed = true;
|
|
52
|
+
current = ts.createSourceFile(sourceFile.fileName, result, sourceFile.languageVersion, true);
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
if (changed) {
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
let remove = [];
|
|
57
|
+
if (!ast.hasMatch(current, isReactiveCallNode)) {
|
|
58
|
+
remove.push(COMPILER_ENTRYPOINT);
|
|
59
|
+
}
|
|
60
|
+
code = imports.modify(code, current, PACKAGE, { namespace: COMPILER_NAMESPACE, remove });
|
|
53
61
|
sourceFile = ts.createSourceFile(sourceFile.fileName, code, sourceFile.languageVersion, true);
|
|
54
62
|
}
|
|
55
63
|
return { changed, code, sourceFile };
|
|
56
64
|
};
|
|
57
|
-
export { transform };
|
|
65
|
+
export { analyze, isReactiveCall, transform };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
2
|
import type { Bindings } from '../types.js';
|
|
3
|
-
declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings,
|
|
3
|
+
declare const _default: (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.TypeChecker) => string;
|
|
4
4
|
export default _default;
|
package/build/compiler/object.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import { code as c,
|
|
3
|
-
import {
|
|
2
|
+
import { code as c, uid } from '@esportsplus/typescript/compiler';
|
|
3
|
+
import { COMPILER_NAMESPACE, COMPILER_TYPES } from '../constants.js';
|
|
4
|
+
import { isReactiveCall } from './index.js';
|
|
4
5
|
function analyzeProperty(prop, sourceFile) {
|
|
5
6
|
if (!ts.isPropertyAssignment(prop)) {
|
|
6
7
|
return null;
|
|
@@ -12,62 +13,103 @@ function analyzeProperty(prop, sourceFile) {
|
|
|
12
13
|
else {
|
|
13
14
|
return null;
|
|
14
15
|
}
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
let unwrapped = prop.initializer, value = unwrapped, valueText = value.getText(sourceFile);
|
|
17
|
+
while (ts.isAsExpression(unwrapped) || ts.isTypeAssertionExpression(unwrapped) || ts.isParenthesizedExpression(unwrapped)) {
|
|
18
|
+
unwrapped = unwrapped.expression;
|
|
18
19
|
}
|
|
19
|
-
if (ts.
|
|
20
|
-
|
|
20
|
+
if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {
|
|
21
|
+
return { isStatic: false, key, type: COMPILER_TYPES.Computed, valueText };
|
|
22
|
+
}
|
|
23
|
+
if (ts.isArrayLiteralExpression(unwrapped)) {
|
|
24
|
+
let elements = unwrapped.elements, isStatic = value === unwrapped;
|
|
21
25
|
for (let i = 0, n = elements.length; i < n; i++) {
|
|
22
|
-
if (
|
|
23
|
-
|
|
26
|
+
if (isStatic && !isStaticValue(elements[i])) {
|
|
27
|
+
isStatic = false;
|
|
24
28
|
}
|
|
25
|
-
elementsText += elements[i].getText(sourceFile);
|
|
26
29
|
}
|
|
27
|
-
return { key, type: COMPILER_TYPES.Array, valueText
|
|
30
|
+
return { isStatic, key, type: COMPILER_TYPES.Array, valueText };
|
|
28
31
|
}
|
|
29
|
-
return { key, type: COMPILER_TYPES.Signal, valueText };
|
|
32
|
+
return { isStatic: isStaticValue(value), key, type: COMPILER_TYPES.Signal, valueText };
|
|
30
33
|
}
|
|
31
|
-
function buildClassCode(className, properties
|
|
32
|
-
let accessors = [],
|
|
33
|
-
fields.push(`[${ns}.REACTIVE_OBJECT] = true;`);
|
|
34
|
+
function buildClassCode(className, properties) {
|
|
35
|
+
let accessors = [], body = [], fields = [], generics = [], parameters = [], setters = 0;
|
|
34
36
|
for (let i = 0, n = properties.length; i < n; i++) {
|
|
35
|
-
let { key, type, valueText } = properties[i]
|
|
37
|
+
let { isStatic, key, type, valueText } = properties[i], generic = `T${parameters.length}`, parameter = `_p${parameters.length}`;
|
|
36
38
|
if (type === COMPILER_TYPES.Signal) {
|
|
37
|
-
let
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
let value = `_v${setters++}`;
|
|
40
|
+
if (isStatic) {
|
|
41
|
+
accessors.push(`
|
|
42
|
+
get ${key}() {
|
|
43
|
+
return ${COMPILER_NAMESPACE}.read(this.#${key});
|
|
44
|
+
}
|
|
45
|
+
set ${key}(${value}) {
|
|
46
|
+
${COMPILER_NAMESPACE}.write(this.#${key}, ${value});
|
|
47
|
+
}
|
|
48
|
+
`);
|
|
49
|
+
fields.push(`#${key} = this[${COMPILER_NAMESPACE}.SIGNAL](${valueText});`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
accessors.push(`
|
|
53
|
+
get ${key}() {
|
|
54
|
+
return ${COMPILER_NAMESPACE}.read(this.#${key}) as ${generic};
|
|
55
|
+
}
|
|
56
|
+
set ${key}(${value}) {
|
|
57
|
+
${COMPILER_NAMESPACE}.write(this.#${key}, ${value});
|
|
58
|
+
}
|
|
59
|
+
`);
|
|
60
|
+
body.push(`this.#${key} = this[${COMPILER_NAMESPACE}.SIGNAL](${parameter});`);
|
|
61
|
+
fields.push(`#${key};`);
|
|
62
|
+
generics.push(generic);
|
|
63
|
+
parameters.push(`${parameter}: ${generic}`);
|
|
64
|
+
}
|
|
41
65
|
}
|
|
42
66
|
else if (type === COMPILER_TYPES.Array) {
|
|
43
|
-
|
|
44
|
-
|
|
67
|
+
accessors.push(`
|
|
68
|
+
get ${key}() {
|
|
69
|
+
return this.#${key};
|
|
70
|
+
}
|
|
71
|
+
`);
|
|
72
|
+
body.push(`this.#${key} = this[${COMPILER_NAMESPACE}.REACTIVE_ARRAY](${parameter});`);
|
|
73
|
+
fields.push(`#${key};`);
|
|
74
|
+
generics.push(`${generic} extends unknown[]`);
|
|
75
|
+
parameters.push(`${parameter}: ${generic}`);
|
|
45
76
|
}
|
|
46
77
|
else if (type === COMPILER_TYPES.Computed) {
|
|
47
|
-
accessors.push(`
|
|
48
|
-
|
|
49
|
-
|
|
78
|
+
accessors.push(`
|
|
79
|
+
get ${key}() {
|
|
80
|
+
return ${COMPILER_NAMESPACE}.read(this.#${key});
|
|
81
|
+
}
|
|
82
|
+
`);
|
|
83
|
+
body.push(`this.#${key} = this[${COMPILER_NAMESPACE}.COMPUTED](${parameter});`);
|
|
84
|
+
fields.push(`#${key};`);
|
|
85
|
+
generics.push(`${generic} extends ${COMPILER_NAMESPACE}.Computed<ReturnType<${generic}>>['fn']`);
|
|
86
|
+
parameters.push(`${parameter}: ${generic}`);
|
|
50
87
|
}
|
|
51
88
|
}
|
|
52
89
|
return `
|
|
53
|
-
class ${className} {
|
|
90
|
+
class ${className}${generics.length > 0 ? `<${generics.join(', ')}>` : ''} extends ${COMPILER_NAMESPACE}.ReactiveObject<any> {
|
|
54
91
|
${fields.join('\n')}
|
|
55
|
-
${
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
${disposeStatements.length > 0 ? disposeStatements.join('\n') : ''}
|
|
92
|
+
constructor(${parameters.join(', ')}) {
|
|
93
|
+
super(null);
|
|
94
|
+
${body.join('\n')}
|
|
59
95
|
}
|
|
96
|
+
${accessors.join('\n')}
|
|
60
97
|
}
|
|
61
98
|
`;
|
|
62
99
|
}
|
|
63
|
-
function
|
|
64
|
-
if (
|
|
65
|
-
|
|
100
|
+
function isStaticValue(node) {
|
|
101
|
+
if (ts.isNumericLiteral(node) ||
|
|
102
|
+
ts.isStringLiteral(node) ||
|
|
103
|
+
node.kind === ts.SyntaxKind.TrueKeyword ||
|
|
104
|
+
node.kind === ts.SyntaxKind.FalseKeyword ||
|
|
105
|
+
node.kind === ts.SyntaxKind.NullKeyword ||
|
|
106
|
+
node.kind === ts.SyntaxKind.UndefinedKeyword) {
|
|
107
|
+
return true;
|
|
66
108
|
}
|
|
67
|
-
if (node.
|
|
68
|
-
return
|
|
109
|
+
if (ts.isPrefixUnaryExpression(node) && ts.isNumericLiteral(node.operand)) {
|
|
110
|
+
return true;
|
|
69
111
|
}
|
|
70
|
-
return
|
|
112
|
+
return false;
|
|
71
113
|
}
|
|
72
114
|
function visit(ctx, node) {
|
|
73
115
|
if (ts.isImportDeclaration(node)) {
|
|
@@ -99,7 +141,7 @@ function visit(ctx, node) {
|
|
|
99
141
|
}
|
|
100
142
|
}
|
|
101
143
|
ctx.calls.push({
|
|
102
|
-
className:
|
|
144
|
+
className: uid('ReactiveObject'),
|
|
103
145
|
end: node.end,
|
|
104
146
|
properties,
|
|
105
147
|
start: node.pos,
|
|
@@ -109,21 +151,20 @@ function visit(ctx, node) {
|
|
|
109
151
|
}
|
|
110
152
|
ts.forEachChild(node, n => visit(ctx, n));
|
|
111
153
|
}
|
|
112
|
-
export default (sourceFile, bindings,
|
|
154
|
+
export default (sourceFile, bindings, checker) => {
|
|
113
155
|
let code = sourceFile.getFullText(), ctx = {
|
|
114
156
|
bindings,
|
|
115
157
|
calls: [],
|
|
116
158
|
checker,
|
|
117
159
|
classCounter: 0,
|
|
118
160
|
lastImportEnd: 0,
|
|
119
|
-
ns,
|
|
120
161
|
sourceFile
|
|
121
162
|
};
|
|
122
163
|
visit(ctx, sourceFile);
|
|
123
164
|
if (ctx.calls.length === 0) {
|
|
124
165
|
return code;
|
|
125
166
|
}
|
|
126
|
-
let classes = ctx.calls.map(c => buildClassCode(c.className, c.properties
|
|
167
|
+
let classes = ctx.calls.map(c => buildClassCode(c.className, c.properties)).join('\n'), replacements = [];
|
|
127
168
|
replacements.push({
|
|
128
169
|
end: ctx.lastImportEnd,
|
|
129
170
|
newText: code.substring(0, ctx.lastImportEnd) + '\n' + classes + '\n',
|
|
@@ -133,7 +174,10 @@ export default (sourceFile, bindings, ns, checker) => {
|
|
|
133
174
|
let call = ctx.calls[i];
|
|
134
175
|
replacements.push({
|
|
135
176
|
end: call.end,
|
|
136
|
-
newText: ` new ${call.className}(
|
|
177
|
+
newText: ` new ${call.className}(${call.properties
|
|
178
|
+
.filter(({ isStatic, type }) => !isStatic || type === COMPILER_TYPES.Computed)
|
|
179
|
+
.map(p => p.valueText)
|
|
180
|
+
.join(', ')})`,
|
|
137
181
|
start: call.start
|
|
138
182
|
});
|
|
139
183
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { plugin } from '@esportsplus/typescript/compiler';
|
|
2
|
-
import { transform } from '../index.js';
|
|
3
|
-
export default plugin.tsc(transform);
|
|
2
|
+
import { analyze, transform } from '../index.js';
|
|
3
|
+
export default plugin.tsc({ analyze, transform });
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
declare const _default: ({ root }?: {
|
|
2
2
|
root?: string;
|
|
3
3
|
}) => {
|
|
4
|
-
configResolved(config:
|
|
5
|
-
enforce:
|
|
4
|
+
configResolved: (config: unknown) => void;
|
|
5
|
+
enforce: "pre";
|
|
6
6
|
name: string;
|
|
7
|
-
transform(code: string, id: string)
|
|
7
|
+
transform: (code: string, id: string) => {
|
|
8
8
|
code: string;
|
|
9
9
|
map: null;
|
|
10
10
|
} | null;
|
|
11
|
-
watchChange(id: string)
|
|
11
|
+
watchChange: (id: string) => void;
|
|
12
12
|
};
|
|
13
13
|
export default _default;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { PACKAGE } from '../../constants.js';
|
|
2
2
|
import { plugin } from '@esportsplus/typescript/compiler';
|
|
3
|
-
import { transform } from '../index.js';
|
|
3
|
+
import { analyze, transform } from '../index.js';
|
|
4
4
|
export default plugin.vite({
|
|
5
|
+
analyze,
|
|
5
6
|
name: PACKAGE,
|
|
6
7
|
transform
|
|
7
8
|
});
|
package/build/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { uid } from '@esportsplus/typescript/compiler';
|
|
2
2
|
const COMPILER_ENTRYPOINT = 'reactive';
|
|
3
3
|
const COMPILER_ENTRYPOINT_REGEX = /\breactive\b/;
|
|
4
|
-
const COMPILER_NAMESPACE = uid(
|
|
4
|
+
const COMPILER_NAMESPACE = uid('reactivity');
|
|
5
5
|
var COMPILER_TYPES;
|
|
6
6
|
(function (COMPILER_TYPES) {
|
|
7
7
|
COMPILER_TYPES[COMPILER_TYPES["Array"] = 0] = "Array";
|
package/build/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { isPromise } from '@esportsplus/utilities';
|
|
2
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL } from './constants.js';
|
|
3
|
+
export { default as reactive, ReactiveArray, ReactiveObject } from './reactive/index.js';
|
|
3
4
|
export * from './system.js';
|
|
4
5
|
export * from './types.js';
|
package/build/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { isPromise } from '@esportsplus/utilities';
|
|
2
|
+
export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL } from './constants.js';
|
|
3
|
+
export { default as reactive, ReactiveArray, ReactiveObject } from './reactive/index.js';
|
|
3
4
|
export * from './system.js';
|
|
4
5
|
export * from './types.js';
|
package/build/reactive/array.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { isArray } from '@esportsplus/utilities';
|
|
2
2
|
import { read, signal, write } from '../system.js';
|
|
3
|
-
import { REACTIVE_ARRAY
|
|
3
|
+
import { REACTIVE_ARRAY } from '../constants.js';
|
|
4
|
+
import { isReactiveObject } from './object.js';
|
|
4
5
|
function dispose(value) {
|
|
5
|
-
if (value
|
|
6
|
+
if (isReactiveObject(value)) {
|
|
6
7
|
value.dispose();
|
|
7
8
|
}
|
|
8
9
|
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { ReactiveArray } from './array.js';
|
|
2
1
|
import { Reactive } from '../types.js';
|
|
2
|
+
import { ReactiveArray } from './array.js';
|
|
3
|
+
import { ReactiveObject } from './object.js';
|
|
3
4
|
type Guard<T> = T extends Record<PropertyKey, unknown> ? T extends {
|
|
4
5
|
dispose: any;
|
|
5
6
|
} ? {
|
|
6
7
|
never: '[ dispose ] is a reserved key';
|
|
7
8
|
} : T : never;
|
|
8
|
-
declare function reactive<T extends
|
|
9
|
+
declare function reactive<T extends unknown[]>(input: T): Reactive<T>;
|
|
10
|
+
declare function reactive<T extends Record<PropertyKey, unknown>>(input: Guard<T>): Reactive<T>;
|
|
9
11
|
export default reactive;
|
|
10
|
-
export { reactive, ReactiveArray };
|
|
12
|
+
export { reactive, ReactiveArray, ReactiveObject };
|
package/build/reactive/index.js
CHANGED
|
@@ -1,8 +1,29 @@
|
|
|
1
|
+
import { onCleanup, root } from '@esportsplus/reactivity';
|
|
2
|
+
import { isArray, isObject } from '@esportsplus/utilities';
|
|
1
3
|
import { ReactiveArray } from './array.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import { ReactiveObject } from './object.js';
|
|
5
|
+
import { PACKAGE } from '../constants.js';
|
|
6
|
+
function reactive(input) {
|
|
7
|
+
let dispose = false, value = root(() => {
|
|
8
|
+
let response;
|
|
9
|
+
if (isObject(input)) {
|
|
10
|
+
response = new ReactiveObject(input);
|
|
11
|
+
}
|
|
12
|
+
else if (isArray(input)) {
|
|
13
|
+
response = new ReactiveArray(...input);
|
|
14
|
+
}
|
|
15
|
+
if (response) {
|
|
16
|
+
if (root.disposables) {
|
|
17
|
+
dispose = true;
|
|
18
|
+
}
|
|
19
|
+
return response;
|
|
20
|
+
}
|
|
21
|
+
throw new Error(`${PACKAGE}: 'reactive' received invalid input - ${JSON.stringify(input)}`);
|
|
22
|
+
});
|
|
23
|
+
if (dispose) {
|
|
24
|
+
onCleanup(() => value.dispose());
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
6
27
|
}
|
|
7
28
|
export default reactive;
|
|
8
|
-
export { reactive, ReactiveArray };
|
|
29
|
+
export { reactive, ReactiveArray, ReactiveObject };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Computed, Signal } from '../types.js';
|
|
2
|
+
import { COMPUTED, REACTIVE_ARRAY, SIGNAL } from '../constants.js';
|
|
3
|
+
import { ReactiveArray } from './array.js';
|
|
4
|
+
declare class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
5
|
+
protected disposers: VoidFunction[] | null;
|
|
6
|
+
constructor(data: T | null);
|
|
7
|
+
protected [REACTIVE_ARRAY]<U>(value: U[]): ReactiveArray<U>;
|
|
8
|
+
protected [COMPUTED]<T extends Computed<ReturnType<T>>['fn']>(value: T): Computed<ReturnType<T>> | Signal<ReturnType<T> | undefined>;
|
|
9
|
+
protected [SIGNAL]<T>(value: T): Signal<T>;
|
|
10
|
+
dispose(): void;
|
|
11
|
+
}
|
|
12
|
+
declare const isReactiveObject: (value: any) => value is ReactiveObject<any>;
|
|
13
|
+
export { isReactiveObject, ReactiveObject };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { defineProperty, isArray, isPromise } from '@esportsplus/utilities';
|
|
2
|
+
import { computed, dispose, effect, read, root, signal, write } from '../system.js';
|
|
3
|
+
import { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL } from '../constants.js';
|
|
4
|
+
import { ReactiveArray } from './array.js';
|
|
5
|
+
class ReactiveObject {
|
|
6
|
+
disposers = null;
|
|
7
|
+
constructor(data) {
|
|
8
|
+
if (data == null) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
for (let key in data) {
|
|
12
|
+
let value = data[key], type = typeof value;
|
|
13
|
+
if (type === 'function') {
|
|
14
|
+
let node = this[COMPUTED](value);
|
|
15
|
+
defineProperty(this, key, {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
get: () => read(node)
|
|
18
|
+
});
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (value == null || type !== 'object') {
|
|
22
|
+
}
|
|
23
|
+
else if (isArray(value)) {
|
|
24
|
+
defineProperty(this, key, {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
value: this[REACTIVE_ARRAY](value)
|
|
27
|
+
});
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
let node = signal(value);
|
|
31
|
+
defineProperty(this, key, {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get() {
|
|
34
|
+
return read(node);
|
|
35
|
+
},
|
|
36
|
+
set(v) {
|
|
37
|
+
write(node, v);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
[REACTIVE_ARRAY](value) {
|
|
43
|
+
let node = new ReactiveArray(...value);
|
|
44
|
+
(this.disposers ??= []).push(() => node.dispose());
|
|
45
|
+
return node;
|
|
46
|
+
}
|
|
47
|
+
[COMPUTED](value) {
|
|
48
|
+
return root(() => {
|
|
49
|
+
let node = computed(value);
|
|
50
|
+
if (isPromise(node.value)) {
|
|
51
|
+
let factory = node, version = 0;
|
|
52
|
+
node = signal(undefined);
|
|
53
|
+
(this.disposers ??= []).push(effect(() => {
|
|
54
|
+
let id = ++version;
|
|
55
|
+
read(factory).then((v) => {
|
|
56
|
+
if (id !== version) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
write(node, v);
|
|
60
|
+
});
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
(this.disposers ??= []).push(() => dispose(node));
|
|
65
|
+
}
|
|
66
|
+
return node;
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
[SIGNAL](value) {
|
|
70
|
+
return signal(value);
|
|
71
|
+
}
|
|
72
|
+
dispose() {
|
|
73
|
+
let disposers = this.disposers, disposer;
|
|
74
|
+
if (!disposers) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
while (disposer = disposers.pop()) {
|
|
78
|
+
disposer();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
Object.defineProperty(ReactiveObject.prototype, REACTIVE_OBJECT, { value: true });
|
|
83
|
+
const isReactiveObject = (value) => {
|
|
84
|
+
return typeof value === 'object' && value !== null && value[REACTIVE_OBJECT] === true;
|
|
85
|
+
};
|
|
86
|
+
export { isReactiveObject, ReactiveObject };
|
package/build/types.d.ts
CHANGED
|
@@ -29,8 +29,8 @@ type Reactive<T> = T extends (...args: unknown[]) => Promise<infer R> ? (R | und
|
|
|
29
29
|
readonly [READONLY]: true;
|
|
30
30
|
} : T extends (...args: any[]) => infer R ? R & {
|
|
31
31
|
readonly [READONLY]: true;
|
|
32
|
-
} : T extends (infer U)[] ? U[] & Pick<ReactiveArray<U>, 'clear' | 'on' | 'once'> : T extends Record<PropertyKey, unknown> ? {
|
|
33
|
-
[K in keyof T]: T[K]
|
|
32
|
+
} : T extends (infer U)[] ? U[] & Pick<ReactiveArray<U>, 'clear' | 'dispose' | 'on' | 'once'> : T extends Record<PropertyKey, unknown> ? {
|
|
33
|
+
[K in keyof T]: Reactive<T[K]>;
|
|
34
34
|
} & {
|
|
35
35
|
dispose: VoidFunction;
|
|
36
36
|
} : T;
|
package/package.json
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
"@esportsplus/utilities": "^0.27.2"
|
|
5
5
|
},
|
|
6
6
|
"devDependencies": {
|
|
7
|
-
"@esportsplus/typescript": "^0.
|
|
7
|
+
"@esportsplus/typescript": "^0.24.2",
|
|
8
8
|
"@types/node": "^25.0.3",
|
|
9
|
-
"vite": "^7.3.
|
|
9
|
+
"vite": "^7.3.1"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
"type": "module",
|
|
33
33
|
"types": "build/index.d.ts",
|
|
34
|
-
"version": "0.27.
|
|
34
|
+
"version": "0.27.3",
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "tsc",
|
|
37
37
|
"build:test": "pnpm build && vite build --config test/vite.config.ts",
|