@esportsplus/reactivity 0.23.0 → 0.23.2

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 (44) hide show
  1. package/build/transformer/detector.js +38 -0
  2. package/build/transformer/index.d.ts +9 -0
  3. package/build/transformer/{core/index.js → index.js} +9 -18
  4. package/build/transformer/plugins/esbuild.d.ts +1 -3
  5. package/build/transformer/plugins/esbuild.js +5 -4
  6. package/build/transformer/plugins/tsc.js +1 -1
  7. package/build/transformer/plugins/vite.d.ts +1 -3
  8. package/build/transformer/plugins/vite.js +4 -4
  9. package/build/transformer/{core/transforms/reactive-array.d.ts → transforms/array.d.ts} +1 -1
  10. package/build/transformer/transforms/array.js +93 -0
  11. package/build/transformer/{core/transforms/reactive-object.d.ts → transforms/object.d.ts} +1 -1
  12. package/build/transformer/transforms/object.js +163 -0
  13. package/build/transformer/{core/transforms/reactive-primitives.d.ts → transforms/primitives.d.ts} +1 -1
  14. package/build/transformer/transforms/primitives.js +335 -0
  15. package/build/transformer/{core/transforms → transforms}/utilities.d.ts +1 -2
  16. package/build/transformer/transforms/utilities.js +73 -0
  17. package/build/types.d.ts +1 -4
  18. package/package.json +8 -12
  19. package/readme.md +0 -24
  20. package/src/transformer/detector.ts +65 -0
  21. package/src/transformer/{core/index.ts → index.ts} +10 -25
  22. package/src/transformer/plugins/esbuild.ts +5 -6
  23. package/src/transformer/plugins/tsc.ts +1 -1
  24. package/src/transformer/plugins/vite.ts +4 -8
  25. package/src/transformer/transforms/array.ts +143 -0
  26. package/src/transformer/transforms/object.ts +251 -0
  27. package/src/transformer/transforms/primitives.ts +461 -0
  28. package/src/transformer/transforms/utilities.ts +119 -0
  29. package/src/types.ts +0 -5
  30. package/build/transformer/core/detector.js +0 -6
  31. package/build/transformer/core/index.d.ts +0 -10
  32. package/build/transformer/core/transforms/auto-dispose.d.ts +0 -3
  33. package/build/transformer/core/transforms/auto-dispose.js +0 -116
  34. package/build/transformer/core/transforms/reactive-array.js +0 -89
  35. package/build/transformer/core/transforms/reactive-object.js +0 -155
  36. package/build/transformer/core/transforms/reactive-primitives.js +0 -325
  37. package/build/transformer/core/transforms/utilities.js +0 -57
  38. package/src/transformer/core/detector.ts +0 -12
  39. package/src/transformer/core/transforms/auto-dispose.ts +0 -194
  40. package/src/transformer/core/transforms/reactive-array.ts +0 -140
  41. package/src/transformer/core/transforms/reactive-object.ts +0 -244
  42. package/src/transformer/core/transforms/reactive-primitives.ts +0 -459
  43. package/src/transformer/core/transforms/utilities.ts +0 -95
  44. /package/build/transformer/{core/detector.d.ts → detector.d.ts} +0 -0
