jslike 1.6.1 → 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/index.cjs +13 -4
- package/dist/index.d.cts +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.js +13 -4
- 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
|
package/dist/index.cjs
CHANGED
|
@@ -9215,6 +9215,7 @@ function containsTopLevelAwait(node) {
|
|
|
9215
9215
|
return false;
|
|
9216
9216
|
}
|
|
9217
9217
|
async function execute(code, env = null, options = {}) {
|
|
9218
|
+
var _a, _b;
|
|
9218
9219
|
const controller = options.executionController;
|
|
9219
9220
|
if (controller) {
|
|
9220
9221
|
controller._start();
|
|
@@ -9231,17 +9232,25 @@ async function execute(code, env = null, options = {}) {
|
|
|
9231
9232
|
});
|
|
9232
9233
|
const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
|
|
9233
9234
|
if (needsAsync) {
|
|
9234
|
-
|
|
9235
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
9235
9236
|
if (controller) {
|
|
9236
9237
|
controller._complete();
|
|
9237
9238
|
}
|
|
9238
|
-
|
|
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;
|
|
9239
9244
|
} else {
|
|
9240
|
-
|
|
9245
|
+
let result = interpreter.evaluate(ast, env);
|
|
9241
9246
|
if (controller) {
|
|
9242
9247
|
controller._complete();
|
|
9243
9248
|
}
|
|
9244
|
-
|
|
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;
|
|
9245
9254
|
}
|
|
9246
9255
|
} catch (e) {
|
|
9247
9256
|
if (controller && e.name === "AbortError") {
|
package/dist/index.d.cts
CHANGED
|
@@ -10771,17 +10771,29 @@ async function execute(code, env = null, options = {}) {
|
|
|
10771
10771
|
controller != null;
|
|
10772
10772
|
|
|
10773
10773
|
if (needsAsync) {
|
|
10774
|
-
|
|
10774
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
10775
10775
|
if (controller) {
|
|
10776
10776
|
controller._complete();
|
|
10777
10777
|
}
|
|
10778
|
-
|
|
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;
|
|
10779
10785
|
} else {
|
|
10780
|
-
|
|
10786
|
+
let result = interpreter.evaluate(ast, env);
|
|
10781
10787
|
if (controller) {
|
|
10782
10788
|
controller._complete();
|
|
10783
10789
|
}
|
|
10784
|
-
|
|
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;
|
|
10785
10797
|
}
|
|
10786
10798
|
} catch (e) {
|
|
10787
10799
|
// Mark as aborted if that's the error type
|
package/dist/index.d.ts
CHANGED
|
@@ -10771,17 +10771,29 @@ async function execute(code, env = null, options = {}) {
|
|
|
10771
10771
|
controller != null;
|
|
10772
10772
|
|
|
10773
10773
|
if (needsAsync) {
|
|
10774
|
-
|
|
10774
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
10775
10775
|
if (controller) {
|
|
10776
10776
|
controller._complete();
|
|
10777
10777
|
}
|
|
10778
|
-
|
|
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;
|
|
10779
10785
|
} else {
|
|
10780
|
-
|
|
10786
|
+
let result = interpreter.evaluate(ast, env);
|
|
10781
10787
|
if (controller) {
|
|
10782
10788
|
controller._complete();
|
|
10783
10789
|
}
|
|
10784
|
-
|
|
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;
|
|
10785
10797
|
}
|
|
10786
10798
|
} catch (e) {
|
|
10787
10799
|
// Mark as aborted if that's the error type
|
package/dist/index.js
CHANGED
|
@@ -9181,6 +9181,7 @@ function containsTopLevelAwait(node) {
|
|
|
9181
9181
|
return false;
|
|
9182
9182
|
}
|
|
9183
9183
|
async function execute(code, env = null, options = {}) {
|
|
9184
|
+
var _a, _b;
|
|
9184
9185
|
const controller = options.executionController;
|
|
9185
9186
|
if (controller) {
|
|
9186
9187
|
controller._start();
|
|
@@ -9197,17 +9198,25 @@ async function execute(code, env = null, options = {}) {
|
|
|
9197
9198
|
});
|
|
9198
9199
|
const needsAsync = options.sourceType === "module" || containsModuleDeclarations(ast) || containsTopLevelAwait(ast) || controller != null;
|
|
9199
9200
|
if (needsAsync) {
|
|
9200
|
-
|
|
9201
|
+
let result = await interpreter.evaluateAsync(ast, env);
|
|
9201
9202
|
if (controller) {
|
|
9202
9203
|
controller._complete();
|
|
9203
9204
|
}
|
|
9204
|
-
|
|
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;
|
|
9205
9210
|
} else {
|
|
9206
|
-
|
|
9211
|
+
let result = interpreter.evaluate(ast, env);
|
|
9207
9212
|
if (controller) {
|
|
9208
9213
|
controller._complete();
|
|
9209
9214
|
}
|
|
9210
|
-
|
|
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;
|
|
9211
9220
|
}
|
|
9212
9221
|
} catch (e) {
|
|
9213
9222
|
if (controller && e.name === "AbortError") {
|