jslike 1.7.3 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -2
- package/dist/esm/index.js +17 -5
- package/dist/esm/interpreter/index.js +2 -1
- package/dist/esm/interpreter/interpreter.js +428 -37
- package/dist/esm/parser.js +5040 -1
- package/dist/index.cjs +5387 -36
- package/dist/index.d.cts +5447 -41
- package/dist/index.d.ts +5447 -41
- package/dist/index.js +5387 -36
- package/dist/validator/index.cjs +5018 -3
- package/dist/validator/index.js +5018 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
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
9
|
- **Native JSX Support** - Parse and execute JSX without pre-transformation
|
|
10
|
+
- **TypeScript & TSX Module Support** - Execute JS-compatible TypeScript syntax directly from `.ts`, `.tsx`, `.mts`, and `.cts` modules
|
|
10
11
|
- **React Integration** - Import React hooks and components via moduleResolver
|
|
11
12
|
- **CSP-Safe** - Tree-walking interpreter, no eval() or new Function()
|
|
12
13
|
- **ASI (Automatic Semicolon Insertion)** - Write JavaScript naturally without mandatory semicolons
|
|
@@ -143,9 +144,10 @@ const moduleResolver = {
|
|
|
143
144
|
|
|
144
145
|
```javascript
|
|
145
146
|
const moduleResolver = {
|
|
146
|
-
async resolve(modulePath) {
|
|
147
|
+
async resolve(modulePath, fromPath) {
|
|
147
148
|
if (modulePath === './utils') {
|
|
148
149
|
return {
|
|
150
|
+
path: '/virtual/project/utils.js',
|
|
149
151
|
code: `
|
|
150
152
|
export function double(x) { return x * 2; }
|
|
151
153
|
export const PI = 3.14159;
|
|
@@ -157,6 +159,40 @@ const moduleResolver = {
|
|
|
157
159
|
};
|
|
158
160
|
```
|
|
159
161
|
|
|
162
|
+
`fromPath` is the path of the importing module. For top-level code, pass `sourcePath` to `execute()` so relative imports have a deterministic root. For nested imports, JSLike passes the resolved module `path` returned by the resolver.
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
const files = {
|
|
166
|
+
'/virtual/project/main.ts': `
|
|
167
|
+
import { user } from './user.ts';
|
|
168
|
+
user.name
|
|
169
|
+
`,
|
|
170
|
+
'/virtual/project/user.ts': `
|
|
171
|
+
type User = { name: string };
|
|
172
|
+
export const user: User = { name: 'Ada' };
|
|
173
|
+
`
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const moduleResolver = {
|
|
177
|
+
async resolve(modulePath, fromPath) {
|
|
178
|
+
const base = new URL('.', `file://${fromPath}`).pathname;
|
|
179
|
+
const resolvedPath = new URL(modulePath, `file://${base}`).pathname;
|
|
180
|
+
|
|
181
|
+
if (!files[resolvedPath]) return null;
|
|
182
|
+
return {
|
|
183
|
+
path: resolvedPath,
|
|
184
|
+
code: files[resolvedPath]
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const result = await execute(files['/virtual/project/main.ts'], null, {
|
|
190
|
+
moduleResolver,
|
|
191
|
+
sourcePath: '/virtual/project/main.ts'
|
|
192
|
+
});
|
|
193
|
+
// "Ada"
|
|
194
|
+
```
|
|
195
|
+
|
|
160
196
|
### Import Styles Supported
|
|
161
197
|
|
|
162
198
|
```javascript
|
|
@@ -165,6 +201,44 @@ import React from 'react'; // Default import
|
|
|
165
201
|
import * as Utils from './utils'; // Namespace import
|
|
166
202
|
```
|
|
167
203
|
|
|
204
|
+
## TypeScript Support
|
|
205
|
+
|
|
206
|
+
JSLike parses TypeScript and TSX with a bundled `@sveltejs/acorn-typescript` parser. TypeScript syntax is enabled automatically for `sourcePath` and resolved module paths ending in `.ts`, `.tsx`, `.mts`, or `.cts`. You can also force it with `typescript: true` or `tsx: true`.
|
|
207
|
+
|
|
208
|
+
```javascript
|
|
209
|
+
const result = await execute(`
|
|
210
|
+
interface User {
|
|
211
|
+
name: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
enum Role {
|
|
215
|
+
Admin,
|
|
216
|
+
Member
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
class Account {
|
|
220
|
+
constructor(public user: User, readonly role: Role) {}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const account = new Account({ name: 'Ada' }, Role.Admin);
|
|
224
|
+
account.user.name + ':' + account.role
|
|
225
|
+
`, null, {
|
|
226
|
+
sourcePath: '/virtual/account.ts'
|
|
227
|
+
});
|
|
228
|
+
// "Ada:0"
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Supported TypeScript runtime behavior:
|
|
232
|
+
|
|
233
|
+
- Type-only declarations and annotations are erased: `type`, `interface`, `declare`, parameter/return annotations, tuple/readonly annotations.
|
|
234
|
+
- Type-only imports and exports do not trigger runtime module resolution.
|
|
235
|
+
- Type wrappers evaluate their inner expression: `as`, `<T>value`, `satisfies`, non-null `!`, generic call instantiation.
|
|
236
|
+
- Enums execute as runtime enum objects, including numeric reverse mappings and string enum members.
|
|
237
|
+
- Constructor parameter properties assign to `this`, including `public`, `private`, and `readonly`.
|
|
238
|
+
- TSX parses and executes JSX in `.tsx` files or with `tsx: true`.
|
|
239
|
+
|
|
240
|
+
Unsupported runtime TypeScript constructs throw explicit errors instead of executing incorrectly. This currently includes `namespace`, `export =`, and `import x = require(...)`.
|
|
241
|
+
|
|
168
242
|
## JSX Features
|
|
169
243
|
|
|
170
244
|
### Basic Elements
|
|
@@ -473,7 +547,10 @@ Execute JavaScript code and return the result.
|
|
|
473
547
|
const result = await execute(code, env, {
|
|
474
548
|
moduleResolver, // For import statements
|
|
475
549
|
executionController, // For pause/resume/abort
|
|
476
|
-
abortSignal
|
|
550
|
+
abortSignal, // For cancellation
|
|
551
|
+
sourcePath, // Optional importer path for resolving top-level imports
|
|
552
|
+
typescript, // Parse TypeScript syntax
|
|
553
|
+
tsx // Parse TypeScript + JSX syntax
|
|
477
554
|
});
|
|
478
555
|
```
|
|
479
556
|
|
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Use bundled Acorn parser for zero runtime dependencies
|
|
2
|
-
import { parse as acornParse } from './parser.js';
|
|
2
|
+
import { parse as acornParse, tsParse, tsxParse } from './parser.js';
|
|
3
3
|
import { Interpreter } from './interpreter/interpreter.js';
|
|
4
4
|
import { Environment, ReturnValue } from './runtime/environment.js';
|
|
5
5
|
import { createGlobalEnvironment } from './runtime/builtins.js';
|
|
@@ -9,7 +9,7 @@ function containsModuleSyntax(code) {
|
|
|
9
9
|
// Trigger module mode for:
|
|
10
10
|
// 1. import/export statements
|
|
11
11
|
// 2. Top-level await (await not inside a function)
|
|
12
|
-
if (
|
|
12
|
+
if (/(^|[;{\n])\s*(import|export)\s+/m.test(code)) {
|
|
13
13
|
return true;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -23,6 +23,13 @@ function containsModuleSyntax(code) {
|
|
|
23
23
|
return false;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function isTypeScriptPath(sourcePath) {
|
|
27
|
+
return typeof sourcePath === 'string' && /\.(ts|tsx|mts|cts)$/i.test(sourcePath);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function isTSXPath(sourcePath) {
|
|
31
|
+
return typeof sourcePath === 'string' && /\.tsx$/i.test(sourcePath);
|
|
32
|
+
}
|
|
26
33
|
|
|
27
34
|
export function parse(code, options = {}) {
|
|
28
35
|
// Determine sourceType: use 'module' ONLY if explicitly requested or if code has imports/exports
|
|
@@ -32,10 +39,13 @@ export function parse(code, options = {}) {
|
|
|
32
39
|
sourceType = 'module';
|
|
33
40
|
}
|
|
34
41
|
|
|
42
|
+
const shouldParseTypeScript = options.typescript || options.tsx || isTypeScriptPath(options.sourcePath);
|
|
43
|
+
const parser = options.tsx || isTSXPath(options.sourcePath) ? tsxParse : shouldParseTypeScript ? tsParse : acornParse;
|
|
44
|
+
|
|
35
45
|
// Parse with Acorn
|
|
36
46
|
try {
|
|
37
|
-
return
|
|
38
|
-
ecmaVersion: 2022, // Support ES2022 features (including top-level await)
|
|
47
|
+
return parser(code, {
|
|
48
|
+
ecmaVersion: shouldParseTypeScript ? 'latest' : 2022, // Support ES2022 features (including top-level await)
|
|
39
49
|
sourceType: sourceType,
|
|
40
50
|
locations: true, // Track source locations for better error messages
|
|
41
51
|
allowReturnOutsideFunction: true, // Allow top-level return statements
|
|
@@ -119,7 +129,9 @@ export async function execute(code, env = null, options = {}) {
|
|
|
119
129
|
const interpreter = new Interpreter(env, {
|
|
120
130
|
moduleResolver: options.moduleResolver,
|
|
121
131
|
abortSignal: options.abortSignal,
|
|
122
|
-
executionController: controller
|
|
132
|
+
executionController: controller,
|
|
133
|
+
currentModulePath: options.sourcePath,
|
|
134
|
+
isTypeScriptModule: options.typescript || options.tsx || isTypeScriptPath(options.sourcePath)
|
|
123
135
|
});
|
|
124
136
|
|
|
125
137
|
// Use async evaluation if:
|
|
@@ -103,7 +103,8 @@ export class WangInterpreter {
|
|
|
103
103
|
const options = {
|
|
104
104
|
moduleResolver: this.moduleResolver,
|
|
105
105
|
executionController: userOptions.executionController,
|
|
106
|
-
abortSignal: userOptions.abortSignal // Pass abort signal to interpreter for cancellation support
|
|
106
|
+
abortSignal: userOptions.abortSignal, // Pass abort signal to interpreter for cancellation support
|
|
107
|
+
sourcePath: userOptions.sourcePath ?? this.options.sourcePath
|
|
107
108
|
// sourceType will be auto-detected from code
|
|
108
109
|
};
|
|
109
110
|
|