@@ -1,116 +0,0 @@
1
- import { uid } from '@esportsplus/typescript/transformer';
2
- import ts from 'typescript';
3
- import { applyReplacements } from './utilities.js';
4
- const TRAILING_SEMICOLON = /;$/;
5
- function visitFunctionBody(body, parentBody) {
6
- let disposables = [], effectsToCapture = [], returnStatement = null;
7
- function visit(n) {
8
- if (ts.isVariableDeclaration(n) &&
9
- ts.isIdentifier(n.name) &&
10
- n.initializer &&
11
- ts.isCallExpression(n.initializer) &&
12
- ts.isIdentifier(n.initializer.expression) &&
13
- n.initializer.expression.text === 'reactive') {
14
- disposables.push({ name: n.name.text, type: 'reactive' });
15
- }
16
- if (ts.isCallExpression(n) &&
17
- ts.isIdentifier(n.expression) &&
18
- n.expression.text === 'effect') {
19
- if (ts.isVariableDeclaration(n.parent) && ts.isIdentifier(n.parent.name)) {
20
- disposables.push({ name: n.parent.name.text, type: 'effect' });
21
- }
22
- else if (ts.isExpressionStatement(n.parent)) {
23
- let name = uid('effect');
24
- effectsToCapture.push({
25
- end: n.parent.end,
26
- name,
27
- start: n.parent.pos
28
- });
29
- disposables.push({ name, type: 'effect' });
30
- }
31
- }
32
- if (ts.isReturnStatement(n) &&
33
- n.expression &&
34
- (ts.isArrowFunction(n.expression) || ts.isFunctionExpression(n.expression)) &&
35
- n.parent === parentBody) {
36
- returnStatement = n;
37
- }
38
- ts.forEachChild(n, visit);
39
- }
40
- visit(body);
41
- return { disposables, effectsToCapture, returnStatement };
42
- }
43
- function processFunction(node, sourceFile, edits) {
44
- if (!node.body || !ts.isBlock(node.body)) {
45
- return;
46
- }
47
- let result = visitFunctionBody(node.body, node.body), disposables = result.disposables, effectsToCapture = result.effectsToCapture, returnStatement = result.returnStatement;
48
- if (disposables.length === 0 || !returnStatement || !returnStatement.expression) {
49
- return;
50
- }
51
- let cleanupFn = returnStatement.expression;
52
- if (!cleanupFn.body) {
53
- return;
54
- }
55
- let disposeStatements = [];
56
- for (let i = disposables.length - 1; i >= 0; i--) {
57
- let d = disposables[i];
58
- if (d.type === 'reactive') {
59
- disposeStatements.push(`${d.name}.dispose();`);
60
- }
61
- else {
62
- disposeStatements.push(`${d.name}();`);
63
- }
64
- }
65
- let disposeCode = disposeStatements.join('\n');
66
- if (ts.isBlock(cleanupFn.body)) {
67
- edits.push({
68
- cleanupBodyEnd: cleanupFn.body.statements[0]?.pos ?? cleanupFn.body.end - 1,
69
- cleanupBodyStart: cleanupFn.body.pos + 1,
70
- disposeCode,
71
- effectsToCapture
72
- });
73
- }
74
- else {
75
- edits.push({
76
- cleanupBodyEnd: cleanupFn.body.end,
77
- cleanupBodyStart: cleanupFn.body.pos,
78
- disposeCode: `{ ${disposeCode}\n return ${cleanupFn.body.getText(sourceFile)}; }`,
79
- effectsToCapture
80
- });
81
- }
82
- }
83
- const injectAutoDispose = (sourceFile) => {
84
- let code = sourceFile.getFullText(), edits = [];
85
- function visit(node) {
86
- if (ts.isFunctionDeclaration(node) ||
87
- ts.isFunctionExpression(node) ||
88
- ts.isArrowFunction(node)) {
89
- processFunction(node, sourceFile, edits);
90
- }
91
- ts.forEachChild(node, visit);
92
- }
93
- visit(sourceFile);
94
- if (edits.length === 0) {
95
- return code;
96
- }
97
- let replacements = [];
98
- for (let i = 0, n = edits.length; i < n; i++) {
99
- let edit = edits[i], effects = edit.effectsToCapture;
100
- for (let j = 0, m = effects.length; j < m; j++) {
101
- let effect = effects[j], original = code.substring(effect.start, effect.end).trim();
102
- replacements.push({
103
- end: effect.end,
104
- newText: `const ${effect.name} = ${original.replace(TRAILING_SEMICOLON, '')}`,
105
- start: effect.start
106
- });
107
- }
108
- replacements.push({
109
- end: edit.cleanupBodyEnd,
110
- newText: `\n${edit.disposeCode}`,
111
- start: edit.cleanupBodyStart
112
- });
113
- }
114
- return applyReplacements(code, replacements);
115
- };
116
- export { injectAutoDispose };
@@ -1,89 +0,0 @@
1
- import ts from 'typescript';
2
- import { applyReplacements } from './utilities.js';
3
- function getPropertyPath(node) {
4
- let current = node, parts = [];
5
- while (ts.isPropertyAccessExpression(current)) {
6
- parts.unshift(current.name.text);
7
- current = current.expression;
8
- }
9
- if (ts.isIdentifier(current)) {
10
- parts.unshift(current.text);
11
- return parts.join('.');
12
- }
13
- return null;
14
- }
15
- function getExpressionName(node) {
16
- if (ts.isIdentifier(node)) {
17
- return node.text;
18
- }
19
- if (ts.isPropertyAccessExpression(node)) {
20
- return getPropertyPath(node);
21
- }
22
- return null;
23
- }
24
- function isAssignmentTarget(node) {
25
- let parent = node.parent;
26
- if ((ts.isBinaryExpression(parent) && parent.left === node) ||
27
- ts.isPostfixUnaryExpression(parent) ||
28
- ts.isPrefixUnaryExpression(parent)) {
29
- return true;
30
- }
31
- return false;
32
- }
33
- const transformReactiveArrays = (sourceFile, bindings) => {
34
- let code = sourceFile.getFullText(), replacements = [];
35
- function visit(node) {
36
- if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name) && node.initializer) {
37
- if (ts.isIdentifier(node.initializer) && bindings.get(node.initializer.text) === 'array') {
38
- bindings.set(node.name.text, 'array');
39
- }
40
- if (ts.isPropertyAccessExpression(node.initializer)) {
41
- let path = getPropertyPath(node.initializer);
42
- if (path && bindings.get(path) === 'array') {
43
- bindings.set(node.name.text, 'array');
44
- }
45
- }
46
- }
47
- if ((ts.isFunctionDeclaration(node) || ts.isArrowFunction(node)) && node.parameters) {
48
- for (let i = 0, n = node.parameters.length; i < n; i++) {
49
- let param = node.parameters[i];
50
- if ((ts.isIdentifier(param.name) && param.type) &&
51
- ts.isTypeReferenceNode(param.type) &&
52
- ts.isIdentifier(param.type.typeName) &&
53
- param.type.typeName.text === 'ReactiveArray') {
54
- bindings.set(param.name.text, 'array');
55
- }
56
- }
57
- }
58
- if (ts.isPropertyAccessExpression(node) &&
59
- node.name.text === 'length' &&
60
- !isAssignmentTarget(node)) {
61
- let objName = getExpressionName(node.expression);
62
- if (objName && bindings.get(objName) === 'array') {
63
- let objText = node.expression.getText(sourceFile);
64
- replacements.push({
65
- end: node.end,
66
- newText: `${objText}.$length()`,
67
- start: node.pos
68
- });
69
- }
70
- }
71
- if (ts.isBinaryExpression(node) &&
72
- node.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
73
- ts.isElementAccessExpression(node.left)) {
74
- let elemAccess = node.left, objName = getExpressionName(elemAccess.expression);
75
- if (objName && bindings.get(objName) === 'array') {
76
- let indexText = elemAccess.argumentExpression.getText(sourceFile), objText = elemAccess.expression.getText(sourceFile), valueText = node.right.getText(sourceFile);
77
- replacements.push({
78
- end: node.end,
79
- newText: `${objText}.$set(${indexText}, ${valueText})`,
80
- start: node.pos
81
- });
82
- }
83
- }
84
- ts.forEachChild(node, visit);
85
- }
86
- visit(sourceFile);
87
- return applyReplacements(code, replacements);
88
- };
89
- export { transformReactiveArrays };
@@ -1,155 +0,0 @@
1
- import { uid } from '@esportsplus/typescript/transformer';
2
- import ts from 'typescript';
3
- import { addMissingImports, applyReplacements } from './utilities.js';
4
- const CLASS_NAME_REGEX = /class (\w+)/;
5
- const EXTRA_IMPORTS = [
6
- { module: '@esportsplus/reactivity/constants', specifier: 'REACTIVE_OBJECT' },
7
- { module: '@esportsplus/reactivity/reactive/array', specifier: 'ReactiveArray' }
8
- ];
9
- function analyzeProperty(prop, sourceFile) {
10
- if (!ts.isPropertyAssignment(prop)) {
11
- return null;
12
- }
13
- let key;
14
- if (ts.isIdentifier(prop.name) || ts.isStringLiteral(prop.name)) {
15
- key = prop.name.text;
16
- }
17
- else {
18
- return null;
19
- }
20
- let value = prop.initializer, valueText = value.getText(sourceFile);
21
- if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) {
22
- return { key, type: 'computed', valueText };
23
- }
24
- if (ts.isArrayLiteralExpression(value)) {
25
- return { key, type: 'array', valueText };
26
- }
27
- return { key, type: 'signal', valueText };
28
- }
29
- function buildClassCode(className, properties) {
30
- let accessors = [], disposeStatements = [], fields = [];
31
- fields.push(`[REACTIVE_OBJECT] = true;`);
32
- for (let i = 0, n = properties.length; i < n; i++) {
33
- let { key, type, valueText } = properties[i];
34
- if (type === 'signal') {
35
- let param = uid('v');
36
- fields.push(`#${key} = signal(${valueText});`);
37
- accessors.push(`get ${key}() { return read(this.#${key}); }`);
38
- accessors.push(`set ${key}(${param}) { set(this.#${key}, ${param}); }`);
39
- }
40
- else if (type === 'array') {
41
- let elements = valueText.slice(1, -1);
42
- fields.push(`${key} = new ReactiveArray(${elements});`);
43
- disposeStatements.push(`this.${key}.dispose();`);
44
- }
45
- else if (type === 'computed') {
46
- fields.push(`#${key}: Computed<unknown> | null = null;`);
47
- accessors.push(`get ${key}() { return read(this.#${key} ??= computed(${valueText})); }`);
48
- disposeStatements.push(`if (this.#${key}) dispose(this.#${key});`);
49
- }
50
- }
51
- let disposeBody = disposeStatements.length > 0 ? disposeStatements.join('\n') : '';
52
- return `
53
- class ${className} {
54
- ${fields.join('\n')}
55
- ${accessors.join('\n')}
56
-
57
- dispose() {
58
- ${disposeBody}
59
- }
60
- }
61
- `;
62
- }
63
- const transformReactiveObjects = (sourceFile, bindings) => {
64
- let allNeededImports = new Set(), calls = [], code = sourceFile.getFullText(), hasReactiveImport = false, lastImportEnd = 0;
65
- function visit(node) {
66
- if (ts.isImportDeclaration(node)) {
67
- lastImportEnd = node.end;
68
- if (ts.isStringLiteral(node.moduleSpecifier) &&
69
- node.moduleSpecifier.text.includes('@esportsplus/reactivity')) {
70
- let clause = node.importClause;
71
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
72
- let elements = clause.namedBindings.elements;
73
- for (let i = 0, n = elements.length; i < n; i++) {
74
- if (elements[i].name.text === 'reactive') {
75
- hasReactiveImport = true;
76
- break;
77
- }
78
- }
79
- }
80
- }
81
- }
82
- if (hasReactiveImport &&
83
- ts.isCallExpression(node) &&
84
- ts.isIdentifier(node.expression) &&
85
- node.expression.text === 'reactive') {
86
- let arg = node.arguments[0];
87
- if (arg && ts.isObjectLiteralExpression(arg)) {
88
- let varName = null;
89
- if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
90
- varName = node.parent.name.text;
91
- bindings.set(varName, 'object');
92
- }
93
- let needsImports = new Set(), properties = [];
94
- needsImports.add('REACTIVE_OBJECT');
95
- let props = arg.properties;
96
- for (let i = 0, n = props.length; i < n; i++) {
97
- let prop = props[i];
98
- if (ts.isSpreadAssignment(prop)) {
99
- return;
100
- }
101
- let analyzed = analyzeProperty(prop, sourceFile);
102
- if (!analyzed) {
103
- return;
104
- }
105
- properties.push(analyzed);
106
- if (analyzed.type === 'signal') {
107
- needsImports.add('read');
108
- needsImports.add('set');
109
- needsImports.add('signal');
110
- }
111
- else if (analyzed.type === 'array') {
112
- needsImports.add('ReactiveArray');
113
- if (varName) {
114
- bindings.set(`${varName}.${analyzed.key}`, 'array');
115
- }
116
- }
117
- else if (analyzed.type === 'computed') {
118
- needsImports.add('computed');
119
- needsImports.add('dispose');
120
- needsImports.add('read');
121
- }
122
- }
123
- needsImports.forEach(imp => allNeededImports.add(imp));
124
- calls.push({
125
- end: node.end,
126
- generatedClass: buildClassCode(uid('ReactiveObject'), properties),
127
- needsImports,
128
- start: node.pos,
129
- varName
130
- });
131
- }
132
- }
133
- ts.forEachChild(node, visit);
134
- }
135
- visit(sourceFile);
136
- if (calls.length === 0) {
137
- return code;
138
- }
139
- let replacements = [];
140
- replacements.push({
141
- end: lastImportEnd,
142
- newText: code.substring(0, lastImportEnd) + '\n' + calls.map(c => c.generatedClass).join('\n') + '\n',
143
- start: 0
144
- });
145
- for (let i = 0, n = calls.length; i < n; i++) {
146
- let call = calls[i], classMatch = call.generatedClass.match(CLASS_NAME_REGEX);
147
- replacements.push({
148
- end: call.end,
149
- newText: ` new ${classMatch ? classMatch[1] : 'ReactiveObject'}()`,
150
- start: call.start
151
- });
152
- }
153
- return addMissingImports(applyReplacements(code, replacements), allNeededImports, EXTRA_IMPORTS);
154
- };
155
- export { transformReactiveObjects };
@@ -1,325 +0,0 @@
1
- import { uid } from '@esportsplus/typescript/transformer';
2
- import ts from 'typescript';
3
- import { addMissingImports, applyReplacements } from './utilities.js';
4
- function findEnclosingScope(node) {
5
- let current = node.parent;
6
- while (current) {
7
- if (ts.isBlock(current) ||
8
- ts.isSourceFile(current) ||
9
- ts.isFunctionDeclaration(current) ||
10
- ts.isFunctionExpression(current) ||
11
- ts.isArrowFunction(current) ||
12
- ts.isForStatement(current) ||
13
- ts.isForInStatement(current) ||
14
- ts.isForOfStatement(current)) {
15
- return current;
16
- }
17
- current = current.parent;
18
- }
19
- return node.getSourceFile();
20
- }
21
- function findBinding(bindings, name, node) {
22
- for (let i = 0, n = bindings.length; i < n; i++) {
23
- let b = bindings[i];
24
- if (b.name === name && isInScope(node, b)) {
25
- return b;
26
- }
27
- }
28
- return undefined;
29
- }
30
- function isInComputedRange(ranges, start, end) {
31
- for (let i = 0, n = ranges.length; i < n; i++) {
32
- let r = ranges[i];
33
- if (start >= r.start && end <= r.end) {
34
- return true;
35
- }
36
- }
37
- return false;
38
- }
39
- function isInScope(reference, binding) {
40
- let current = reference;
41
- while (current) {
42
- if (current === binding.scope) {
43
- return true;
44
- }
45
- current = current.parent;
46
- }
47
- return false;
48
- }
49
- function classifyReactiveArg(arg) {
50
- if (ts.isArrowFunction(arg) || ts.isFunctionExpression(arg)) {
51
- return 'computed';
52
- }
53
- if (ts.isObjectLiteralExpression(arg) || ts.isArrayLiteralExpression(arg)) {
54
- return null;
55
- }
56
- return 'signal';
57
- }
58
- function isInDeclarationInit(node) {
59
- let parent = node.parent;
60
- if (ts.isVariableDeclaration(parent) && parent.initializer === node) {
61
- return true;
62
- }
63
- return false;
64
- }
65
- function isReactiveReassignment(node) {
66
- let parent = node.parent;
67
- if (ts.isBinaryExpression(parent) &&
68
- parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
69
- parent.right === node &&
70
- ts.isCallExpression(node) &&
71
- ts.isIdentifier(node.expression) &&
72
- node.expression.text === 'reactive') {
73
- return true;
74
- }
75
- return false;
76
- }
77
- function isWriteContext(node) {
78
- let parent = node.parent;
79
- if (ts.isBinaryExpression(parent) && parent.left === node) {
80
- let op = parent.operatorToken.kind;
81
- if (op === ts.SyntaxKind.EqualsToken) {
82
- return 'simple';
83
- }
84
- if (op >= ts.SyntaxKind.PlusEqualsToken && op <= ts.SyntaxKind.CaretEqualsToken) {
85
- return 'compound';
86
- }
87
- if (op === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
88
- op === ts.SyntaxKind.BarBarEqualsToken ||
89
- op === ts.SyntaxKind.QuestionQuestionEqualsToken) {
90
- return 'compound';
91
- }
92
- }
93
- if (ts.isPostfixUnaryExpression(parent) || ts.isPrefixUnaryExpression(parent)) {
94
- let op = parent.operator;
95
- if (op === ts.SyntaxKind.PlusPlusToken || op === ts.SyntaxKind.MinusMinusToken) {
96
- return 'increment';
97
- }
98
- }
99
- return false;
100
- }
101
- function getCompoundOperator(kind) {
102
- if (kind === ts.SyntaxKind.PlusEqualsToken) {
103
- return '+';
104
- }
105
- else if (kind === ts.SyntaxKind.MinusEqualsToken) {
106
- return '-';
107
- }
108
- else if (kind === ts.SyntaxKind.AsteriskEqualsToken) {
109
- return '*';
110
- }
111
- else if (kind === ts.SyntaxKind.SlashEqualsToken) {
112
- return '/';
113
- }
114
- else if (kind === ts.SyntaxKind.PercentEqualsToken) {
115
- return '%';
116
- }
117
- else if (kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken) {
118
- return '**';
119
- }
120
- else if (kind === ts.SyntaxKind.AmpersandEqualsToken) {
121
- return '&';
122
- }
123
- else if (kind === ts.SyntaxKind.BarEqualsToken) {
124
- return '|';
125
- }
126
- else if (kind === ts.SyntaxKind.CaretEqualsToken) {
127
- return '^';
128
- }
129
- else if (kind === ts.SyntaxKind.LessThanLessThanEqualsToken) {
130
- return '<<';
131
- }
132
- else if (kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken) {
133
- return '>>';
134
- }
135
- else if (kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken) {
136
- return '>>>';
137
- }
138
- else if (kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken) {
139
- return '&&';
140
- }
141
- else if (kind === ts.SyntaxKind.BarBarEqualsToken) {
142
- return '||';
143
- }
144
- else if (kind === ts.SyntaxKind.QuestionQuestionEqualsToken) {
145
- return '??';
146
- }
147
- else {
148
- return '+';
149
- }
150
- }
151
- function transformComputedArg(arg, scopedBindings, sourceFile, neededImports) {
152
- let argStart = arg.getStart(sourceFile), innerReplacements = [], text = arg.getText(sourceFile);
153
- function visitArg(node) {
154
- if (ts.isIdentifier(node)) {
155
- if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
156
- ts.forEachChild(node, visitArg);
157
- return;
158
- }
159
- if (ts.isCallExpression(node.parent) && node.parent.expression === node) {
160
- ts.forEachChild(node, visitArg);
161
- return;
162
- }
163
- let binding = findBinding(scopedBindings, node.text, node);
164
- if (binding) {
165
- neededImports.add('read');
166
- innerReplacements.push({
167
- end: node.end - argStart,
168
- newText: `read(${node.text})`,
169
- start: node.getStart(sourceFile) - argStart
170
- });
171
- }
172
- }
173
- ts.forEachChild(node, visitArg);
174
- }
175
- visitArg(arg);
176
- return applyReplacements(text, innerReplacements);
177
- }
178
- const transformReactivePrimitives = (sourceFile, bindings) => {
179
- let code = sourceFile.getFullText(), computedArgRanges = [], hasReactiveImport = false, neededImports = new Set(), replacements = [], scopedBindings = [];
180
- function visit(node) {
181
- if (ts.isImportDeclaration(node) &&
182
- ts.isStringLiteral(node.moduleSpecifier) &&
183
- node.moduleSpecifier.text.includes('@esportsplus/reactivity')) {
184
- let clause = node.importClause;
185
- if (clause?.namedBindings && ts.isNamedImports(clause.namedBindings)) {
186
- for (let i = 0, n = clause.namedBindings.elements.length; i < n; i++) {
187
- if (clause.namedBindings.elements[i].name.text === 'reactive') {
188
- hasReactiveImport = true;
189
- break;
190
- }
191
- }
192
- }
193
- }
194
- if (hasReactiveImport &&
195
- ts.isCallExpression(node) &&
196
- ts.isIdentifier(node.expression) &&
197
- node.expression.text === 'reactive' &&
198
- node.arguments.length > 0) {
199
- let arg = node.arguments[0], classification = classifyReactiveArg(arg);
200
- if (classification) {
201
- let varName = null;
202
- if (ts.isVariableDeclaration(node.parent) && ts.isIdentifier(node.parent.name)) {
203
- varName = node.parent.name.text;
204
- }
205
- else if (ts.isBinaryExpression(node.parent) &&
206
- node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
207
- ts.isIdentifier(node.parent.left)) {
208
- varName = node.parent.left.text;
209
- }
210
- if (varName) {
211
- let scope = findEnclosingScope(node);
212
- scopedBindings.push({ name: varName, scope, type: classification });
213
- bindings.set(varName, classification);
214
- }
215
- if (classification === 'computed') {
216
- computedArgRanges.push({
217
- end: arg.end,
218
- start: arg.getStart(sourceFile)
219
- });
220
- let argText = transformComputedArg(arg, scopedBindings, sourceFile, neededImports);
221
- replacements.push({
222
- end: node.end,
223
- newText: `computed(${argText})`,
224
- start: node.pos
225
- });
226
- neededImports.add('computed');
227
- }
228
- else {
229
- let argText = arg.getText(sourceFile);
230
- replacements.push({
231
- end: node.end,
232
- newText: `signal(${argText})`,
233
- start: node.pos
234
- });
235
- neededImports.add('signal');
236
- }
237
- }
238
- }
239
- if (ts.isIdentifier(node) && !isInDeclarationInit(node.parent)) {
240
- if (ts.isPropertyAccessExpression(node.parent) && node.parent.name === node) {
241
- ts.forEachChild(node, visit);
242
- return;
243
- }
244
- let nodeStart = node.getStart(sourceFile);
245
- let insideComputedArg = isInComputedRange(computedArgRanges, nodeStart, node.end);
246
- if (insideComputedArg) {
247
- ts.forEachChild(node, visit);
248
- return;
249
- }
250
- let binding = findBinding(scopedBindings, node.text, node), name = node.text;
251
- if (binding) {
252
- if (!isReactiveReassignment(node.parent) &&
253
- !(ts.isTypeOfExpression(node.parent) && node.parent.expression === node)) {
254
- let writeCtx = isWriteContext(node);
255
- if (writeCtx) {
256
- if (binding.type !== 'computed') {
257
- neededImports.add('set');
258
- let parent = node.parent;
259
- if (writeCtx === 'simple' && ts.isBinaryExpression(parent)) {
260
- let valueText = parent.right.getText(sourceFile);
261
- replacements.push({
262
- end: parent.end,
263
- newText: `set(${name}, ${valueText})`,
264
- start: parent.pos
265
- });
266
- }
267
- else if (writeCtx === 'compound' && ts.isBinaryExpression(parent)) {
268
- let op = getCompoundOperator(parent.operatorToken.kind), valueText = parent.right.getText(sourceFile);
269
- replacements.push({
270
- end: parent.end,
271
- newText: `set(${name}, ${name}.value ${op} ${valueText})`,
272
- start: parent.pos
273
- });
274
- }
275
- else if (writeCtx === 'increment') {
276
- let isPrefix = ts.isPrefixUnaryExpression(parent), op = parent.operator, delta = op === ts.SyntaxKind.PlusPlusToken ? '+ 1' : '- 1';
277
- if (ts.isExpressionStatement(parent.parent)) {
278
- replacements.push({
279
- end: parent.end,
280
- newText: `set(${name}, ${name}.value ${delta})`,
281
- start: parent.pos
282
- });
283
- }
284
- else if (isPrefix) {
285
- replacements.push({
286
- end: parent.end,
287
- newText: `(set(${name}, ${name}.value ${delta}), ${name}.value)`,
288
- start: parent.pos
289
- });
290
- }
291
- else {
292
- let tmp = uid('tmp');
293
- replacements.push({
294
- end: parent.end,
295
- newText: `((${tmp}) => (set(${name}, ${tmp} ${delta}), ${tmp}))(${name}.value)`,
296
- start: parent.pos
297
- });
298
- }
299
- }
300
- }
301
- }
302
- else {
303
- neededImports.add('read');
304
- replacements.push({
305
- end: node.end,
306
- newText: `read(${name})`,
307
- start: node.pos
308
- });
309
- }
310
- }
311
- }
312
- }
313
- ts.forEachChild(node, visit);
314
- }
315
- visit(sourceFile);
316
- if (replacements.length === 0) {
317
- return code;
318
- }
319
- let result = applyReplacements(code, replacements);
320
- if (neededImports.size > 0) {
321
- result = addMissingImports(result, neededImports);
322
- }
323
- return result;
324
- };
325
- export { transformReactivePrimitives };