jslike 1.6.0 → 1.6.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.
- package/README.md +439 -211
- package/dist/esm/index.js +16 -4
- package/dist/esm/interpreter/interpreter.js +30 -23
- package/dist/index.cjs +32 -18
- package/dist/index.d.cts +43 -24
- package/dist/index.d.ts +43 -24
- package/dist/index.js +32 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,77 +1,246 @@
|
|
|
1
1
|
# JSLike
|
|
2
2
|
|
|
3
|
-
**Production-ready JavaScript interpreter** with full ES6+ support
|
|
3
|
+
**Production-ready JavaScript interpreter** with full ES6+ support, native JSX parsing, and React integration. JSLike executes real JavaScript code with a custom runtime environment, supporting modern ES6+ features including classes, destructuring, template literals, JSX, and more.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
7
|
+
- **Production-Ready** - Handles files of any size, tested with 1000+ tests
|
|
8
8
|
- **Full ES6+ JavaScript Support** - Classes, destructuring, template literals, spread operator, arrow functions
|
|
9
|
+
- **Native JSX Support** - Parse and execute JSX without pre-transformation
|
|
10
|
+
- **React Integration** - Import React hooks and components via moduleResolver
|
|
11
|
+
- **CSP-Safe** - Tree-walking interpreter, no eval() or new Function()
|
|
9
12
|
- **ASI (Automatic Semicolon Insertion)** - Write JavaScript naturally without mandatory semicolons
|
|
10
13
|
- **Acorn Parser** - Battle-tested parser used by webpack, ESLint, and major tools
|
|
11
|
-
- **
|
|
12
|
-
- **
|
|
13
|
-
- **REPL** - Interactive development environment
|
|
14
|
-
- **CLI** - Run `.js` files directly
|
|
14
|
+
- **Zero Runtime Dependencies** - Parser bundled, no npm install needed after build
|
|
15
|
+
- **REPL & CLI** - Interactive development and direct file execution
|
|
15
16
|
|
|
16
17
|
## Installation
|
|
17
18
|
|
|
18
19
|
```bash
|
|
19
|
-
|
|
20
|
+
npm install jslike
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Or for development:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/artpar/jslike.git
|
|
27
|
+
cd jslike
|
|
20
28
|
npm install
|
|
21
29
|
npm run build
|
|
30
|
+
```
|
|
22
31
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
### Programmatic Usage
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
import { execute, createEnvironment } from 'jslike';
|
|
38
|
+
|
|
39
|
+
// Simple execution
|
|
40
|
+
const result = await execute(`
|
|
41
|
+
const greeting = "Hello";
|
|
42
|
+
const name = "World";
|
|
43
|
+
greeting + ", " + name + "!"
|
|
44
|
+
`);
|
|
45
|
+
console.log(result); // "Hello, World!"
|
|
26
46
|
```
|
|
27
47
|
|
|
28
|
-
###
|
|
48
|
+
### JSX Support
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import { execute } from 'jslike';
|
|
29
52
|
|
|
30
|
-
|
|
53
|
+
const element = await execute(`
|
|
54
|
+
function Button({ label, onClick }) {
|
|
55
|
+
return <button className="btn" onClick={onClick}>{label}</button>;
|
|
56
|
+
}
|
|
31
57
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
58
|
+
<div className="container">
|
|
59
|
+
<h1>Welcome</h1>
|
|
60
|
+
<Button label="Click me" onClick={() => console.log('clicked')} />
|
|
61
|
+
</div>
|
|
62
|
+
`);
|
|
63
|
+
|
|
64
|
+
// element is a React-compatible element object:
|
|
65
|
+
// { $$typeof: Symbol(react.element), type: 'div', props: {...}, ... }
|
|
35
66
|
```
|
|
36
67
|
|
|
37
|
-
|
|
68
|
+
### React Integration
|
|
69
|
+
|
|
70
|
+
```javascript
|
|
71
|
+
import { execute, createEnvironment } from 'jslike';
|
|
72
|
+
import * as React from 'react';
|
|
73
|
+
|
|
74
|
+
// Create module resolver for React imports
|
|
75
|
+
const moduleResolver = {
|
|
76
|
+
async resolve(modulePath) {
|
|
77
|
+
if (modulePath === 'react') {
|
|
78
|
+
return { exports: React }; // Return native module exports
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// Create environment with React for JSX
|
|
85
|
+
const env = createEnvironment();
|
|
86
|
+
env.define('React', React);
|
|
87
|
+
|
|
88
|
+
// Execute with React hooks
|
|
89
|
+
const component = await execute(`
|
|
90
|
+
import { useState, useEffect } from 'react';
|
|
91
|
+
|
|
92
|
+
function Counter() {
|
|
93
|
+
const [count, setCount] = useState(0);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
document.title = \`Count: \${count}\`;
|
|
97
|
+
}, [count]);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div>
|
|
101
|
+
<p>Count: {count}</p>
|
|
102
|
+
<button onClick={() => setCount(count + 1)}>+</button>
|
|
103
|
+
</div>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
<Counter />
|
|
108
|
+
`, env, { moduleResolver });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### CLI Usage
|
|
112
|
+
|
|
38
113
|
```bash
|
|
39
|
-
|
|
40
|
-
jslike myfile.js
|
|
114
|
+
# Run a file
|
|
115
|
+
npx jslike myfile.js
|
|
116
|
+
|
|
117
|
+
# Interactive REPL
|
|
118
|
+
npx jslike --repl
|
|
41
119
|
```
|
|
42
120
|
|
|
43
|
-
##
|
|
121
|
+
## Module System
|
|
44
122
|
|
|
45
|
-
|
|
123
|
+
JSLike supports ES6 imports with a flexible module resolver:
|
|
46
124
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
125
|
+
### Native Module Exports (React, lodash, etc.)
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
const moduleResolver = {
|
|
129
|
+
async resolve(modulePath) {
|
|
130
|
+
// Return native JavaScript objects directly
|
|
131
|
+
if (modulePath === 'react') {
|
|
132
|
+
return { exports: React };
|
|
133
|
+
}
|
|
134
|
+
if (modulePath === 'lodash') {
|
|
135
|
+
return { exports: _ };
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
51
140
|
```
|
|
52
141
|
|
|
53
|
-
###
|
|
142
|
+
### Code Modules (parsed and executed)
|
|
54
143
|
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
./
|
|
144
|
+
```javascript
|
|
145
|
+
const moduleResolver = {
|
|
146
|
+
async resolve(modulePath) {
|
|
147
|
+
if (modulePath === './utils') {
|
|
148
|
+
return {
|
|
149
|
+
code: `
|
|
150
|
+
export function double(x) { return x * 2; }
|
|
151
|
+
export const PI = 3.14159;
|
|
152
|
+
`
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
59
158
|
```
|
|
60
159
|
|
|
61
|
-
###
|
|
160
|
+
### Import Styles Supported
|
|
62
161
|
|
|
162
|
+
```javascript
|
|
163
|
+
import { useState, useEffect } from 'react'; // Named imports
|
|
164
|
+
import React from 'react'; // Default import
|
|
165
|
+
import * as Utils from './utils'; // Namespace import
|
|
63
166
|
```
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
167
|
+
|
|
168
|
+
## JSX Features
|
|
169
|
+
|
|
170
|
+
### Basic Elements
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
<div className="container">Hello World</div>
|
|
174
|
+
<input type="text" disabled />
|
|
175
|
+
<br />
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Expressions
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
const name = "World";
|
|
182
|
+
<div>Hello {name}</div>
|
|
183
|
+
<div>{1 + 2 + 3}</div>
|
|
184
|
+
<div>{items.map(item => <span key={item.id}>{item.name}</span>)}</div>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Attributes
|
|
188
|
+
|
|
189
|
+
```javascript
|
|
190
|
+
// String attributes
|
|
191
|
+
<div className="container" id="main">
|
|
192
|
+
|
|
193
|
+
// Expression attributes
|
|
194
|
+
<div className={isActive ? 'active' : 'inactive'}>
|
|
195
|
+
|
|
196
|
+
// Spread attributes
|
|
197
|
+
const props = { className: 'btn', disabled: true };
|
|
198
|
+
<button {...props}>Click</button>
|
|
199
|
+
|
|
200
|
+
// Boolean attributes
|
|
201
|
+
<input disabled /> // Same as disabled={true}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Fragments
|
|
205
|
+
|
|
206
|
+
```javascript
|
|
207
|
+
<>
|
|
208
|
+
<div>First</div>
|
|
209
|
+
<div>Second</div>
|
|
210
|
+
</>
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Components
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
// Function components
|
|
217
|
+
function Card({ title, children }) {
|
|
218
|
+
return (
|
|
219
|
+
<div className="card">
|
|
220
|
+
<h2>{title}</h2>
|
|
221
|
+
<div className="card-body">{children}</div>
|
|
222
|
+
</div>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Usage - components are stored as type (React behavior)
|
|
227
|
+
<Card title="Welcome">
|
|
228
|
+
<p>Card content here</p>
|
|
229
|
+
</Card>
|
|
230
|
+
|
|
231
|
+
// To render, call component manually or use React renderer
|
|
232
|
+
Card({ title: "Welcome", children: <p>Content</p> })
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Member Expression Components
|
|
236
|
+
|
|
237
|
+
```javascript
|
|
238
|
+
const UI = {
|
|
239
|
+
Button: ({ children }) => <button className="ui-btn">{children}</button>,
|
|
240
|
+
Card: ({ children }) => <div className="ui-card">{children}</div>
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
<UI.Button>Click me</UI.Button>
|
|
75
244
|
```
|
|
76
245
|
|
|
77
246
|
## Language Features
|
|
@@ -94,30 +263,80 @@ function add(a, b) {
|
|
|
94
263
|
|
|
95
264
|
// Arrow functions
|
|
96
265
|
const multiply = (x, y) => x * y;
|
|
97
|
-
const greet =
|
|
98
|
-
|
|
99
|
-
|
|
266
|
+
const greet = name => `Hello, ${name}`;
|
|
267
|
+
|
|
268
|
+
// Default parameters
|
|
269
|
+
function greet(name = "World") {
|
|
270
|
+
return `Hello, ${name}`;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Rest parameters
|
|
274
|
+
function sum(...numbers) {
|
|
275
|
+
return numbers.reduce((a, b) => a + b, 0);
|
|
276
|
+
}
|
|
100
277
|
```
|
|
101
278
|
|
|
102
|
-
###
|
|
279
|
+
### Classes
|
|
103
280
|
|
|
104
281
|
```javascript
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
282
|
+
class Animal {
|
|
283
|
+
constructor(name) {
|
|
284
|
+
this.name = name;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
speak() {
|
|
288
|
+
return `${this.name} makes a sound`;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
class Dog extends Animal {
|
|
293
|
+
speak() {
|
|
294
|
+
return `${this.name} barks`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const dog = new Dog("Rex");
|
|
299
|
+
dog.speak(); // "Rex barks"
|
|
108
300
|
```
|
|
109
301
|
|
|
110
|
-
###
|
|
302
|
+
### Destructuring
|
|
111
303
|
|
|
112
304
|
```javascript
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
305
|
+
// Object destructuring
|
|
306
|
+
const { name, age } = person;
|
|
307
|
+
const { name: userName, age: userAge } = person;
|
|
308
|
+
|
|
309
|
+
// Array destructuring
|
|
310
|
+
const [first, second, ...rest] = array;
|
|
311
|
+
|
|
312
|
+
// Parameter destructuring
|
|
313
|
+
function greet({ name, age }) {
|
|
314
|
+
return `${name} is ${age}`;
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Template Literals
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
const name = "World";
|
|
322
|
+
const greeting = `Hello, ${name}!`;
|
|
323
|
+
const multiline = `
|
|
324
|
+
Line 1
|
|
325
|
+
Line 2
|
|
326
|
+
`;
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Async/Await
|
|
330
|
+
|
|
331
|
+
```javascript
|
|
332
|
+
async function fetchData() {
|
|
333
|
+
const response = await fetch(url);
|
|
334
|
+
const data = await response.json();
|
|
335
|
+
return data;
|
|
336
|
+
}
|
|
118
337
|
|
|
119
|
-
|
|
120
|
-
|
|
338
|
+
// Top-level await supported
|
|
339
|
+
const result = await fetchData();
|
|
121
340
|
```
|
|
122
341
|
|
|
123
342
|
### Control Flow
|
|
@@ -126,20 +345,26 @@ console.log(person["age"]); // 30
|
|
|
126
345
|
// If-else
|
|
127
346
|
if (x > 10) {
|
|
128
347
|
console.log("Greater");
|
|
348
|
+
} else if (x === 10) {
|
|
349
|
+
console.log("Equal");
|
|
129
350
|
} else {
|
|
130
|
-
console.log("
|
|
351
|
+
console.log("Less");
|
|
131
352
|
}
|
|
132
353
|
|
|
133
354
|
// Ternary
|
|
134
355
|
const result = x > 5 ? "yes" : "no";
|
|
135
356
|
|
|
357
|
+
// Nullish coalescing
|
|
358
|
+
const value = input ?? "default";
|
|
359
|
+
|
|
360
|
+
// Optional chaining
|
|
361
|
+
const name = user?.profile?.name;
|
|
362
|
+
|
|
136
363
|
// Switch
|
|
137
364
|
switch (day) {
|
|
138
|
-
case 1:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
default:
|
|
142
|
-
console.log("Other day");
|
|
365
|
+
case 1: return "Monday";
|
|
366
|
+
case 2: return "Tuesday";
|
|
367
|
+
default: return "Other";
|
|
143
368
|
}
|
|
144
369
|
```
|
|
145
370
|
|
|
@@ -147,53 +372,30 @@ switch (day) {
|
|
|
147
372
|
|
|
148
373
|
```javascript
|
|
149
374
|
// For loop
|
|
150
|
-
for (let i = 0; i < 10; i
|
|
375
|
+
for (let i = 0; i < 10; i++) {
|
|
151
376
|
console.log(i);
|
|
152
377
|
}
|
|
153
378
|
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
379
|
+
// For-of
|
|
380
|
+
for (const item of array) {
|
|
381
|
+
console.log(item);
|
|
157
382
|
}
|
|
158
383
|
|
|
159
|
-
// Do-while
|
|
160
|
-
do {
|
|
161
|
-
// code
|
|
162
|
-
} while (condition);
|
|
163
|
-
|
|
164
384
|
// For-in
|
|
165
|
-
for (
|
|
166
|
-
console.log(key);
|
|
385
|
+
for (const key in object) {
|
|
386
|
+
console.log(key, object[key]);
|
|
167
387
|
}
|
|
168
388
|
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
console.log(value);
|
|
172
|
-
}
|
|
173
|
-
```
|
|
389
|
+
// While
|
|
390
|
+
while (condition) { /* ... */ }
|
|
174
391
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
```javascript
|
|
178
|
-
try {
|
|
179
|
-
throw "Error message";
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.log("Caught:", error);
|
|
182
|
-
} finally {
|
|
183
|
-
console.log("Cleanup");
|
|
184
|
-
}
|
|
392
|
+
// Do-while
|
|
393
|
+
do { /* ... */ } while (condition);
|
|
185
394
|
```
|
|
186
395
|
|
|
187
|
-
|
|
396
|
+
## Built-in Objects & Functions
|
|
188
397
|
|
|
189
|
-
|
|
190
|
-
- **Comparison**: `<`, `>`, `<=`, `>=`, `==`, `!=`, `===`, `!==`
|
|
191
|
-
- **Logical**: `&&`, `||`, `!`, `??` (nullish coalescing)
|
|
192
|
-
- **Bitwise**: `&`, `|`, `^`, `~`, `<<`, `>>`, `>>>`
|
|
193
|
-
- **Assignment**: `=`, `+=`, `-=`, `*=`, `/=`, `%=`
|
|
194
|
-
- **Update**: `++`, `--`
|
|
195
|
-
|
|
196
|
-
### Built-in Objects
|
|
398
|
+
### Standard JavaScript
|
|
197
399
|
|
|
198
400
|
- `console.log()`, `console.error()`, `console.warn()`
|
|
199
401
|
- `Math.PI`, `Math.sqrt()`, `Math.random()`, etc.
|
|
@@ -201,148 +403,174 @@ try {
|
|
|
201
403
|
- `Map`, `Set`, `WeakMap`, `WeakSet`
|
|
202
404
|
- `RegExp`, `Symbol`
|
|
203
405
|
- `JSON.parse()`, `JSON.stringify()`
|
|
406
|
+
- `Promise`, `Date`, `Error`
|
|
204
407
|
- `setTimeout()`, `setInterval()`
|
|
205
408
|
- `parseInt()`, `parseFloat()`, `isNaN()`, `isFinite()`
|
|
206
|
-
- `Promise`, `Date`, `Error`
|
|
207
409
|
|
|
208
|
-
|
|
410
|
+
### JSX Runtime
|
|
411
|
+
|
|
412
|
+
- `createElement(type, props, ...children)` - Creates React-compatible elements
|
|
413
|
+
- `Fragment` - Symbol for React fragments
|
|
209
414
|
|
|
210
|
-
|
|
415
|
+
### Wang Standard Library
|
|
211
416
|
|
|
212
|
-
|
|
213
|
-
- `functions.js` - Function examples including closures
|
|
214
|
-
- `loops.js` - All loop types
|
|
215
|
-
- `arrays-objects.js` - Arrays and objects
|
|
216
|
-
- `algorithms.js` - Factorial, Fibonacci, prime numbers
|
|
217
|
-
- `control-flow.js` - If-else, switch, try-catch
|
|
218
|
-
- `comprehensive.js` - Complete feature demonstration
|
|
417
|
+
Utility functions available globally:
|
|
219
418
|
|
|
220
|
-
|
|
419
|
+
```javascript
|
|
420
|
+
// Array operations
|
|
421
|
+
sort_by(array, key) // Sort by key or function
|
|
422
|
+
group_by(array, key) // Group into object by key
|
|
423
|
+
unique(array) // Remove duplicates
|
|
424
|
+
chunk(array, size) // Split into chunks
|
|
425
|
+
flatten(array, depth) // Flatten nested arrays
|
|
426
|
+
first(array, n) // Get first n items
|
|
427
|
+
last(array, n) // Get last n items
|
|
428
|
+
range(start, end, step) // Generate number sequence
|
|
429
|
+
|
|
430
|
+
// Object operations
|
|
431
|
+
keys(obj) // Object.keys
|
|
432
|
+
values(obj) // Object.values
|
|
433
|
+
entries(obj) // Object.entries
|
|
434
|
+
pick(obj, keys) // Pick specific keys
|
|
435
|
+
omit(obj, keys) // Omit specific keys
|
|
436
|
+
merge(...objects) // Merge objects
|
|
437
|
+
get(obj, path, default) // Deep get with dot notation
|
|
438
|
+
clone(obj) // Deep clone
|
|
439
|
+
|
|
440
|
+
// String operations
|
|
441
|
+
split(str, sep) // Split string
|
|
442
|
+
join(arr, sep) // Join array
|
|
443
|
+
trim(str) // Trim whitespace
|
|
444
|
+
upper(str) // Uppercase
|
|
445
|
+
lower(str) // Lowercase
|
|
446
|
+
capitalize(str) // Capitalize first letter
|
|
447
|
+
truncate(str, len) // Truncate with ellipsis
|
|
448
|
+
|
|
449
|
+
// Type checking
|
|
450
|
+
is_string(val) // Check if string
|
|
451
|
+
is_number(val) // Check if number
|
|
452
|
+
is_array(val) // Check if array
|
|
453
|
+
is_object(val) // Check if object
|
|
454
|
+
is_function(val) // Check if function
|
|
455
|
+
is_empty(val) // Check if empty
|
|
456
|
+
|
|
457
|
+
// Math operations
|
|
458
|
+
sum(array) // Sum of numbers
|
|
459
|
+
avg(array) // Average
|
|
460
|
+
min(array) // Minimum
|
|
461
|
+
max(array) // Maximum
|
|
462
|
+
clamp(num, min, max) // Clamp to range
|
|
463
|
+
round(num, decimals) // Round to decimals
|
|
464
|
+
```
|
|
221
465
|
|
|
222
|
-
|
|
466
|
+
## API Reference
|
|
223
467
|
|
|
224
|
-
|
|
468
|
+
### execute(code, env?, options?)
|
|
225
469
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
470
|
+
Execute JavaScript code and return the result.
|
|
471
|
+
|
|
472
|
+
```javascript
|
|
473
|
+
const result = await execute(code, env, {
|
|
474
|
+
moduleResolver, // For import statements
|
|
475
|
+
executionController, // For pause/resume/abort
|
|
476
|
+
abortSignal // For cancellation
|
|
477
|
+
});
|
|
229
478
|
```
|
|
230
479
|
|
|
231
|
-
|
|
232
|
-
- Complete Acorn parser
|
|
233
|
-
- No external dependencies at runtime
|
|
234
|
-
- ES6 module format
|
|
480
|
+
### createEnvironment()
|
|
235
481
|
|
|
236
|
-
|
|
482
|
+
Create a new execution environment with built-ins.
|
|
237
483
|
|
|
238
|
-
```
|
|
239
|
-
|
|
484
|
+
```javascript
|
|
485
|
+
const env = createEnvironment();
|
|
486
|
+
env.define('myVar', 42);
|
|
487
|
+
env.define('myFunc', (x) => x * 2);
|
|
240
488
|
```
|
|
241
489
|
|
|
242
|
-
###
|
|
490
|
+
### ModuleResolver
|
|
243
491
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
492
|
+
Interface for resolving imports:
|
|
493
|
+
|
|
494
|
+
```javascript
|
|
495
|
+
const moduleResolver = {
|
|
496
|
+
async resolve(modulePath, fromPath) {
|
|
497
|
+
// Return { exports: object } for native modules
|
|
498
|
+
// Return { code: string } for code modules
|
|
499
|
+
// Return null if not found
|
|
500
|
+
},
|
|
501
|
+
async exists(modulePath, fromPath) {
|
|
502
|
+
// Return boolean
|
|
503
|
+
},
|
|
504
|
+
async list(prefix) {
|
|
505
|
+
// Return string[] of module paths
|
|
506
|
+
}
|
|
507
|
+
};
|
|
255
508
|
```
|
|
256
509
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
1. **Parsing**: Acorn parser processes JavaScript code and builds an ESTree-compatible AST
|
|
260
|
-
2. **Evaluation**: The interpreter walks the AST and executes each node
|
|
261
|
-
3. **Runtime**: Environment manages variable scopes (lexical scoping with closures)
|
|
262
|
-
4. **Built-ins**: Native JavaScript objects (console, Math, JSON, etc.) integrated into runtime
|
|
263
|
-
|
|
264
|
-
## Current Status
|
|
265
|
-
|
|
266
|
-
### ✅ Production-Ready & Fully Implemented
|
|
267
|
-
|
|
268
|
-
**ES6+ Language Features**:
|
|
269
|
-
- ✅ Variables (var, let, const)
|
|
270
|
-
- ✅ Functions (declarations, expressions, arrow functions)
|
|
271
|
-
- ✅ **Classes** with constructors, methods, and inheritance
|
|
272
|
-
- ✅ **Destructuring** (objects and arrays)
|
|
273
|
-
- ✅ **Template literals** with interpolation
|
|
274
|
-
- ✅ **Spread operator** for arrays and objects
|
|
275
|
-
- ✅ Rest parameters `(...rest)`
|
|
276
|
-
- ✅ Object/array shorthand syntax
|
|
277
|
-
- ✅ Method shorthand in objects
|
|
278
|
-
- ✅ **ASI (Automatic Semicolon Insertion)** - semicolons optional!
|
|
279
|
-
- ✅ All operators (arithmetic, logical, bitwise, comparison)
|
|
280
|
-
- ✅ Control flow (if/else, switch, try/catch)
|
|
281
|
-
- ✅ Loops (for, while, do-while, for-in, for-of)
|
|
282
|
-
- ✅ Closures and higher-order functions
|
|
283
|
-
- ✅ Proper `this` binding in classes and methods
|
|
284
|
-
- ✅ Comments (single-line //, multi-line /* */)
|
|
285
|
-
- ✅ **Async/await** with Promises
|
|
286
|
-
- ✅ **Regular expressions** (literals and RegExp constructor)
|
|
287
|
-
- ✅ Enhanced error messages with suggestions
|
|
288
|
-
|
|
289
|
-
**Infrastructure**:
|
|
290
|
-
- ✅ Acorn parser - production-grade ES2020 support
|
|
291
|
-
- ✅ Tree-walking interpreter (~2000 LOC)
|
|
292
|
-
- ✅ Runtime environment with proper lexical scoping
|
|
293
|
-
- ✅ Built-in objects (console, Math, JSON, Array, Object, etc.)
|
|
294
|
-
- ✅ CLI for running .js files
|
|
295
|
-
- ✅ Interactive REPL
|
|
296
|
-
- ✅ Handles files of **any size** (tested on 100+ line programs)
|
|
297
|
-
- ✅ **No file size limitations** - O(n) parsing performance
|
|
298
|
-
|
|
299
|
-
### Performance
|
|
300
|
-
|
|
301
|
-
- ✅ **Fast parsing**: O(n) linear time complexity
|
|
302
|
-
- ✅ **Handles large files**: No OOM errors, no timeouts
|
|
303
|
-
- ✅ **Production-tested parser**: Acorn is used by webpack, ESLint, Rollup
|
|
304
|
-
- ✅ Tested on complex programs with 100+ lines
|
|
305
|
-
|
|
306
|
-
### Not Yet Implemented
|
|
307
|
-
|
|
308
|
-
- Generators (function* and yield)
|
|
309
|
-
- Proxies and Reflect API
|
|
310
|
-
- Module system (import/export between files)
|
|
510
|
+
### ExecutionController
|
|
311
511
|
|
|
312
|
-
|
|
512
|
+
Control execution flow:
|
|
313
513
|
|
|
314
|
-
|
|
514
|
+
```javascript
|
|
515
|
+
import { execute, ExecutionController } from 'jslike';
|
|
516
|
+
|
|
517
|
+
const controller = new ExecutionController();
|
|
518
|
+
|
|
519
|
+
// Start execution
|
|
520
|
+
const promise = execute(code, null, { executionController: controller });
|
|
521
|
+
|
|
522
|
+
// Control execution
|
|
523
|
+
controller.pause();
|
|
524
|
+
controller.resume();
|
|
525
|
+
controller.abort();
|
|
526
|
+
|
|
527
|
+
// Check state
|
|
528
|
+
console.log(controller.state); // 'running' | 'paused' | 'completed' | 'aborted'
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
## Testing
|
|
315
532
|
|
|
316
533
|
```bash
|
|
317
|
-
# Run
|
|
318
|
-
npm test
|
|
534
|
+
npm test # Run all tests
|
|
535
|
+
npm test -- tests/jsx.test.js # Run specific test file
|
|
536
|
+
```
|
|
319
537
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
538
|
+
**1009 tests** covering:
|
|
539
|
+
- ES6+ language features
|
|
540
|
+
- JSX parsing and execution
|
|
541
|
+
- React integration
|
|
542
|
+
- Module imports
|
|
543
|
+
- Error handling
|
|
544
|
+
- Edge cases
|
|
323
545
|
|
|
324
|
-
|
|
325
|
-
node bin/jslike.js examples/es6-test.js
|
|
326
|
-
node bin/jslike.js examples/class-simple.js
|
|
546
|
+
## Architecture
|
|
327
547
|
|
|
328
|
-
# Test complex algorithms (100+ lines)
|
|
329
|
-
node bin/jslike.js examples/algorithms.js
|
|
330
548
|
```
|
|
549
|
+
src/
|
|
550
|
+
├── parser.js - Bundled Acorn + acorn-jsx (~245KB)
|
|
551
|
+
├── index.js - Main API (parse/execute)
|
|
552
|
+
├── interpreter/
|
|
553
|
+
│ └── interpreter.js - Tree-walking interpreter (~2500 LOC)
|
|
554
|
+
├── runtime/
|
|
555
|
+
│ ├── environment.js - Lexical scoping and closures
|
|
556
|
+
│ ├── builtins.js - Built-in objects and JSX runtime
|
|
557
|
+
│ └── execution-controller.js - Pause/resume/abort
|
|
558
|
+
└── ast/
|
|
559
|
+
└── nodes.js - AST node types
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## Known Limitations
|
|
331
563
|
|
|
332
|
-
|
|
333
|
-
-
|
|
334
|
-
-
|
|
335
|
-
-
|
|
336
|
-
- Destructuring (objects, arrays, parameters)
|
|
337
|
-
- Control flow and loops
|
|
338
|
-
- Closures and higher-order functions
|
|
339
|
-
- Classes, inheritance, and constructors
|
|
340
|
-
- Template literals
|
|
341
|
-
- Async/await and Promises
|
|
342
|
-
- Regular expressions
|
|
343
|
-
- Native method calls
|
|
344
|
-
- Error handling and edge cases
|
|
564
|
+
- Generator functions (`function*`, `yield`) not supported
|
|
565
|
+
- Tagged template literals not fully supported
|
|
566
|
+
- Class getters/setters not fully supported
|
|
567
|
+
- Proxies and Reflect API not implemented
|
|
345
568
|
|
|
346
569
|
## License
|
|
347
570
|
|
|
348
571
|
MIT
|
|
572
|
+
|
|
573
|
+
## Links
|
|
574
|
+
|
|
575
|
+
- [npm package](https://www.npmjs.com/package/jslike)
|
|
576
|
+
- [GitHub repository](https://github.com/artpar/jslike)
|
package/dist/esm/index.js
CHANGED
|
@@ -133,17 +133,29 @@ export async function execute(code, env = null, options = {}) {
|
|
|
133
133
|
controller != null;
|
|
134
134
|
|
|
135
135
|
if (needsAsync) {
|
|
136
|
-
|
|
136
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
137
137
|
if (controller) {
|
|
138
138
|
controller._complete();
|
|
139
139
|
}
|
|
140
|
-
|
|
140
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
141
|
+
|
|
142
|
+
// If result is undefined and code has module declarations, return default export
|
|
143
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
144
|
+
return interpreter.moduleExports.default;
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
141
147
|
} else {
|
|
142
|
-
|
|
148
|
+
let result = interpreter.evaluate(ast, env);
|
|
143
149
|
if (controller) {
|
|
144
150
|
controller._complete();
|
|
145
151
|
}
|
|
146
|
-
|
|
152
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
153
|
+
|
|
154
|
+
// If result is undefined and code has module declarations, return default export
|
|
155
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
156
|
+
return interpreter.moduleExports.default;
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
147
159
|
}
|
|
148
160
|
} catch (e) {
|
|
149
161
|
// Mark as aborted if that's the error type
|
|
@@ -1671,29 +1671,36 @@ export class Interpreter {
|
|
|
1671
1671
|
throw new Error(`Cannot find module '${modulePath}'`);
|
|
1672
1672
|
}
|
|
1673
1673
|
|
|
1674
|
-
// Handle
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1674
|
+
// Handle native module exports (for libraries like React)
|
|
1675
|
+
// If resolution has 'exports' property, use it directly without parsing
|
|
1676
|
+
if (resolution.exports) {
|
|
1677
|
+
moduleExports = resolution.exports;
|
|
1678
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
1679
|
+
} else {
|
|
1680
|
+
// Handle both old (string) and new (ModuleResolution) formats
|
|
1681
|
+
const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
|
|
1682
|
+
|
|
1683
|
+
// Parse and execute module in its own environment
|
|
1684
|
+
const moduleAst = acornParse(moduleCode, {
|
|
1685
|
+
ecmaVersion: 2020,
|
|
1686
|
+
sourceType: 'module',
|
|
1687
|
+
locations: false
|
|
1688
|
+
});
|
|
1689
|
+
const moduleEnv = new Environment(this.globalEnv);
|
|
1690
|
+
|
|
1691
|
+
// Create a new interpreter for the module with shared module cache
|
|
1692
|
+
const moduleInterpreter = new Interpreter(this.globalEnv, {
|
|
1693
|
+
moduleResolver: this.moduleResolver
|
|
1694
|
+
});
|
|
1695
|
+
moduleInterpreter.moduleCache = this.moduleCache; // Share cache
|
|
1696
|
+
|
|
1697
|
+
// Execute module and collect exports
|
|
1698
|
+
await moduleInterpreter.evaluateAsync(moduleAst, moduleEnv);
|
|
1699
|
+
|
|
1700
|
+
// Cache the module exports
|
|
1701
|
+
moduleExports = moduleInterpreter.moduleExports;
|
|
1702
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
1703
|
+
}
|
|
1697
1704
|
}
|
|
1698
1705
|
|
|
1699
1706
|
// Import specified bindings into current environment
|
package/dist/index.cjs
CHANGED
|
@@ -7765,20 +7765,25 @@ var Interpreter = class _Interpreter {
|
|
|
7765
7765
|
if (!resolution) {
|
|
7766
7766
|
throw new Error(`Cannot find module '${modulePath}'`);
|
|
7767
7767
|
}
|
|
7768
|
-
|
|
7769
|
-
|
|
7770
|
-
|
|
7771
|
-
|
|
7772
|
-
|
|
7773
|
-
|
|
7774
|
-
|
|
7775
|
-
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7779
|
-
|
|
7780
|
-
|
|
7781
|
-
|
|
7768
|
+
if (resolution.exports) {
|
|
7769
|
+
moduleExports = resolution.exports;
|
|
7770
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
7771
|
+
} else {
|
|
7772
|
+
const moduleCode = typeof resolution === "string" ? resolution : resolution.code;
|
|
7773
|
+
const moduleAst = jsxParse(moduleCode, {
|
|
7774
|
+
ecmaVersion: 2020,
|
|
7775
|
+
sourceType: "module",
|
|
7776
|
+
locations: false
|
|
7777
|
+
});
|
|
7778
|
+
const moduleEnv = new Environment(this.globalEnv);
|
|
7779
|
+
const moduleInterpreter = new _Interpreter(this.globalEnv, {
|
|
7780
|
+
moduleResolver: this.moduleResolver
|
|
7781
|
+
});
|
|
7782
|
+
moduleInterpreter.moduleCache = this.moduleCache;
|
|
7783
|
+
await moduleInterpreter.evaluateAsync(moduleAst, moduleEnv);
|
|
7784
|
+
moduleExports = moduleInterpreter.moduleExports;
|
|
7785
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
7786
|
+
}
|
|
7782
7787
|
}
|
|
7783
7788
|
for (const specifier of node.specifiers) {
|
|
7784
7789
|
if (specifier.type === "ImportSpecifier") {
|
|
@@ -9210,6 +9215,7 @@ function containsTopLevelAwait(node) {
|
|
|
9210
9215
|
return false;
|
|
9211
9216
|
}
|
|
9212
9217
|
async function execute(code, env = null, options = {}) {
|
|
9218
|
+
var _a, _b;
|
|
9213
9219
|
const controller = options.executionController;
|
|
9214
9220
|
if (controller) {
|
|
9215
9221
|
controller._start();
|
|
@@ -9226,17 +9232,25 @@ async function execute(code, env = null, options = {}) {
|
|
|
9226
9232
|
});
|
|
9227
9233
|
const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
|
|
9228
9234
|
if (needsAsync) {
|
|
9229
|
-
|
|
9235
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
9230
9236
|
if (controller) {
|
|
9231
9237
|
controller._complete();
|
|
9232
9238
|
}
|
|
9233
|
-
|
|
9239
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9240
|
+
if (result === void 0 && ((_a = interpreter.moduleExports) == null ? void 0 : _a.default) !== void 0) {
|
|
9241
|
+
return interpreter.moduleExports.default;
|
|
9242
|
+
}
|
|
9243
|
+
return result;
|
|
9234
9244
|
} else {
|
|
9235
|
-
|
|
9245
|
+
let result = interpreter.evaluate(ast, env);
|
|
9236
9246
|
if (controller) {
|
|
9237
9247
|
controller._complete();
|
|
9238
9248
|
}
|
|
9239
|
-
|
|
9249
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9250
|
+
if (result === void 0 && ((_b = interpreter.moduleExports) == null ? void 0 : _b.default) !== void 0) {
|
|
9251
|
+
return interpreter.moduleExports.default;
|
|
9252
|
+
}
|
|
9253
|
+
return result;
|
|
9240
9254
|
}
|
|
9241
9255
|
} catch (e) {
|
|
9242
9256
|
if (controller && e.name === "AbortError") {
|
package/dist/index.d.cts
CHANGED
|
@@ -8836,29 +8836,36 @@ class Interpreter {
|
|
|
8836
8836
|
throw new Error(`Cannot find module '${modulePath}'`);
|
|
8837
8837
|
}
|
|
8838
8838
|
|
|
8839
|
-
// Handle
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8839
|
+
// Handle native module exports (for libraries like React)
|
|
8840
|
+
// If resolution has 'exports' property, use it directly without parsing
|
|
8841
|
+
if (resolution.exports) {
|
|
8842
|
+
moduleExports = resolution.exports;
|
|
8843
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
8844
|
+
} else {
|
|
8845
|
+
// Handle both old (string) and new (ModuleResolution) formats
|
|
8846
|
+
const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
|
|
8847
|
+
|
|
8848
|
+
// Parse and execute module in its own environment
|
|
8849
|
+
const moduleAst = jsxParse(moduleCode, {
|
|
8850
|
+
ecmaVersion: 2020,
|
|
8851
|
+
sourceType: 'module',
|
|
8852
|
+
locations: false
|
|
8853
|
+
});
|
|
8854
|
+
const moduleEnv = new Environment(this.globalEnv);
|
|
8849
8855
|
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8856
|
+
// Create a new interpreter for the module with shared module cache
|
|
8857
|
+
const moduleInterpreter = new Interpreter(this.globalEnv, {
|
|
8858
|
+
moduleResolver: this.moduleResolver
|
|
8859
|
+
});
|
|
8860
|
+
moduleInterpreter.moduleCache = this.moduleCache; // Share cache
|
|
8855
8861
|
|
|
8856
|
-
|
|
8857
|
-
|
|
8862
|
+
// Execute module and collect exports
|
|
8863
|
+
await moduleInterpreter.evaluateAsync(moduleAst, moduleEnv);
|
|
8858
8864
|
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8865
|
+
// Cache the module exports
|
|
8866
|
+
moduleExports = moduleInterpreter.moduleExports;
|
|
8867
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
8868
|
+
}
|
|
8862
8869
|
}
|
|
8863
8870
|
|
|
8864
8871
|
// Import specified bindings into current environment
|
|
@@ -10764,17 +10771,29 @@ async function execute(code, env = null, options = {}) {
|
|
|
10764
10771
|
controller != null;
|
|
10765
10772
|
|
|
10766
10773
|
if (needsAsync) {
|
|
10767
|
-
|
|
10774
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
10768
10775
|
if (controller) {
|
|
10769
10776
|
controller._complete();
|
|
10770
10777
|
}
|
|
10771
|
-
|
|
10778
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10779
|
+
|
|
10780
|
+
// If result is undefined and code has module declarations, return default export
|
|
10781
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10782
|
+
return interpreter.moduleExports.default;
|
|
10783
|
+
}
|
|
10784
|
+
return result;
|
|
10772
10785
|
} else {
|
|
10773
|
-
|
|
10786
|
+
let result = interpreter.evaluate(ast, env);
|
|
10774
10787
|
if (controller) {
|
|
10775
10788
|
controller._complete();
|
|
10776
10789
|
}
|
|
10777
|
-
|
|
10790
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10791
|
+
|
|
10792
|
+
// If result is undefined and code has module declarations, return default export
|
|
10793
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10794
|
+
return interpreter.moduleExports.default;
|
|
10795
|
+
}
|
|
10796
|
+
return result;
|
|
10778
10797
|
}
|
|
10779
10798
|
} catch (e) {
|
|
10780
10799
|
// Mark as aborted if that's the error type
|
package/dist/index.d.ts
CHANGED
|
@@ -8836,29 +8836,36 @@ class Interpreter {
|
|
|
8836
8836
|
throw new Error(`Cannot find module '${modulePath}'`);
|
|
8837
8837
|
}
|
|
8838
8838
|
|
|
8839
|
-
// Handle
|
|
8840
|
-
|
|
8841
|
-
|
|
8842
|
-
|
|
8843
|
-
|
|
8844
|
-
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
|
|
8848
|
-
|
|
8839
|
+
// Handle native module exports (for libraries like React)
|
|
8840
|
+
// If resolution has 'exports' property, use it directly without parsing
|
|
8841
|
+
if (resolution.exports) {
|
|
8842
|
+
moduleExports = resolution.exports;
|
|
8843
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
8844
|
+
} else {
|
|
8845
|
+
// Handle both old (string) and new (ModuleResolution) formats
|
|
8846
|
+
const moduleCode = typeof resolution === 'string' ? resolution : resolution.code;
|
|
8847
|
+
|
|
8848
|
+
// Parse and execute module in its own environment
|
|
8849
|
+
const moduleAst = jsxParse(moduleCode, {
|
|
8850
|
+
ecmaVersion: 2020,
|
|
8851
|
+
sourceType: 'module',
|
|
8852
|
+
locations: false
|
|
8853
|
+
});
|
|
8854
|
+
const moduleEnv = new Environment(this.globalEnv);
|
|
8849
8855
|
|
|
8850
|
-
|
|
8851
|
-
|
|
8852
|
-
|
|
8853
|
-
|
|
8854
|
-
|
|
8856
|
+
// Create a new interpreter for the module with shared module cache
|
|
8857
|
+
const moduleInterpreter = new Interpreter(this.globalEnv, {
|
|
8858
|
+
moduleResolver: this.moduleResolver
|
|
8859
|
+
});
|
|
8860
|
+
moduleInterpreter.moduleCache = this.moduleCache; // Share cache
|
|
8855
8861
|
|
|
8856
|
-
|
|
8857
|
-
|
|
8862
|
+
// Execute module and collect exports
|
|
8863
|
+
await moduleInterpreter.evaluateAsync(moduleAst, moduleEnv);
|
|
8858
8864
|
|
|
8859
|
-
|
|
8860
|
-
|
|
8861
|
-
|
|
8865
|
+
// Cache the module exports
|
|
8866
|
+
moduleExports = moduleInterpreter.moduleExports;
|
|
8867
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
8868
|
+
}
|
|
8862
8869
|
}
|
|
8863
8870
|
|
|
8864
8871
|
// Import specified bindings into current environment
|
|
@@ -10764,17 +10771,29 @@ async function execute(code, env = null, options = {}) {
|
|
|
10764
10771
|
controller != null;
|
|
10765
10772
|
|
|
10766
10773
|
if (needsAsync) {
|
|
10767
|
-
|
|
10774
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
10768
10775
|
if (controller) {
|
|
10769
10776
|
controller._complete();
|
|
10770
10777
|
}
|
|
10771
|
-
|
|
10778
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10779
|
+
|
|
10780
|
+
// If result is undefined and code has module declarations, return default export
|
|
10781
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10782
|
+
return interpreter.moduleExports.default;
|
|
10783
|
+
}
|
|
10784
|
+
return result;
|
|
10772
10785
|
} else {
|
|
10773
|
-
|
|
10786
|
+
let result = interpreter.evaluate(ast, env);
|
|
10774
10787
|
if (controller) {
|
|
10775
10788
|
controller._complete();
|
|
10776
10789
|
}
|
|
10777
|
-
|
|
10790
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10791
|
+
|
|
10792
|
+
// If result is undefined and code has module declarations, return default export
|
|
10793
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10794
|
+
return interpreter.moduleExports.default;
|
|
10795
|
+
}
|
|
10796
|
+
return result;
|
|
10778
10797
|
}
|
|
10779
10798
|
} catch (e) {
|
|
10780
10799
|
// Mark as aborted if that's the error type
|
package/dist/index.js
CHANGED
|
@@ -7731,20 +7731,25 @@ var Interpreter = class _Interpreter {
|
|
|
7731
7731
|
if (!resolution) {
|
|
7732
7732
|
throw new Error(`Cannot find module '${modulePath}'`);
|
|
7733
7733
|
}
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7742
|
-
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7734
|
+
if (resolution.exports) {
|
|
7735
|
+
moduleExports = resolution.exports;
|
|
7736
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
7737
|
+
} else {
|
|
7738
|
+
const moduleCode = typeof resolution === "string" ? resolution : resolution.code;
|
|
7739
|
+
const moduleAst = jsxParse(moduleCode, {
|
|
7740
|
+
ecmaVersion: 2020,
|
|
7741
|
+
sourceType: "module",
|
|
7742
|
+
locations: false
|
|
7743
|
+
});
|
|
7744
|
+
const moduleEnv = new Environment(this.globalEnv);
|
|
7745
|
+
const moduleInterpreter = new _Interpreter(this.globalEnv, {
|
|
7746
|
+
moduleResolver: this.moduleResolver
|
|
7747
|
+
});
|
|
7748
|
+
moduleInterpreter.moduleCache = this.moduleCache;
|
|
7749
|
+
await moduleInterpreter.evaluateAsync(moduleAst, moduleEnv);
|
|
7750
|
+
moduleExports = moduleInterpreter.moduleExports;
|
|
7751
|
+
this.moduleCache.set(modulePath, moduleExports);
|
|
7752
|
+
}
|
|
7748
7753
|
}
|
|
7749
7754
|
for (const specifier of node.specifiers) {
|
|
7750
7755
|
if (specifier.type === "ImportSpecifier") {
|
|
@@ -9176,6 +9181,7 @@ function containsTopLevelAwait(node) {
|
|
|
9176
9181
|
return false;
|
|
9177
9182
|
}
|
|
9178
9183
|
async function execute(code, env = null, options = {}) {
|
|
9184
|
+
var _a, _b;
|
|
9179
9185
|
const controller = options.executionController;
|
|
9180
9186
|
if (controller) {
|
|
9181
9187
|
controller._start();
|
|
@@ -9192,17 +9198,25 @@ async function execute(code, env = null, options = {}) {
|
|
|
9192
9198
|
});
|
|
9193
9199
|
const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
|
|
9194
9200
|
if (needsAsync) {
|
|
9195
|
-
|
|
9201
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
9196
9202
|
if (controller) {
|
|
9197
9203
|
controller._complete();
|
|
9198
9204
|
}
|
|
9199
|
-
|
|
9205
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9206
|
+
if (result === void 0 && ((_a = interpreter.moduleExports) == null ? void 0 : _a.default) !== void 0) {
|
|
9207
|
+
return interpreter.moduleExports.default;
|
|
9208
|
+
}
|
|
9209
|
+
return result;
|
|
9200
9210
|
} else {
|
|
9201
|
-
|
|
9211
|
+
let result = interpreter.evaluate(ast, env);
|
|
9202
9212
|
if (controller) {
|
|
9203
9213
|
controller._complete();
|
|
9204
9214
|
}
|
|
9205
|
-
|
|
9215
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9216
|
+
if (result === void 0 && ((_b = interpreter.moduleExports) == null ? void 0 : _b.default) !== void 0) {
|
|
9217
|
+
return interpreter.moduleExports.default;
|
|
9218
|
+
}
|
|
9219
|
+
return result;
|
|
9206
9220
|
}
|
|
9207
9221
|
} catch (e) {
|
|
9208
9222
|
if (controller && e.name === "AbortError") {
|