jslike 1.7.2 → 1.8.0
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/README.md +4 -1
- package/dist/esm/index.js +16 -5
- package/dist/esm/interpreter/index.js +2 -1
- package/dist/esm/interpreter/interpreter.js +320 -66
- package/dist/esm/parser.js +5040 -1
- package/dist/index.cjs +5267 -57
- package/dist/index.d.cts +5338 -70
- package/dist/index.d.ts +5338 -70
- package/dist/index.js +5267 -57
- package/dist/validator/index.cjs +5018 -3
- package/dist/validator/index.js +5018 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -473,7 +473,10 @@ Execute JavaScript code and return the result.
|
|
|
473
473
|
const result = await execute(code, env, {
|
|
474
474
|
moduleResolver, // For import statements
|
|
475
475
|
executionController, // For pause/resume/abort
|
|
476
|
-
abortSignal
|
|
476
|
+
abortSignal, // For cancellation
|
|
477
|
+
sourcePath, // Optional importer path for resolving top-level imports
|
|
478
|
+
typescript, // Parse TypeScript syntax
|
|
479
|
+
tsx // Parse TypeScript + JSX syntax
|
|
477
480
|
});
|
|
478
481
|
```
|
|
479
482
|
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Use bundled Acorn parser for zero runtime dependencies
|
|
2
|
-
import { parse as acornParse } from './parser.js';
|
|
2
|
+
import { parse as acornParse, tsParse, tsxParse } from './parser.js';
|
|
3
3
|
import { Interpreter } from './interpreter/interpreter.js';
|
|
4
4
|
import { Environment, ReturnValue } from './runtime/environment.js';
|
|
5
5
|
import { createGlobalEnvironment } from './runtime/builtins.js';
|
|
@@ -9,7 +9,7 @@ function containsModuleSyntax(code) {
|
|
|
9
9
|
// Trigger module mode for:
|
|
10
10
|
// 1. import/export statements
|
|
11
11
|
// 2. Top-level await (await not inside a function)
|
|
12
|
-
if (
|
|
12
|
+
if (/(^|[;{\n])\s*(import|export)\s+/m.test(code)) {
|
|
13
13
|
return true;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -23,6 +23,13 @@ function containsModuleSyntax(code) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function isTypeScriptPath(sourcePath) {
|
|
27
|
+
return typeof sourcePath === 'string' && /\.(ts|tsx|mts|cts)$/i.test(sourcePath);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isTSXPath(sourcePath) {
|
|
31
|
+
return typeof sourcePath === 'string' && /\.tsx$/i.test(sourcePath);
|
|
32
|
+
}
|
|
26
33
|
|
|
27
34
|
export function parse(code, options = {}) {
|
|
28
35
|
// Determine sourceType: use 'module' ONLY if explicitly requested or if code has imports/exports
|
|
@@ -32,10 +39,13 @@ export function parse(code, options = {}) {
|
|
|
32
39
|
sourceType = 'module';
|
|
33
40
|
}
|
|
34
41
|
|
|
42
|
+
const shouldParseTypeScript = options.typescript || options.tsx || isTypeScriptPath(options.sourcePath);
|
|
43
|
+
const parser = options.tsx || isTSXPath(options.sourcePath) ? tsxParse : shouldParseTypeScript ? tsParse : acornParse;
|
|
44
|
+
|
|
35
45
|
// Parse with Acorn
|
|
36
46
|
try {
|
|
37
|
-
return
|
|
38
|
-
ecmaVersion: 2022, // Support ES2022 features (including top-level await)
|
|
47
|
+
return parser(code, {
|
|
48
|
+
ecmaVersion: shouldParseTypeScript ? 'latest' : 2022, // Support ES2022 features (including top-level await)
|
|
39
49
|
sourceType: sourceType,
|
|
40
50
|
locations: true, // Track source locations for better error messages
|
|
41
51
|
allowReturnOutsideFunction: true, // Allow top-level return statements
|
|
@@ -119,7 +129,8 @@ export async function execute(code, env = null, options = {}) {
|
|
|
119
129
|
const interpreter = new Interpreter(env, {
|
|
120
130
|
moduleResolver: options.moduleResolver,
|
|
121
131
|
abortSignal: options.abortSignal,
|
|
122
|
-
executionController: controller
|
|
132
|
+
executionController: controller,
|
|
133
|
+
currentModulePath: options.sourcePath
|
|
123
134
|
});
|
|
124
135
|
|
|
125
136
|
// Use async evaluation if:
|
|
@@ -103,7 +103,8 @@ export class WangInterpreter {
|
|
|
103
103
|
const options = {
|
|
104
104
|
moduleResolver: this.moduleResolver,
|
|
105
105
|
executionController: userOptions.executionController,
|
|
106
|
-
abortSignal: userOptions.abortSignal // Pass abort signal to interpreter for cancellation support
|
|
106
|
+
abortSignal: userOptions.abortSignal, // Pass abort signal to interpreter for cancellation support
|
|
107
|
+
sourcePath: userOptions.sourcePath ?? this.options.sourcePath
|
|
107
108
|
// sourceType will be auto-detected from code
|
|
108
109
|
};
|
|
109
110
|
|
|
@@ -1,13 +1,70 @@
|
|
|
1
1
|
import { Environment, ReturnValue, BreakSignal, ContinueSignal, ThrowSignal } from '../runtime/environment.js';
|
|
2
|
-
import { parse as acornParse } from '../parser.js';
|
|
2
|
+
import { parse as acornParse, tsParse, tsxParse } from '../parser.js';
|
|
3
3
|
import { createMethodNotFoundError } from '../errors/enhanced-error.js';
|
|
4
4
|
|
|
5
|
+
function isTypeScriptPath(sourcePath) {
|
|
6
|
+
return typeof sourcePath === 'string' && /\.(ts|tsx|mts|cts)$/i.test(sourcePath);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function isTSXPath(sourcePath) {
|
|
10
|
+
return typeof sourcePath === 'string' && /\.tsx$/i.test(sourcePath);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function parseModuleCode(code, sourcePath) {
|
|
14
|
+
const parser = isTSXPath(sourcePath) ? tsxParse : isTypeScriptPath(sourcePath) ? tsParse : acornParse;
|
|
15
|
+
return parser(code, {
|
|
16
|
+
ecmaVersion: isTypeScriptPath(sourcePath) ? 'latest' : 2020,
|
|
17
|
+
sourceType: 'module',
|
|
18
|
+
locations: isTypeScriptPath(sourcePath)
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const TYPE_ONLY_DECLARATIONS = new Set([
|
|
23
|
+
'TSTypeAliasDeclaration',
|
|
24
|
+
'TSInterfaceDeclaration',
|
|
25
|
+
'TSDeclareFunction'
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const TYPE_WRAPPER_EXPRESSIONS = new Set([
|
|
29
|
+
'TSAsExpression',
|
|
30
|
+
'TSTypeAssertion',
|
|
31
|
+
'TSNonNullExpression',
|
|
32
|
+
'TSSatisfiesExpression',
|
|
33
|
+
'TSInstantiationExpression'
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
function isTypeOnlyDeclaration(node) {
|
|
37
|
+
return TYPE_ONLY_DECLARATIONS.has(node?.type) || node?.declare === true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function isTypeWrapperExpression(node) {
|
|
41
|
+
return TYPE_WRAPPER_EXPRESSIONS.has(node?.type);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getTypeWrapperInnerExpression(node) {
|
|
45
|
+
return node.expression;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function createUnsupportedTypeScriptRuntimeError(node) {
|
|
49
|
+
return new Error(`Unsupported runtime TypeScript syntax: ${node.type}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function getPatternName(pattern) {
|
|
53
|
+
if (!pattern) return undefined;
|
|
54
|
+
if (pattern.type === 'Identifier') return pattern.name;
|
|
55
|
+
if (pattern.type === 'AssignmentPattern') return getPatternName(pattern.left);
|
|
56
|
+
if (pattern.type === 'TSParameterProperty') return getPatternName(pattern.parameter);
|
|
57
|
+
return pattern.name;
|
|
58
|
+
}
|
|
59
|
+
|
|
5
60
|
export class Interpreter {
|
|
6
61
|
constructor(globalEnv, options = {}) {
|
|
7
62
|
this.globalEnv = globalEnv;
|
|
8
63
|
this.moduleResolver = options.moduleResolver;
|
|
9
64
|
this.moduleCache = new Map(); // Cache loaded modules
|
|
65
|
+
this.moduleResolutionCache = options.moduleResolutionCache || new Map();
|
|
10
66
|
this.moduleExports = {}; // Track exports in current module
|
|
67
|
+
this.currentModulePath = options.currentModulePath;
|
|
11
68
|
this.abortSignal = options.abortSignal;
|
|
12
69
|
this.executionController = options.executionController;
|
|
13
70
|
}
|
|
@@ -39,6 +96,121 @@ export class Interpreter {
|
|
|
39
96
|
}
|
|
40
97
|
}
|
|
41
98
|
|
|
99
|
+
async evaluateAsyncRawValue(node, env) {
|
|
100
|
+
if (!node) return { value: undefined };
|
|
101
|
+
|
|
102
|
+
if (node.type === 'CallExpression') {
|
|
103
|
+
return await this.evaluateCallExpressionAsyncRawValue(node, env);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (node.type === 'MemberExpression') {
|
|
107
|
+
const obj = (await this.evaluateAsyncRawValue(node.object, env)).value;
|
|
108
|
+
|
|
109
|
+
if (node.optional && (obj === null || obj === undefined)) {
|
|
110
|
+
return { value: undefined };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (obj === null || obj === undefined) {
|
|
114
|
+
throw new TypeError(`Cannot read property of ${obj}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const prop = node.computed
|
|
118
|
+
? await this.evaluateAsync(node.property, env)
|
|
119
|
+
: node.property.name;
|
|
120
|
+
|
|
121
|
+
return { value: obj[prop] };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (node.type === 'ChainExpression') {
|
|
125
|
+
return await this.evaluateAsyncRawValue(node.expression, env);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (node.type === 'ConditionalExpression') {
|
|
129
|
+
const test = await this.evaluateAsync(node.test, env);
|
|
130
|
+
return await this.evaluateAsyncRawValue(test ? node.consequent : node.alternate, env);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (node.type === 'LogicalExpression') {
|
|
134
|
+
const left = await this.evaluateAsync(node.left, env);
|
|
135
|
+
if (node.operator === '&&') {
|
|
136
|
+
return left ? await this.evaluateAsyncRawValue(node.right, env) : { value: left };
|
|
137
|
+
}
|
|
138
|
+
if (node.operator === '||') {
|
|
139
|
+
return left ? { value: left } : await this.evaluateAsyncRawValue(node.right, env);
|
|
140
|
+
}
|
|
141
|
+
if (node.operator === '??') {
|
|
142
|
+
return left !== null && left !== undefined
|
|
143
|
+
? { value: left }
|
|
144
|
+
: await this.evaluateAsyncRawValue(node.right, env);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (node.type === 'SequenceExpression') {
|
|
149
|
+
for (let i = 0; i < node.expressions.length - 1; i++) {
|
|
150
|
+
await this.evaluateAsync(node.expressions[i], env);
|
|
151
|
+
}
|
|
152
|
+
return await this.evaluateAsyncRawValue(node.expressions[node.expressions.length - 1], env);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (node.type === 'NewExpression') {
|
|
156
|
+
return { value: this.evaluateNewExpression(node, env) };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (['Literal', 'Identifier', 'ThisExpression', 'Super'].includes(node.type)) {
|
|
160
|
+
return { value: this.evaluate(node, env) };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { value: await this.evaluateAsync(node, env) };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async evaluateCallExpressionAsyncRawValue(node, env) {
|
|
167
|
+
let thisContext = undefined;
|
|
168
|
+
let callee;
|
|
169
|
+
let objectName = null;
|
|
170
|
+
let methodName = null;
|
|
171
|
+
|
|
172
|
+
if (node.callee.type === 'MemberExpression') {
|
|
173
|
+
thisContext = (await this.evaluateAsyncRawValue(node.callee.object, env)).value;
|
|
174
|
+
if (node.callee.optional && (thisContext === null || thisContext === undefined)) {
|
|
175
|
+
return { value: undefined };
|
|
176
|
+
}
|
|
177
|
+
const prop = node.callee.computed
|
|
178
|
+
? await this.evaluateAsync(node.callee.property, env)
|
|
179
|
+
: node.callee.property.name;
|
|
180
|
+
callee = thisContext[prop];
|
|
181
|
+
|
|
182
|
+
methodName = prop;
|
|
183
|
+
objectName = this.getExpressionName(node.callee.object);
|
|
184
|
+
} else {
|
|
185
|
+
callee = await this.evaluateAsync(node.callee, env);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (node.optional && (callee === null || callee === undefined)) {
|
|
189
|
+
return { value: undefined };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const rawArgs = [];
|
|
193
|
+
for (const arg of node.arguments) {
|
|
194
|
+
rawArgs.push(await this.evaluateAsync(arg, env));
|
|
195
|
+
}
|
|
196
|
+
const args = this.flattenSpreadArgs(rawArgs);
|
|
197
|
+
|
|
198
|
+
if (typeof callee === 'function') {
|
|
199
|
+
const value = thisContext !== undefined
|
|
200
|
+
? callee.call(thisContext, ...args)
|
|
201
|
+
: callee(...args);
|
|
202
|
+
return { value };
|
|
203
|
+
} else if (callee && callee.__isFunction) {
|
|
204
|
+
return { value: this.callUserFunction(callee, args, env, thisContext) };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (objectName && methodName) {
|
|
208
|
+
throw createMethodNotFoundError(objectName, methodName, thisContext);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
throw new TypeError(`${node.callee.name || 'Expression'} is not a function`);
|
|
212
|
+
}
|
|
213
|
+
|
|
42
214
|
// Async evaluation for async functions - handles await expressions
|
|
43
215
|
async evaluateAsync(node, env) {
|
|
44
216
|
if (!node) return undefined;
|
|
@@ -48,6 +220,27 @@ export class Interpreter {
|
|
|
48
220
|
const checkpointPromise = this._getCheckpointPromise(node, env);
|
|
49
221
|
if (checkpointPromise) await checkpointPromise;
|
|
50
222
|
|
|
223
|
+
if (isTypeOnlyDeclaration(node)) {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (node.type === 'TSExportAssignment' || node.type === 'TSImportEqualsDeclaration') {
|
|
228
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (node.type === 'TSEnumDeclaration') {
|
|
232
|
+
return this.evaluateTSEnumDeclaration(node, env);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (node.type === 'TSModuleDeclaration') {
|
|
236
|
+
if (node.declare) return undefined;
|
|
237
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (isTypeWrapperExpression(node)) {
|
|
241
|
+
return await this.evaluateAsync(getTypeWrapperInnerExpression(node), env);
|
|
242
|
+
}
|
|
243
|
+
|
|
51
244
|
// Handle await expressions by actually awaiting the promise
|
|
52
245
|
if (node.type === 'AwaitExpression') {
|
|
53
246
|
const promise = await this.evaluateAsync(node.argument, env);
|
|
@@ -77,7 +270,7 @@ export class Interpreter {
|
|
|
77
270
|
if (node.type === 'VariableDeclaration') {
|
|
78
271
|
for (const declarator of node.declarations) {
|
|
79
272
|
const value = declarator.init
|
|
80
|
-
? await this.
|
|
273
|
+
? (await this.evaluateAsyncRawValue(declarator.init, env)).value
|
|
81
274
|
: undefined;
|
|
82
275
|
|
|
83
276
|
const isConst = node.kind === 'const';
|
|
@@ -134,54 +327,8 @@ export class Interpreter {
|
|
|
134
327
|
|
|
135
328
|
// For call expressions (might be calling async functions)
|
|
136
329
|
if (node.type === 'CallExpression') {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let objectName = null;
|
|
140
|
-
let methodName = null;
|
|
141
|
-
|
|
142
|
-
if (node.callee.type === 'MemberExpression') {
|
|
143
|
-
thisContext = await this.evaluateAsync(node.callee.object, env);
|
|
144
|
-
if (node.callee.optional && (thisContext === null || thisContext === undefined)) {
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
const prop = node.callee.computed
|
|
148
|
-
? await this.evaluateAsync(node.callee.property, env)
|
|
149
|
-
: node.callee.property.name;
|
|
150
|
-
callee = thisContext[prop];
|
|
151
|
-
|
|
152
|
-
// Capture names for enhanced error messages
|
|
153
|
-
methodName = prop;
|
|
154
|
-
objectName = this.getExpressionName(node.callee.object);
|
|
155
|
-
} else {
|
|
156
|
-
callee = await this.evaluateAsync(node.callee, env);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Handle optional call - if optional and callee is null/undefined, return undefined
|
|
160
|
-
if (node.optional && (callee === null || callee === undefined)) {
|
|
161
|
-
return undefined;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const rawArgs = [];
|
|
165
|
-
for (const arg of node.arguments) {
|
|
166
|
-
rawArgs.push(await this.evaluateAsync(arg, env));
|
|
167
|
-
}
|
|
168
|
-
const args = this.flattenSpreadArgs(rawArgs);
|
|
169
|
-
|
|
170
|
-
if (typeof callee === 'function') {
|
|
171
|
-
if (thisContext !== undefined) {
|
|
172
|
-
return await callee.call(thisContext, ...args);
|
|
173
|
-
}
|
|
174
|
-
return await callee(...args);
|
|
175
|
-
} else if (callee && callee.__isFunction) {
|
|
176
|
-
return await this.callUserFunction(callee, args, env, thisContext);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Throw enhanced error for member expression calls
|
|
180
|
-
if (objectName && methodName) {
|
|
181
|
-
throw createMethodNotFoundError(objectName, methodName, thisContext);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
throw new TypeError(`${node.callee.name || 'Expression'} is not a function`);
|
|
330
|
+
const result = (await this.evaluateCallExpressionAsyncRawValue(node, env)).value;
|
|
331
|
+
return await result;
|
|
185
332
|
}
|
|
186
333
|
|
|
187
334
|
// For chain expressions (optional chaining)
|
|
@@ -191,7 +338,7 @@ export class Interpreter {
|
|
|
191
338
|
|
|
192
339
|
// For member expressions in async context
|
|
193
340
|
if (node.type === 'MemberExpression') {
|
|
194
|
-
const obj = await this.
|
|
341
|
+
const obj = (await this.evaluateAsyncRawValue(node.object, env)).value;
|
|
195
342
|
|
|
196
343
|
// Handle optional chaining
|
|
197
344
|
if (node.optional && (obj === null || obj === undefined)) {
|
|
@@ -513,7 +660,7 @@ export class Interpreter {
|
|
|
513
660
|
|
|
514
661
|
// For AssignmentExpression with async value
|
|
515
662
|
if (node.type === 'AssignmentExpression') {
|
|
516
|
-
const value = await this.
|
|
663
|
+
const value = (await this.evaluateAsyncRawValue(node.right, env)).value;
|
|
517
664
|
|
|
518
665
|
if (node.left.type === 'Identifier') {
|
|
519
666
|
const name = node.left.name;
|
|
@@ -745,6 +892,27 @@ export class Interpreter {
|
|
|
745
892
|
// Check for abort signal before evaluating
|
|
746
893
|
this.checkAbortSignal();
|
|
747
894
|
|
|
895
|
+
if (isTypeOnlyDeclaration(node)) {
|
|
896
|
+
return undefined;
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
if (node.type === 'TSExportAssignment' || node.type === 'TSImportEqualsDeclaration') {
|
|
900
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
if (node.type === 'TSEnumDeclaration') {
|
|
904
|
+
return this.evaluateTSEnumDeclaration(node, env);
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
if (node.type === 'TSModuleDeclaration') {
|
|
908
|
+
if (node.declare) return undefined;
|
|
909
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
if (isTypeWrapperExpression(node)) {
|
|
913
|
+
return this.evaluate(getTypeWrapperInnerExpression(node), env);
|
|
914
|
+
}
|
|
915
|
+
|
|
748
916
|
switch (node.type) {
|
|
749
917
|
case 'Program':
|
|
750
918
|
return this.evaluateProgram(node, env);
|
|
@@ -905,6 +1073,9 @@ export class Interpreter {
|
|
|
905
1073
|
case 'Property':
|
|
906
1074
|
return this.evaluateProperty(node, env);
|
|
907
1075
|
|
|
1076
|
+
case 'TSEnumDeclaration':
|
|
1077
|
+
return this.evaluateTSEnumDeclaration(node, env);
|
|
1078
|
+
|
|
908
1079
|
// JSX Support
|
|
909
1080
|
case 'JSXElement':
|
|
910
1081
|
return this.evaluateJSXElement(node, env);
|
|
@@ -1327,7 +1498,9 @@ export class Interpreter {
|
|
|
1327
1498
|
|
|
1328
1499
|
// Bind parameters
|
|
1329
1500
|
for (let i = 0; i < metadata.params.length; i++) {
|
|
1330
|
-
const param = metadata.params[i]
|
|
1501
|
+
const param = metadata.params[i].type === 'TSParameterProperty'
|
|
1502
|
+
? metadata.params[i].parameter
|
|
1503
|
+
: metadata.params[i];
|
|
1331
1504
|
|
|
1332
1505
|
if (param.type === 'Identifier') {
|
|
1333
1506
|
// Simple parameter: function(x)
|
|
@@ -1650,6 +1823,34 @@ export class Interpreter {
|
|
|
1650
1823
|
return undefined;
|
|
1651
1824
|
}
|
|
1652
1825
|
|
|
1826
|
+
evaluateTSEnumDeclaration(node, env) {
|
|
1827
|
+
const enumObject = {};
|
|
1828
|
+
let nextNumericValue = 0;
|
|
1829
|
+
|
|
1830
|
+
for (const member of node.members) {
|
|
1831
|
+
const memberName = member.id.name ?? member.id.value;
|
|
1832
|
+
let value;
|
|
1833
|
+
|
|
1834
|
+
if (member.initializer) {
|
|
1835
|
+
value = this.evaluate(member.initializer, env);
|
|
1836
|
+
} else {
|
|
1837
|
+
value = nextNumericValue;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
enumObject[memberName] = value;
|
|
1841
|
+
|
|
1842
|
+
if (typeof value === 'number') {
|
|
1843
|
+
enumObject[value] = memberName;
|
|
1844
|
+
nextNumericValue = value + 1;
|
|
1845
|
+
} else {
|
|
1846
|
+
nextNumericValue = undefined;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
|
|
1850
|
+
env.define(node.id.name, enumObject, false);
|
|
1851
|
+
return undefined;
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1653
1854
|
bindObjectPattern(pattern, value, env, isConst = false) {
|
|
1654
1855
|
if (value === null || value === undefined) {
|
|
1655
1856
|
throw new TypeError('Cannot destructure undefined or null');
|
|
@@ -1754,42 +1955,65 @@ export class Interpreter {
|
|
|
1754
1955
|
// Get module path from import source
|
|
1755
1956
|
const modulePath = node.source.value;
|
|
1756
1957
|
|
|
1958
|
+
if (node.importKind === 'type' ||
|
|
1959
|
+
(node.specifiers.length > 0 && node.specifiers.every(specifier => specifier.importKind === 'type'))) {
|
|
1960
|
+
return undefined;
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1757
1963
|
// Check if module resolver is configured
|
|
1758
1964
|
if (!this.moduleResolver) {
|
|
1759
1965
|
throw new Error('Module resolver not configured - cannot import modules');
|
|
1760
1966
|
}
|
|
1761
1967
|
|
|
1968
|
+
const fromPath = this.currentModulePath;
|
|
1969
|
+
|
|
1970
|
+
const resolutionCacheKey = `${fromPath || ''}\0${modulePath}`;
|
|
1971
|
+
let resolution;
|
|
1972
|
+
let resolvedPath = this.moduleResolutionCache.get(resolutionCacheKey);
|
|
1973
|
+
if (!resolvedPath && !modulePath.startsWith('.') && this.moduleCache.has(modulePath)) {
|
|
1974
|
+
resolvedPath = modulePath;
|
|
1975
|
+
}
|
|
1976
|
+
|
|
1762
1977
|
// Check if module is already cached
|
|
1763
1978
|
let moduleExports;
|
|
1764
|
-
if (this.moduleCache.has(
|
|
1765
|
-
moduleExports = this.moduleCache.get(
|
|
1979
|
+
if (resolvedPath && this.moduleCache.has(resolvedPath)) {
|
|
1980
|
+
moduleExports = this.moduleCache.get(resolvedPath);
|
|
1766
1981
|
} else {
|
|
1767
|
-
// Resolve and
|
|
1768
|
-
|
|
1982
|
+
// Resolve first so relative imports can use importer context and cache by resolved path.
|
|
1983
|
+
resolution = await this.moduleResolver.resolve(modulePath, fromPath);
|
|
1769
1984
|
if (!resolution) {
|
|
1770
1985
|
throw new Error(`Cannot find module '${modulePath}'`);
|
|
1771
1986
|
}
|
|
1772
1987
|
|
|
1988
|
+
resolvedPath = typeof resolution === 'string'
|
|
1989
|
+
? modulePath
|
|
1990
|
+
: resolution.path || modulePath;
|
|
1991
|
+
this.moduleResolutionCache.set(resolutionCacheKey, resolvedPath);
|
|
1992
|
+
if (this.moduleCache.has(resolvedPath)) {
|
|
1993
|
+
moduleExports = this.moduleCache.get(resolvedPath);
|
|
1994
|
+
return this.bindImportSpecifiers(node, env, modulePath, moduleExports);
|
|
1995
|
+
}
|
|
1996
|
+
|
|
1773
1997
|
// Handle native module exports (for libraries like React)
|
|
1774
1998
|
// If resolution has 'exports' property, use it directly without parsing
|
|
1775
1999
|
if (resolution.exports) {
|
|
1776
2000
|
moduleExports = resolution.exports;
|
|
1777
|
-
this.moduleCache.set(
|
|
2001
|
+
this.moduleCache.set(resolvedPath, moduleExports);
|
|
1778
2002
|
} else {
|
|
1779
2003
|
// Handle both old (string) and new (ModuleResolution) formats
|
|
1780
2004
|
const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
|
|
1781
2005
|
|
|
1782
2006
|
// Parse and execute module in its own environment
|
|
1783
|
-
const moduleAst =
|
|
1784
|
-
ecmaVersion: 2020,
|
|
1785
|
-
sourceType: 'module',
|
|
1786
|
-
locations: false
|
|
1787
|
-
});
|
|
2007
|
+
const moduleAst = parseModuleCode(moduleCode, resolvedPath);
|
|
1788
2008
|
const moduleEnv = new Environment(this.globalEnv);
|
|
1789
2009
|
|
|
1790
2010
|
// Create a new interpreter for the module with shared module cache
|
|
1791
2011
|
const moduleInterpreter = new Interpreter(this.globalEnv, {
|
|
1792
|
-
moduleResolver: this.moduleResolver
|
|
2012
|
+
moduleResolver: this.moduleResolver,
|
|
2013
|
+
moduleResolutionCache: this.moduleResolutionCache,
|
|
2014
|
+
currentModulePath: resolvedPath,
|
|
2015
|
+
abortSignal: this.abortSignal,
|
|
2016
|
+
executionController: this.executionController
|
|
1793
2017
|
});
|
|
1794
2018
|
moduleInterpreter.moduleCache = this.moduleCache; // Share cache
|
|
1795
2019
|
|
|
@@ -1798,12 +2022,21 @@ export class Interpreter {
|
|
|
1798
2022
|
|
|
1799
2023
|
// Cache the module exports
|
|
1800
2024
|
moduleExports = moduleInterpreter.moduleExports;
|
|
1801
|
-
this.moduleCache.set(
|
|
2025
|
+
this.moduleCache.set(resolvedPath, moduleExports);
|
|
1802
2026
|
}
|
|
1803
2027
|
}
|
|
1804
2028
|
|
|
2029
|
+
this.bindImportSpecifiers(node, env, modulePath, moduleExports);
|
|
2030
|
+
return undefined;
|
|
2031
|
+
}
|
|
2032
|
+
|
|
2033
|
+
bindImportSpecifiers(node, env, modulePath, moduleExports) {
|
|
1805
2034
|
// Import specified bindings into current environment
|
|
1806
2035
|
for (const specifier of node.specifiers) {
|
|
2036
|
+
if (specifier.importKind === 'type') {
|
|
2037
|
+
continue;
|
|
2038
|
+
}
|
|
2039
|
+
|
|
1807
2040
|
if (specifier.type === 'ImportSpecifier') {
|
|
1808
2041
|
// Named import: import { foo, bar } from "module"
|
|
1809
2042
|
const importedName = specifier.imported.name;
|
|
@@ -1834,6 +2067,10 @@ export class Interpreter {
|
|
|
1834
2067
|
}
|
|
1835
2068
|
|
|
1836
2069
|
evaluateExportNamedDeclaration(node, env) {
|
|
2070
|
+
if (node.exportKind === 'type' || isTypeOnlyDeclaration(node.declaration)) {
|
|
2071
|
+
return undefined;
|
|
2072
|
+
}
|
|
2073
|
+
|
|
1837
2074
|
// Handle export with declaration: export function foo() {} or export const x = 42
|
|
1838
2075
|
if (node.declaration) {
|
|
1839
2076
|
const result = this.evaluate(node.declaration, env);
|
|
@@ -1853,6 +2090,9 @@ export class Interpreter {
|
|
|
1853
2090
|
// export class Foo {}
|
|
1854
2091
|
const name = node.declaration.id.name;
|
|
1855
2092
|
this.moduleExports[name] = env.get(name);
|
|
2093
|
+
} else if (node.declaration.type === 'TSEnumDeclaration') {
|
|
2094
|
+
const name = node.declaration.id.name;
|
|
2095
|
+
this.moduleExports[name] = env.get(name);
|
|
1856
2096
|
}
|
|
1857
2097
|
|
|
1858
2098
|
return result;
|
|
@@ -1861,6 +2101,10 @@ export class Interpreter {
|
|
|
1861
2101
|
// Handle export list: export { foo, bar }
|
|
1862
2102
|
if (node.specifiers && node.specifiers.length > 0) {
|
|
1863
2103
|
for (const specifier of node.specifiers) {
|
|
2104
|
+
if (specifier.exportKind === 'type') {
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
2107
|
+
|
|
1864
2108
|
const exportedName = specifier.exported.name;
|
|
1865
2109
|
const localName = specifier.local.name;
|
|
1866
2110
|
this.moduleExports[exportedName] = env.get(localName);
|
|
@@ -2337,7 +2581,10 @@ export class Interpreter {
|
|
|
2337
2581
|
|
|
2338
2582
|
// Bind parameters
|
|
2339
2583
|
for (let i = 0; i < methodFunc.__params.length; i++) {
|
|
2340
|
-
const
|
|
2584
|
+
const originalParam = methodFunc.__params[i];
|
|
2585
|
+
const param = originalParam.type === 'TSParameterProperty'
|
|
2586
|
+
? originalParam.parameter
|
|
2587
|
+
: originalParam;
|
|
2341
2588
|
|
|
2342
2589
|
if (param.type === 'Identifier') {
|
|
2343
2590
|
// Simple parameter: function(x)
|
|
@@ -2360,6 +2607,13 @@ export class Interpreter {
|
|
|
2360
2607
|
// Fallback for simple parameter names
|
|
2361
2608
|
funcEnv.define(param.name, args[i]);
|
|
2362
2609
|
}
|
|
2610
|
+
|
|
2611
|
+
if (originalParam.type === 'TSParameterProperty') {
|
|
2612
|
+
const propertyName = getPatternName(originalParam.parameter);
|
|
2613
|
+
if (propertyName) {
|
|
2614
|
+
thisContext[propertyName] = funcEnv.get(propertyName);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2363
2617
|
}
|
|
2364
2618
|
|
|
2365
2619
|
const result = this.evaluate(methodFunc.__body, funcEnv);
|