jslike 1.7.3 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -2
- package/dist/esm/index.js +17 -5
- package/dist/esm/interpreter/index.js +2 -1
- package/dist/esm/interpreter/interpreter.js +428 -37
- package/dist/esm/parser.js +5040 -1
- package/dist/index.cjs +5387 -36
- package/dist/index.d.cts +5447 -41
- package/dist/index.d.ts +5447 -41
- package/dist/index.js +5387 -36
- package/dist/validator/index.cjs +5018 -3
- package/dist/validator/index.js +5018 -3
- package/package.json +2 -1
|
@@ -1,13 +1,240 @@
|
|
|
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
|
+
|
|
60
|
+
function collectRuntimeIdentifierReferences(node) {
|
|
61
|
+
const references = new Set();
|
|
62
|
+
const skipKeys = new Set([
|
|
63
|
+
'type',
|
|
64
|
+
'start',
|
|
65
|
+
'end',
|
|
66
|
+
'loc',
|
|
67
|
+
'range',
|
|
68
|
+
'raw',
|
|
69
|
+
'typeAnnotation',
|
|
70
|
+
'returnType',
|
|
71
|
+
'typeParameters',
|
|
72
|
+
'typeArguments',
|
|
73
|
+
'implements'
|
|
74
|
+
]);
|
|
75
|
+
|
|
76
|
+
const visitPatternDefaults = (pattern) => {
|
|
77
|
+
if (!pattern || typeof pattern !== 'object') return;
|
|
78
|
+
if (pattern.type === 'AssignmentPattern') {
|
|
79
|
+
visit(pattern.right);
|
|
80
|
+
visitPatternDefaults(pattern.left);
|
|
81
|
+
} else if (pattern.type === 'ObjectPattern') {
|
|
82
|
+
for (const property of pattern.properties || []) {
|
|
83
|
+
visitPatternDefaults(property.value || property.argument);
|
|
84
|
+
}
|
|
85
|
+
} else if (pattern.type === 'ArrayPattern') {
|
|
86
|
+
for (const element of pattern.elements || []) {
|
|
87
|
+
visitPatternDefaults(element);
|
|
88
|
+
}
|
|
89
|
+
} else if (pattern.type === 'RestElement') {
|
|
90
|
+
visitPatternDefaults(pattern.argument);
|
|
91
|
+
} else if (pattern.type === 'TSParameterProperty') {
|
|
92
|
+
visitPatternDefaults(pattern.parameter);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const visitFunction = (fn) => {
|
|
97
|
+
for (const param of fn.params || []) {
|
|
98
|
+
visitPatternDefaults(param);
|
|
99
|
+
}
|
|
100
|
+
visit(fn.body);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const visitJSXName = (jsxName) => {
|
|
104
|
+
if (!jsxName || typeof jsxName !== 'object') return;
|
|
105
|
+
if (jsxName.type === 'JSXIdentifier') {
|
|
106
|
+
if (/^[A-Z]/.test(jsxName.name)) {
|
|
107
|
+
references.add(jsxName.name);
|
|
108
|
+
}
|
|
109
|
+
} else if (jsxName.type === 'JSXMemberExpression') {
|
|
110
|
+
visitJSXName(jsxName.object);
|
|
111
|
+
} else if (jsxName.type === 'JSXNamespacedName') {
|
|
112
|
+
visitJSXName(jsxName.namespace);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const visit = (current, parent = null, parentKey = null) => {
|
|
117
|
+
if (!current || typeof current !== 'object') return;
|
|
118
|
+
|
|
119
|
+
if (Array.isArray(current)) {
|
|
120
|
+
for (const item of current) visit(item, parent, parentKey);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (current.type?.startsWith('TS')) {
|
|
125
|
+
if (isTypeWrapperExpression(current)) {
|
|
126
|
+
visit(getTypeWrapperInnerExpression(current), current, 'expression');
|
|
127
|
+
} else if (current.type === 'TSEnumDeclaration') {
|
|
128
|
+
for (const member of current.members || []) {
|
|
129
|
+
visit(member.initializer);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
switch (current.type) {
|
|
136
|
+
case 'Identifier':
|
|
137
|
+
references.add(current.name);
|
|
138
|
+
return;
|
|
139
|
+
case 'ImportDeclaration':
|
|
140
|
+
return;
|
|
141
|
+
case 'ExportNamedDeclaration':
|
|
142
|
+
if (current.declaration) {
|
|
143
|
+
visit(current.declaration, current, 'declaration');
|
|
144
|
+
} else if (current.exportKind !== 'type') {
|
|
145
|
+
for (const specifier of current.specifiers || []) {
|
|
146
|
+
if (specifier.exportKind !== 'type' && specifier.local?.name) {
|
|
147
|
+
references.add(specifier.local.name);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
case 'ExportDefaultDeclaration':
|
|
153
|
+
visit(current.declaration, current, 'declaration');
|
|
154
|
+
return;
|
|
155
|
+
case 'VariableDeclarator':
|
|
156
|
+
visitPatternDefaults(current.id);
|
|
157
|
+
visit(current.init, current, 'init');
|
|
158
|
+
return;
|
|
159
|
+
case 'FunctionDeclaration':
|
|
160
|
+
visitFunction(current);
|
|
161
|
+
return;
|
|
162
|
+
case 'FunctionExpression':
|
|
163
|
+
case 'ArrowFunctionExpression':
|
|
164
|
+
visitFunction(current);
|
|
165
|
+
return;
|
|
166
|
+
case 'ClassDeclaration':
|
|
167
|
+
case 'ClassExpression':
|
|
168
|
+
visit(current.superClass, current, 'superClass');
|
|
169
|
+
visit(current.body, current, 'body');
|
|
170
|
+
return;
|
|
171
|
+
case 'MemberExpression':
|
|
172
|
+
case 'OptionalMemberExpression':
|
|
173
|
+
visit(current.object, current, 'object');
|
|
174
|
+
if (current.computed) {
|
|
175
|
+
visit(current.property, current, 'property');
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
case 'Property':
|
|
179
|
+
if (current.computed) {
|
|
180
|
+
visit(current.key, current, 'key');
|
|
181
|
+
}
|
|
182
|
+
visit(current.value, current, 'value');
|
|
183
|
+
return;
|
|
184
|
+
case 'MethodDefinition':
|
|
185
|
+
case 'PropertyDefinition':
|
|
186
|
+
if (current.computed) {
|
|
187
|
+
visit(current.key, current, 'key');
|
|
188
|
+
}
|
|
189
|
+
if (!current.declare && !current.abstract) {
|
|
190
|
+
visit(current.value, current, 'value');
|
|
191
|
+
}
|
|
192
|
+
return;
|
|
193
|
+
case 'AssignmentPattern':
|
|
194
|
+
visit(current.right, current, 'right');
|
|
195
|
+
return;
|
|
196
|
+
case 'RestElement':
|
|
197
|
+
return;
|
|
198
|
+
case 'ObjectPattern':
|
|
199
|
+
case 'ArrayPattern':
|
|
200
|
+
visitPatternDefaults(current);
|
|
201
|
+
return;
|
|
202
|
+
case 'JSXElement':
|
|
203
|
+
visitJSXName(current.openingElement?.name);
|
|
204
|
+
for (const child of current.children || []) {
|
|
205
|
+
visit(child);
|
|
206
|
+
}
|
|
207
|
+
return;
|
|
208
|
+
case 'JSXFragment':
|
|
209
|
+
for (const child of current.children || []) {
|
|
210
|
+
visit(child);
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
case 'JSXExpressionContainer':
|
|
214
|
+
visit(current.expression, current, 'expression');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
for (const [key, value] of Object.entries(current)) {
|
|
219
|
+
if (skipKeys.has(key)) continue;
|
|
220
|
+
visit(value, current, key);
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
visit(node);
|
|
225
|
+
return references;
|
|
226
|
+
}
|
|
227
|
+
|
|
5
228
|
export class Interpreter {
|
|
6
229
|
constructor(globalEnv, options = {}) {
|
|
7
230
|
this.globalEnv = globalEnv;
|
|
8
231
|
this.moduleResolver = options.moduleResolver;
|
|
9
232
|
this.moduleCache = new Map(); // Cache loaded modules
|
|
233
|
+
this.moduleResolutionCache = options.moduleResolutionCache || new Map();
|
|
10
234
|
this.moduleExports = {}; // Track exports in current module
|
|
235
|
+
this.currentModulePath = options.currentModulePath;
|
|
236
|
+
this.isTypeScriptModule = options.isTypeScriptModule || false;
|
|
237
|
+
this.runtimeIdentifierReferences = null;
|
|
11
238
|
this.abortSignal = options.abortSignal;
|
|
12
239
|
this.executionController = options.executionController;
|
|
13
240
|
}
|
|
@@ -163,6 +390,27 @@ export class Interpreter {
|
|
|
163
390
|
const checkpointPromise = this._getCheckpointPromise(node, env);
|
|
164
391
|
if (checkpointPromise) await checkpointPromise;
|
|
165
392
|
|
|
393
|
+
if (isTypeOnlyDeclaration(node)) {
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (node.type === 'TSExportAssignment' || node.type === 'TSImportEqualsDeclaration') {
|
|
398
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (node.type === 'TSEnumDeclaration') {
|
|
402
|
+
return this.evaluateTSEnumDeclaration(node, env);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (node.type === 'TSModuleDeclaration') {
|
|
406
|
+
if (node.declare) return undefined;
|
|
407
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (isTypeWrapperExpression(node)) {
|
|
411
|
+
return await this.evaluateAsync(getTypeWrapperInnerExpression(node), env);
|
|
412
|
+
}
|
|
413
|
+
|
|
166
414
|
// Handle await expressions by actually awaiting the promise
|
|
167
415
|
if (node.type === 'AwaitExpression') {
|
|
168
416
|
const promise = await this.evaluateAsync(node.argument, env);
|
|
@@ -209,15 +457,21 @@ export class Interpreter {
|
|
|
209
457
|
|
|
210
458
|
// For Program nodes (evaluate all statements async)
|
|
211
459
|
if (node.type === 'Program') {
|
|
460
|
+
const previousReferences = this.runtimeIdentifierReferences;
|
|
461
|
+
this.runtimeIdentifierReferences = collectRuntimeIdentifierReferences(node);
|
|
212
462
|
let result = undefined;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
463
|
+
try {
|
|
464
|
+
for (const statement of node.body) {
|
|
465
|
+
result = await this.evaluateAsync(statement, env);
|
|
466
|
+
// Handle top-level return and throw
|
|
467
|
+
if (result instanceof ReturnValue || result instanceof ThrowSignal) {
|
|
468
|
+
return result;
|
|
469
|
+
}
|
|
218
470
|
}
|
|
471
|
+
return result;
|
|
472
|
+
} finally {
|
|
473
|
+
this.runtimeIdentifierReferences = previousReferences;
|
|
219
474
|
}
|
|
220
|
-
return result;
|
|
221
475
|
}
|
|
222
476
|
|
|
223
477
|
// For import declarations (always async)
|
|
@@ -814,6 +1068,27 @@ export class Interpreter {
|
|
|
814
1068
|
// Check for abort signal before evaluating
|
|
815
1069
|
this.checkAbortSignal();
|
|
816
1070
|
|
|
1071
|
+
if (isTypeOnlyDeclaration(node)) {
|
|
1072
|
+
return undefined;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (node.type === 'TSExportAssignment' || node.type === 'TSImportEqualsDeclaration') {
|
|
1076
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if (node.type === 'TSEnumDeclaration') {
|
|
1080
|
+
return this.evaluateTSEnumDeclaration(node, env);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
if (node.type === 'TSModuleDeclaration') {
|
|
1084
|
+
if (node.declare) return undefined;
|
|
1085
|
+
throw createUnsupportedTypeScriptRuntimeError(node);
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
if (isTypeWrapperExpression(node)) {
|
|
1089
|
+
return this.evaluate(getTypeWrapperInnerExpression(node), env);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
817
1092
|
switch (node.type) {
|
|
818
1093
|
case 'Program':
|
|
819
1094
|
return this.evaluateProgram(node, env);
|
|
@@ -974,6 +1249,9 @@ export class Interpreter {
|
|
|
974
1249
|
case 'Property':
|
|
975
1250
|
return this.evaluateProperty(node, env);
|
|
976
1251
|
|
|
1252
|
+
case 'TSEnumDeclaration':
|
|
1253
|
+
return this.evaluateTSEnumDeclaration(node, env);
|
|
1254
|
+
|
|
977
1255
|
// JSX Support
|
|
978
1256
|
case 'JSXElement':
|
|
979
1257
|
return this.evaluateJSXElement(node, env);
|
|
@@ -996,27 +1274,33 @@ export class Interpreter {
|
|
|
996
1274
|
}
|
|
997
1275
|
|
|
998
1276
|
evaluateProgram(node, env) {
|
|
1277
|
+
const previousReferences = this.runtimeIdentifierReferences;
|
|
1278
|
+
this.runtimeIdentifierReferences = collectRuntimeIdentifierReferences(node);
|
|
999
1279
|
let result = undefined;
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1280
|
+
try {
|
|
1281
|
+
for (let i = 0; i < node.body.length; i++) {
|
|
1282
|
+
const statement = node.body[i];
|
|
1283
|
+
const isLast = i === node.body.length - 1;
|
|
1284
|
+
|
|
1285
|
+
// Special case: Last statement is a BlockStatement that looks like object literal
|
|
1286
|
+
// Handle both shorthand { x, y } and full syntax { key: value, key2: value2 }
|
|
1287
|
+
if (isLast && statement.type === 'BlockStatement') {
|
|
1288
|
+
const objLiteral = this.tryConvertBlockToObjectLiteral(statement, env);
|
|
1289
|
+
if (objLiteral !== null) {
|
|
1290
|
+
return objLiteral;
|
|
1291
|
+
}
|
|
1010
1292
|
}
|
|
1011
|
-
}
|
|
1012
1293
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1294
|
+
const statementResult = this.evaluate(statement, env);
|
|
1295
|
+
if (statementResult instanceof ReturnValue || statementResult instanceof ThrowSignal) {
|
|
1296
|
+
return statementResult;
|
|
1297
|
+
}
|
|
1298
|
+
result = statementResult;
|
|
1016
1299
|
}
|
|
1017
|
-
result
|
|
1300
|
+
return result;
|
|
1301
|
+
} finally {
|
|
1302
|
+
this.runtimeIdentifierReferences = previousReferences;
|
|
1018
1303
|
}
|
|
1019
|
-
return result;
|
|
1020
1304
|
}
|
|
1021
1305
|
|
|
1022
1306
|
// Try to convert a BlockStatement to an object literal
|
|
@@ -1396,7 +1680,9 @@ export class Interpreter {
|
|
|
1396
1680
|
|
|
1397
1681
|
// Bind parameters
|
|
1398
1682
|
for (let i = 0; i < metadata.params.length; i++) {
|
|
1399
|
-
const param = metadata.params[i]
|
|
1683
|
+
const param = metadata.params[i].type === 'TSParameterProperty'
|
|
1684
|
+
? metadata.params[i].parameter
|
|
1685
|
+
: metadata.params[i];
|
|
1400
1686
|
|
|
1401
1687
|
if (param.type === 'Identifier') {
|
|
1402
1688
|
// Simple parameter: function(x)
|
|
@@ -1719,6 +2005,34 @@ export class Interpreter {
|
|
|
1719
2005
|
return undefined;
|
|
1720
2006
|
}
|
|
1721
2007
|
|
|
2008
|
+
evaluateTSEnumDeclaration(node, env) {
|
|
2009
|
+
const enumObject = {};
|
|
2010
|
+
let nextNumericValue = 0;
|
|
2011
|
+
|
|
2012
|
+
for (const member of node.members) {
|
|
2013
|
+
const memberName = member.id.name ?? member.id.value;
|
|
2014
|
+
let value;
|
|
2015
|
+
|
|
2016
|
+
if (member.initializer) {
|
|
2017
|
+
value = this.evaluate(member.initializer, env);
|
|
2018
|
+
} else {
|
|
2019
|
+
value = nextNumericValue;
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
enumObject[memberName] = value;
|
|
2023
|
+
|
|
2024
|
+
if (typeof value === 'number') {
|
|
2025
|
+
enumObject[value] = memberName;
|
|
2026
|
+
nextNumericValue = value + 1;
|
|
2027
|
+
} else {
|
|
2028
|
+
nextNumericValue = undefined;
|
|
2029
|
+
}
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
env.define(node.id.name, enumObject, false);
|
|
2033
|
+
return undefined;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
1722
2036
|
bindObjectPattern(pattern, value, env, isConst = false) {
|
|
1723
2037
|
if (value === null || value === undefined) {
|
|
1724
2038
|
throw new TypeError('Cannot destructure undefined or null');
|
|
@@ -1823,42 +2137,72 @@ export class Interpreter {
|
|
|
1823
2137
|
// Get module path from import source
|
|
1824
2138
|
const modulePath = node.source.value;
|
|
1825
2139
|
|
|
2140
|
+
if (node.importKind === 'type' ||
|
|
2141
|
+
(node.specifiers.length > 0 && node.specifiers.every(specifier => specifier.importKind === 'type'))) {
|
|
2142
|
+
return undefined;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
if (this.isTypeScriptModule &&
|
|
2146
|
+
node.specifiers.length > 0 &&
|
|
2147
|
+
node.specifiers.every(specifier => !this.isRuntimeImportSpecifier(specifier))) {
|
|
2148
|
+
return undefined;
|
|
2149
|
+
}
|
|
2150
|
+
|
|
1826
2151
|
// Check if module resolver is configured
|
|
1827
2152
|
if (!this.moduleResolver) {
|
|
1828
2153
|
throw new Error('Module resolver not configured - cannot import modules');
|
|
1829
2154
|
}
|
|
1830
2155
|
|
|
2156
|
+
const fromPath = this.currentModulePath;
|
|
2157
|
+
|
|
2158
|
+
const resolutionCacheKey = `${fromPath || ''}\0${modulePath}`;
|
|
2159
|
+
let resolution;
|
|
2160
|
+
let resolvedPath = this.moduleResolutionCache.get(resolutionCacheKey);
|
|
2161
|
+
if (!resolvedPath && !modulePath.startsWith('.') && this.moduleCache.has(modulePath)) {
|
|
2162
|
+
resolvedPath = modulePath;
|
|
2163
|
+
}
|
|
2164
|
+
|
|
1831
2165
|
// Check if module is already cached
|
|
1832
2166
|
let moduleExports;
|
|
1833
|
-
if (this.moduleCache.has(
|
|
1834
|
-
moduleExports = this.moduleCache.get(
|
|
2167
|
+
if (resolvedPath && this.moduleCache.has(resolvedPath)) {
|
|
2168
|
+
moduleExports = this.moduleCache.get(resolvedPath);
|
|
1835
2169
|
} else {
|
|
1836
|
-
// Resolve and
|
|
1837
|
-
|
|
2170
|
+
// Resolve first so relative imports can use importer context and cache by resolved path.
|
|
2171
|
+
resolution = await this.moduleResolver.resolve(modulePath, fromPath);
|
|
1838
2172
|
if (!resolution) {
|
|
1839
2173
|
throw new Error(`Cannot find module '${modulePath}'`);
|
|
1840
2174
|
}
|
|
1841
2175
|
|
|
2176
|
+
resolvedPath = typeof resolution === 'string'
|
|
2177
|
+
? modulePath
|
|
2178
|
+
: resolution.path || modulePath;
|
|
2179
|
+
this.moduleResolutionCache.set(resolutionCacheKey, resolvedPath);
|
|
2180
|
+
if (this.moduleCache.has(resolvedPath)) {
|
|
2181
|
+
moduleExports = this.moduleCache.get(resolvedPath);
|
|
2182
|
+
return this.bindImportSpecifiers(node, env, modulePath, moduleExports);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
1842
2185
|
// Handle native module exports (for libraries like React)
|
|
1843
2186
|
// If resolution has 'exports' property, use it directly without parsing
|
|
1844
2187
|
if (resolution.exports) {
|
|
1845
2188
|
moduleExports = resolution.exports;
|
|
1846
|
-
this.moduleCache.set(
|
|
2189
|
+
this.moduleCache.set(resolvedPath, moduleExports);
|
|
1847
2190
|
} else {
|
|
1848
2191
|
// Handle both old (string) and new (ModuleResolution) formats
|
|
1849
2192
|
const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
|
|
1850
2193
|
|
|
1851
2194
|
// Parse and execute module in its own environment
|
|
1852
|
-
const moduleAst =
|
|
1853
|
-
ecmaVersion: 2020,
|
|
1854
|
-
sourceType: 'module',
|
|
1855
|
-
locations: false
|
|
1856
|
-
});
|
|
2195
|
+
const moduleAst = parseModuleCode(moduleCode, resolvedPath);
|
|
1857
2196
|
const moduleEnv = new Environment(this.globalEnv);
|
|
1858
2197
|
|
|
1859
2198
|
// Create a new interpreter for the module with shared module cache
|
|
1860
2199
|
const moduleInterpreter = new Interpreter(this.globalEnv, {
|
|
1861
|
-
moduleResolver: this.moduleResolver
|
|
2200
|
+
moduleResolver: this.moduleResolver,
|
|
2201
|
+
moduleResolutionCache: this.moduleResolutionCache,
|
|
2202
|
+
currentModulePath: resolvedPath,
|
|
2203
|
+
isTypeScriptModule: isTypeScriptPath(resolvedPath),
|
|
2204
|
+
abortSignal: this.abortSignal,
|
|
2205
|
+
executionController: this.executionController
|
|
1862
2206
|
});
|
|
1863
2207
|
moduleInterpreter.moduleCache = this.moduleCache; // Share cache
|
|
1864
2208
|
|
|
@@ -1867,12 +2211,21 @@ export class Interpreter {
|
|
|
1867
2211
|
|
|
1868
2212
|
// Cache the module exports
|
|
1869
2213
|
moduleExports = moduleInterpreter.moduleExports;
|
|
1870
|
-
this.moduleCache.set(
|
|
2214
|
+
this.moduleCache.set(resolvedPath, moduleExports);
|
|
1871
2215
|
}
|
|
1872
2216
|
}
|
|
1873
2217
|
|
|
2218
|
+
this.bindImportSpecifiers(node, env, modulePath, moduleExports);
|
|
2219
|
+
return undefined;
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2222
|
+
bindImportSpecifiers(node, env, modulePath, moduleExports) {
|
|
1874
2223
|
// Import specified bindings into current environment
|
|
1875
2224
|
for (const specifier of node.specifiers) {
|
|
2225
|
+
if (!this.isRuntimeImportSpecifier(specifier)) {
|
|
2226
|
+
continue;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
1876
2229
|
if (specifier.type === 'ImportSpecifier') {
|
|
1877
2230
|
// Named import: import { foo, bar } from "module"
|
|
1878
2231
|
const importedName = specifier.imported.name;
|
|
@@ -1902,7 +2255,28 @@ export class Interpreter {
|
|
|
1902
2255
|
return undefined;
|
|
1903
2256
|
}
|
|
1904
2257
|
|
|
2258
|
+
isRuntimeImportSpecifier(specifier) {
|
|
2259
|
+
if (specifier.importKind === 'type') {
|
|
2260
|
+
return false;
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
if (!this.isTypeScriptModule) {
|
|
2264
|
+
return true;
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
const localName = specifier.local?.name;
|
|
2268
|
+
if (!localName || !this.runtimeIdentifierReferences) {
|
|
2269
|
+
return true;
|
|
2270
|
+
}
|
|
2271
|
+
|
|
2272
|
+
return this.runtimeIdentifierReferences.has(localName);
|
|
2273
|
+
}
|
|
2274
|
+
|
|
1905
2275
|
evaluateExportNamedDeclaration(node, env) {
|
|
2276
|
+
if (node.exportKind === 'type' || isTypeOnlyDeclaration(node.declaration)) {
|
|
2277
|
+
return undefined;
|
|
2278
|
+
}
|
|
2279
|
+
|
|
1906
2280
|
// Handle export with declaration: export function foo() {} or export const x = 42
|
|
1907
2281
|
if (node.declaration) {
|
|
1908
2282
|
const result = this.evaluate(node.declaration, env);
|
|
@@ -1922,6 +2296,9 @@ export class Interpreter {
|
|
|
1922
2296
|
// export class Foo {}
|
|
1923
2297
|
const name = node.declaration.id.name;
|
|
1924
2298
|
this.moduleExports[name] = env.get(name);
|
|
2299
|
+
} else if (node.declaration.type === 'TSEnumDeclaration') {
|
|
2300
|
+
const name = node.declaration.id.name;
|
|
2301
|
+
this.moduleExports[name] = env.get(name);
|
|
1925
2302
|
}
|
|
1926
2303
|
|
|
1927
2304
|
return result;
|
|
@@ -1930,6 +2307,10 @@ export class Interpreter {
|
|
|
1930
2307
|
// Handle export list: export { foo, bar }
|
|
1931
2308
|
if (node.specifiers && node.specifiers.length > 0) {
|
|
1932
2309
|
for (const specifier of node.specifiers) {
|
|
2310
|
+
if (specifier.exportKind === 'type') {
|
|
2311
|
+
continue;
|
|
2312
|
+
}
|
|
2313
|
+
|
|
1933
2314
|
const exportedName = specifier.exported.name;
|
|
1934
2315
|
const localName = specifier.local.name;
|
|
1935
2316
|
this.moduleExports[exportedName] = env.get(localName);
|
|
@@ -2406,7 +2787,10 @@ export class Interpreter {
|
|
|
2406
2787
|
|
|
2407
2788
|
// Bind parameters
|
|
2408
2789
|
for (let i = 0; i < methodFunc.__params.length; i++) {
|
|
2409
|
-
const
|
|
2790
|
+
const originalParam = methodFunc.__params[i];
|
|
2791
|
+
const param = originalParam.type === 'TSParameterProperty'
|
|
2792
|
+
? originalParam.parameter
|
|
2793
|
+
: originalParam;
|
|
2410
2794
|
|
|
2411
2795
|
if (param.type === 'Identifier') {
|
|
2412
2796
|
// Simple parameter: function(x)
|
|
@@ -2429,6 +2813,13 @@ export class Interpreter {
|
|
|
2429
2813
|
// Fallback for simple parameter names
|
|
2430
2814
|
funcEnv.define(param.name, args[i]);
|
|
2431
2815
|
}
|
|
2816
|
+
|
|
2817
|
+
if (originalParam.type === 'TSParameterProperty') {
|
|
2818
|
+
const propertyName = getPatternName(originalParam.parameter);
|
|
2819
|
+
if (propertyName) {
|
|
2820
|
+
thisContext[propertyName] = funcEnv.get(propertyName);
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2432
2823
|
}
|
|
2433
2824
|
|
|
2434
2825
|
const result = this.evaluate(methodFunc.__body, funcEnv);
|