jslike 1.6.1 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +439 -211
- package/dist/esm/index.js +16 -4
- package/dist/esm/interpreter/index.js +2 -1
- package/dist/esm/interpreter/interpreter.js +21 -2
- package/dist/index.cjs +35 -7
- package/dist/index.d.cts +39 -7
- package/dist/index.d.ts +39 -7
- package/dist/index.js +35 -7
- 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
|
|
@@ -102,7 +102,8 @@ export class WangInterpreter {
|
|
|
102
102
|
// Prepare execution options
|
|
103
103
|
const options = {
|
|
104
104
|
moduleResolver: this.moduleResolver,
|
|
105
|
-
executionController: userOptions.executionController
|
|
105
|
+
executionController: userOptions.executionController,
|
|
106
|
+
abortSignal: userOptions.abortSignal // Pass abort signal to interpreter for cancellation support
|
|
106
107
|
// sourceType will be auto-detected from code
|
|
107
108
|
};
|
|
108
109
|
|
|
@@ -158,10 +158,11 @@ export class Interpreter {
|
|
|
158
158
|
return undefined;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
const
|
|
161
|
+
const rawArgs = [];
|
|
162
162
|
for (const arg of node.arguments) {
|
|
163
|
-
|
|
163
|
+
rawArgs.push(await this.evaluateAsync(arg, env));
|
|
164
164
|
}
|
|
165
|
+
const args = this.flattenSpreadArgs(rawArgs);
|
|
165
166
|
|
|
166
167
|
if (typeof callee === 'function') {
|
|
167
168
|
if (thisContext !== undefined) {
|
|
@@ -656,6 +657,24 @@ export class Interpreter {
|
|
|
656
657
|
return this.normalizeJSXText(node.value);
|
|
657
658
|
}
|
|
658
659
|
|
|
660
|
+
// For SpreadElement - return spread marker for flattenSpreadArgs
|
|
661
|
+
if (node.type === 'SpreadElement') {
|
|
662
|
+
const arg = await this.evaluateAsync(node.argument, env);
|
|
663
|
+
if (Array.isArray(arg)) {
|
|
664
|
+
return { __spread: true, __values: arg };
|
|
665
|
+
}
|
|
666
|
+
if (typeof arg === 'string') {
|
|
667
|
+
return { __spread: true, __values: [...arg] };
|
|
668
|
+
}
|
|
669
|
+
if (arg !== null && arg !== undefined && typeof arg[Symbol.iterator] === 'function') {
|
|
670
|
+
return { __spread: true, __values: [...arg] };
|
|
671
|
+
}
|
|
672
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
673
|
+
return { __spread: true, __values: Object.entries(arg) };
|
|
674
|
+
}
|
|
675
|
+
throw new TypeError('Spread syntax requires an iterable');
|
|
676
|
+
}
|
|
677
|
+
|
|
659
678
|
// Only leaf nodes should fall through to sync evaluate
|
|
660
679
|
// These have no sub-expressions that could contain await
|
|
661
680
|
if (['Literal', 'Identifier', 'BreakStatement', 'ContinueStatement',
|
package/dist/index.cjs
CHANGED
|
@@ -6589,10 +6589,11 @@ var Interpreter = class _Interpreter {
|
|
|
6589
6589
|
if (node.optional && (callee === null || callee === void 0)) {
|
|
6590
6590
|
return void 0;
|
|
6591
6591
|
}
|
|
6592
|
-
const
|
|
6592
|
+
const rawArgs = [];
|
|
6593
6593
|
for (const arg of node.arguments) {
|
|
6594
|
-
|
|
6594
|
+
rawArgs.push(await this.evaluateAsync(arg, env));
|
|
6595
6595
|
}
|
|
6596
|
+
const args = this.flattenSpreadArgs(rawArgs);
|
|
6596
6597
|
if (typeof callee === "function") {
|
|
6597
6598
|
if (thisContext !== void 0) {
|
|
6598
6599
|
return await callee.call(thisContext, ...args);
|
|
@@ -6994,6 +6995,22 @@ var Interpreter = class _Interpreter {
|
|
|
6994
6995
|
if (node.type === "JSXText") {
|
|
6995
6996
|
return this.normalizeJSXText(node.value);
|
|
6996
6997
|
}
|
|
6998
|
+
if (node.type === "SpreadElement") {
|
|
6999
|
+
const arg = await this.evaluateAsync(node.argument, env);
|
|
7000
|
+
if (Array.isArray(arg)) {
|
|
7001
|
+
return { __spread: true, __values: arg };
|
|
7002
|
+
}
|
|
7003
|
+
if (typeof arg === "string") {
|
|
7004
|
+
return { __spread: true, __values: [...arg] };
|
|
7005
|
+
}
|
|
7006
|
+
if (arg !== null && arg !== void 0 && typeof arg[Symbol.iterator] === "function") {
|
|
7007
|
+
return { __spread: true, __values: [...arg] };
|
|
7008
|
+
}
|
|
7009
|
+
if (typeof arg === "object" && arg !== null) {
|
|
7010
|
+
return { __spread: true, __values: Object.entries(arg) };
|
|
7011
|
+
}
|
|
7012
|
+
throw new TypeError("Spread syntax requires an iterable");
|
|
7013
|
+
}
|
|
6997
7014
|
if ([
|
|
6998
7015
|
"Literal",
|
|
6999
7016
|
"Identifier",
|
|
@@ -8885,7 +8902,9 @@ var WangInterpreter = class {
|
|
|
8885
8902
|
const hasTopLevelReturn = this.hasTopLevelReturn(code);
|
|
8886
8903
|
const options = {
|
|
8887
8904
|
moduleResolver: this.moduleResolver,
|
|
8888
|
-
executionController: userOptions.executionController
|
|
8905
|
+
executionController: userOptions.executionController,
|
|
8906
|
+
abortSignal: userOptions.abortSignal
|
|
8907
|
+
// Pass abort signal to interpreter for cancellation support
|
|
8889
8908
|
// sourceType will be auto-detected from code
|
|
8890
8909
|
};
|
|
8891
8910
|
let result;
|
|
@@ -9215,6 +9234,7 @@ function containsTopLevelAwait(node) {
|
|
|
9215
9234
|
return false;
|
|
9216
9235
|
}
|
|
9217
9236
|
async function execute(code, env = null, options = {}) {
|
|
9237
|
+
var _a, _b;
|
|
9218
9238
|
const controller = options.executionController;
|
|
9219
9239
|
if (controller) {
|
|
9220
9240
|
controller._start();
|
|
@@ -9231,17 +9251,25 @@ async function execute(code, env = null, options = {}) {
|
|
|
9231
9251
|
});
|
|
9232
9252
|
const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
|
|
9233
9253
|
if (needsAsync) {
|
|
9234
|
-
|
|
9254
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
9235
9255
|
if (controller) {
|
|
9236
9256
|
controller._complete();
|
|
9237
9257
|
}
|
|
9238
|
-
|
|
9258
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9259
|
+
if (result === void 0 && ((_a = interpreter.moduleExports) == null ? void 0 : _a.default) !== void 0) {
|
|
9260
|
+
return interpreter.moduleExports.default;
|
|
9261
|
+
}
|
|
9262
|
+
return result;
|
|
9239
9263
|
} else {
|
|
9240
|
-
|
|
9264
|
+
let result = interpreter.evaluate(ast, env);
|
|
9241
9265
|
if (controller) {
|
|
9242
9266
|
controller._complete();
|
|
9243
9267
|
}
|
|
9244
|
-
|
|
9268
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9269
|
+
if (result === void 0 && ((_b = interpreter.moduleExports) == null ? void 0 : _b.default) !== void 0) {
|
|
9270
|
+
return interpreter.moduleExports.default;
|
|
9271
|
+
}
|
|
9272
|
+
return result;
|
|
9245
9273
|
}
|
|
9246
9274
|
} catch (e) {
|
|
9247
9275
|
if (controller && e.name === "AbortError") {
|
package/dist/index.d.cts
CHANGED
|
@@ -7323,10 +7323,11 @@ class Interpreter {
|
|
|
7323
7323
|
return undefined;
|
|
7324
7324
|
}
|
|
7325
7325
|
|
|
7326
|
-
const
|
|
7326
|
+
const rawArgs = [];
|
|
7327
7327
|
for (const arg of node.arguments) {
|
|
7328
|
-
|
|
7328
|
+
rawArgs.push(await this.evaluateAsync(arg, env));
|
|
7329
7329
|
}
|
|
7330
|
+
const args = this.flattenSpreadArgs(rawArgs);
|
|
7330
7331
|
|
|
7331
7332
|
if (typeof callee === 'function') {
|
|
7332
7333
|
if (thisContext !== undefined) {
|
|
@@ -7821,6 +7822,24 @@ class Interpreter {
|
|
|
7821
7822
|
return this.normalizeJSXText(node.value);
|
|
7822
7823
|
}
|
|
7823
7824
|
|
|
7825
|
+
// For SpreadElement - return spread marker for flattenSpreadArgs
|
|
7826
|
+
if (node.type === 'SpreadElement') {
|
|
7827
|
+
const arg = await this.evaluateAsync(node.argument, env);
|
|
7828
|
+
if (Array.isArray(arg)) {
|
|
7829
|
+
return { __spread: true, __values: arg };
|
|
7830
|
+
}
|
|
7831
|
+
if (typeof arg === 'string') {
|
|
7832
|
+
return { __spread: true, __values: [...arg] };
|
|
7833
|
+
}
|
|
7834
|
+
if (arg !== null && arg !== undefined && typeof arg[Symbol.iterator] === 'function') {
|
|
7835
|
+
return { __spread: true, __values: [...arg] };
|
|
7836
|
+
}
|
|
7837
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
7838
|
+
return { __spread: true, __values: Object.entries(arg) };
|
|
7839
|
+
}
|
|
7840
|
+
throw new TypeError('Spread syntax requires an iterable');
|
|
7841
|
+
}
|
|
7842
|
+
|
|
7824
7843
|
// Only leaf nodes should fall through to sync evaluate
|
|
7825
7844
|
// These have no sub-expressions that could contain await
|
|
7826
7845
|
if (['Literal', 'Identifier', 'BreakStatement', 'ContinueStatement',
|
|
@@ -10328,7 +10347,8 @@ class WangInterpreter {
|
|
|
10328
10347
|
// Prepare execution options
|
|
10329
10348
|
const options = {
|
|
10330
10349
|
moduleResolver: this.moduleResolver,
|
|
10331
|
-
executionController: userOptions.executionController
|
|
10350
|
+
executionController: userOptions.executionController,
|
|
10351
|
+
abortSignal: userOptions.abortSignal // Pass abort signal to interpreter for cancellation support
|
|
10332
10352
|
// sourceType will be auto-detected from code
|
|
10333
10353
|
};
|
|
10334
10354
|
|
|
@@ -10771,17 +10791,29 @@ async function execute(code, env = null, options = {}) {
|
|
|
10771
10791
|
controller != null;
|
|
10772
10792
|
|
|
10773
10793
|
if (needsAsync) {
|
|
10774
|
-
|
|
10794
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
10775
10795
|
if (controller) {
|
|
10776
10796
|
controller._complete();
|
|
10777
10797
|
}
|
|
10778
|
-
|
|
10798
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10799
|
+
|
|
10800
|
+
// If result is undefined and code has module declarations, return default export
|
|
10801
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10802
|
+
return interpreter.moduleExports.default;
|
|
10803
|
+
}
|
|
10804
|
+
return result;
|
|
10779
10805
|
} else {
|
|
10780
|
-
|
|
10806
|
+
let result = interpreter.evaluate(ast, env);
|
|
10781
10807
|
if (controller) {
|
|
10782
10808
|
controller._complete();
|
|
10783
10809
|
}
|
|
10784
|
-
|
|
10810
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10811
|
+
|
|
10812
|
+
// If result is undefined and code has module declarations, return default export
|
|
10813
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10814
|
+
return interpreter.moduleExports.default;
|
|
10815
|
+
}
|
|
10816
|
+
return result;
|
|
10785
10817
|
}
|
|
10786
10818
|
} catch (e) {
|
|
10787
10819
|
// Mark as aborted if that's the error type
|
package/dist/index.d.ts
CHANGED
|
@@ -7323,10 +7323,11 @@ class Interpreter {
|
|
|
7323
7323
|
return undefined;
|
|
7324
7324
|
}
|
|
7325
7325
|
|
|
7326
|
-
const
|
|
7326
|
+
const rawArgs = [];
|
|
7327
7327
|
for (const arg of node.arguments) {
|
|
7328
|
-
|
|
7328
|
+
rawArgs.push(await this.evaluateAsync(arg, env));
|
|
7329
7329
|
}
|
|
7330
|
+
const args = this.flattenSpreadArgs(rawArgs);
|
|
7330
7331
|
|
|
7331
7332
|
if (typeof callee === 'function') {
|
|
7332
7333
|
if (thisContext !== undefined) {
|
|
@@ -7821,6 +7822,24 @@ class Interpreter {
|
|
|
7821
7822
|
return this.normalizeJSXText(node.value);
|
|
7822
7823
|
}
|
|
7823
7824
|
|
|
7825
|
+
// For SpreadElement - return spread marker for flattenSpreadArgs
|
|
7826
|
+
if (node.type === 'SpreadElement') {
|
|
7827
|
+
const arg = await this.evaluateAsync(node.argument, env);
|
|
7828
|
+
if (Array.isArray(arg)) {
|
|
7829
|
+
return { __spread: true, __values: arg };
|
|
7830
|
+
}
|
|
7831
|
+
if (typeof arg === 'string') {
|
|
7832
|
+
return { __spread: true, __values: [...arg] };
|
|
7833
|
+
}
|
|
7834
|
+
if (arg !== null && arg !== undefined && typeof arg[Symbol.iterator] === 'function') {
|
|
7835
|
+
return { __spread: true, __values: [...arg] };
|
|
7836
|
+
}
|
|
7837
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
7838
|
+
return { __spread: true, __values: Object.entries(arg) };
|
|
7839
|
+
}
|
|
7840
|
+
throw new TypeError('Spread syntax requires an iterable');
|
|
7841
|
+
}
|
|
7842
|
+
|
|
7824
7843
|
// Only leaf nodes should fall through to sync evaluate
|
|
7825
7844
|
// These have no sub-expressions that could contain await
|
|
7826
7845
|
if (['Literal', 'Identifier', 'BreakStatement', 'ContinueStatement',
|
|
@@ -10328,7 +10347,8 @@ class WangInterpreter {
|
|
|
10328
10347
|
// Prepare execution options
|
|
10329
10348
|
const options = {
|
|
10330
10349
|
moduleResolver: this.moduleResolver,
|
|
10331
|
-
executionController: userOptions.executionController
|
|
10350
|
+
executionController: userOptions.executionController,
|
|
10351
|
+
abortSignal: userOptions.abortSignal // Pass abort signal to interpreter for cancellation support
|
|
10332
10352
|
// sourceType will be auto-detected from code
|
|
10333
10353
|
};
|
|
10334
10354
|
|
|
@@ -10771,17 +10791,29 @@ async function execute(code, env = null, options = {}) {
|
|
|
10771
10791
|
controller != null;
|
|
10772
10792
|
|
|
10773
10793
|
if (needsAsync) {
|
|
10774
|
-
|
|
10794
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
10775
10795
|
if (controller) {
|
|
10776
10796
|
controller._complete();
|
|
10777
10797
|
}
|
|
10778
|
-
|
|
10798
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10799
|
+
|
|
10800
|
+
// If result is undefined and code has module declarations, return default export
|
|
10801
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10802
|
+
return interpreter.moduleExports.default;
|
|
10803
|
+
}
|
|
10804
|
+
return result;
|
|
10779
10805
|
} else {
|
|
10780
|
-
|
|
10806
|
+
let result = interpreter.evaluate(ast, env);
|
|
10781
10807
|
if (controller) {
|
|
10782
10808
|
controller._complete();
|
|
10783
10809
|
}
|
|
10784
|
-
|
|
10810
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
10811
|
+
|
|
10812
|
+
// If result is undefined and code has module declarations, return default export
|
|
10813
|
+
if (result === undefined && interpreter.moduleExports?.default !== undefined) {
|
|
10814
|
+
return interpreter.moduleExports.default;
|
|
10815
|
+
}
|
|
10816
|
+
return result;
|
|
10785
10817
|
}
|
|
10786
10818
|
} catch (e) {
|
|
10787
10819
|
// Mark as aborted if that's the error type
|
package/dist/index.js
CHANGED
|
@@ -6555,10 +6555,11 @@ var Interpreter = class _Interpreter {
|
|
|
6555
6555
|
if (node.optional && (callee === null || callee === void 0)) {
|
|
6556
6556
|
return void 0;
|
|
6557
6557
|
}
|
|
6558
|
-
const
|
|
6558
|
+
const rawArgs = [];
|
|
6559
6559
|
for (const arg of node.arguments) {
|
|
6560
|
-
|
|
6560
|
+
rawArgs.push(await this.evaluateAsync(arg, env));
|
|
6561
6561
|
}
|
|
6562
|
+
const args = this.flattenSpreadArgs(rawArgs);
|
|
6562
6563
|
if (typeof callee === "function") {
|
|
6563
6564
|
if (thisContext !== void 0) {
|
|
6564
6565
|
return await callee.call(thisContext, ...args);
|
|
@@ -6960,6 +6961,22 @@ var Interpreter = class _Interpreter {
|
|
|
6960
6961
|
if (node.type === "JSXText") {
|
|
6961
6962
|
return this.normalizeJSXText(node.value);
|
|
6962
6963
|
}
|
|
6964
|
+
if (node.type === "SpreadElement") {
|
|
6965
|
+
const arg = await this.evaluateAsync(node.argument, env);
|
|
6966
|
+
if (Array.isArray(arg)) {
|
|
6967
|
+
return { __spread: true, __values: arg };
|
|
6968
|
+
}
|
|
6969
|
+
if (typeof arg === "string") {
|
|
6970
|
+
return { __spread: true, __values: [...arg] };
|
|
6971
|
+
}
|
|
6972
|
+
if (arg !== null && arg !== void 0 && typeof arg[Symbol.iterator] === "function") {
|
|
6973
|
+
return { __spread: true, __values: [...arg] };
|
|
6974
|
+
}
|
|
6975
|
+
if (typeof arg === "object" && arg !== null) {
|
|
6976
|
+
return { __spread: true, __values: Object.entries(arg) };
|
|
6977
|
+
}
|
|
6978
|
+
throw new TypeError("Spread syntax requires an iterable");
|
|
6979
|
+
}
|
|
6963
6980
|
if ([
|
|
6964
6981
|
"Literal",
|
|
6965
6982
|
"Identifier",
|
|
@@ -8851,7 +8868,9 @@ var WangInterpreter = class {
|
|
|
8851
8868
|
const hasTopLevelReturn = this.hasTopLevelReturn(code);
|
|
8852
8869
|
const options = {
|
|
8853
8870
|
moduleResolver: this.moduleResolver,
|
|
8854
|
-
executionController: userOptions.executionController
|
|
8871
|
+
executionController: userOptions.executionController,
|
|
8872
|
+
abortSignal: userOptions.abortSignal
|
|
8873
|
+
// Pass abort signal to interpreter for cancellation support
|
|
8855
8874
|
// sourceType will be auto-detected from code
|
|
8856
8875
|
};
|
|
8857
8876
|
let result;
|
|
@@ -9181,6 +9200,7 @@ function containsTopLevelAwait(node) {
|
|
|
9181
9200
|
return false;
|
|
9182
9201
|
}
|
|
9183
9202
|
async function execute(code, env = null, options = {}) {
|
|
9203
|
+
var _a, _b;
|
|
9184
9204
|
const controller = options.executionController;
|
|
9185
9205
|
if (controller) {
|
|
9186
9206
|
controller._start();
|
|
@@ -9197,17 +9217,25 @@ async function execute(code, env = null, options = {}) {
|
|
|
9197
9217
|
});
|
|
9198
9218
|
const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
|
|
9199
9219
|
if (needsAsync) {
|
|
9200
|
-
|
|
9220
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
9201
9221
|
if (controller) {
|
|
9202
9222
|
controller._complete();
|
|
9203
9223
|
}
|
|
9204
|
-
|
|
9224
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9225
|
+
if (result === void 0 && ((_a = interpreter.moduleExports) == null ? void 0 : _a.default) !== void 0) {
|
|
9226
|
+
return interpreter.moduleExports.default;
|
|
9227
|
+
}
|
|
9228
|
+
return result;
|
|
9205
9229
|
} else {
|
|
9206
|
-
|
|
9230
|
+
let result = interpreter.evaluate(ast, env);
|
|
9207
9231
|
if (controller) {
|
|
9208
9232
|
controller._complete();
|
|
9209
9233
|
}
|
|
9210
|
-
|
|
9234
|
+
result = result instanceof ReturnValue ? result.value : result;
|
|
9235
|
+
if (result === void 0 && ((_b = interpreter.moduleExports) == null ? void 0 : _b.default) !== void 0) {
|
|
9236
|
+
return interpreter.moduleExports.default;
|
|
9237
|
+
}
|
|
9238
|
+
return result;
|
|
9211
9239
|
}
|
|
9212
9240
|
} catch (e) {
|
|
9213
9241
|
if (controller && e.name === "AbortError") {
|