agentlang 0.7.0 → 0.7.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/out/api/http.d.ts.map +1 -1
- package/out/api/http.js +46 -26
- package/out/api/http.js.map +1 -1
- package/out/cli/main.d.ts +1 -0
- package/out/cli/main.d.ts.map +1 -1
- package/out/cli/main.js +15 -2
- package/out/cli/main.js.map +1 -1
- package/out/language/generated/ast.d.ts +15 -6
- package/out/language/generated/ast.d.ts.map +1 -1
- package/out/language/generated/ast.js +16 -2
- package/out/language/generated/ast.js.map +1 -1
- package/out/language/generated/grammar.d.ts.map +1 -1
- package/out/language/generated/grammar.js +215 -164
- package/out/language/generated/grammar.js.map +1 -1
- package/out/language/main.cjs +227 -166
- package/out/language/main.cjs.map +2 -2
- package/out/runtime/defs.d.ts +16 -1
- package/out/runtime/defs.d.ts.map +1 -1
- package/out/runtime/defs.js +29 -1
- package/out/runtime/defs.js.map +1 -1
- package/out/runtime/exec-graph.d.ts.map +1 -1
- package/out/runtime/exec-graph.js +8 -0
- package/out/runtime/exec-graph.js.map +1 -1
- package/out/runtime/interpreter.d.ts +2 -1
- package/out/runtime/interpreter.d.ts.map +1 -1
- package/out/runtime/interpreter.js +10 -0
- package/out/runtime/interpreter.js.map +1 -1
- package/out/runtime/jsmodules.d.ts +8 -0
- package/out/runtime/jsmodules.d.ts.map +1 -1
- package/out/runtime/jsmodules.js +139 -22
- package/out/runtime/jsmodules.js.map +1 -1
- package/out/runtime/loader.d.ts +1 -0
- package/out/runtime/loader.d.ts.map +1 -1
- package/out/runtime/loader.js +9 -2
- package/out/runtime/loader.js.map +1 -1
- package/out/runtime/module-transform.d.ts +77 -0
- package/out/runtime/module-transform.d.ts.map +1 -0
- package/out/runtime/module-transform.js +201 -0
- package/out/runtime/module-transform.js.map +1 -0
- package/out/runtime/modules/ai.d.ts.map +1 -1
- package/out/runtime/modules/ai.js +18 -6
- package/out/runtime/modules/ai.js.map +1 -1
- package/out/runtime/modules/core.d.ts +5 -0
- package/out/runtime/modules/core.d.ts.map +1 -1
- package/out/runtime/modules/core.js +58 -6
- package/out/runtime/modules/core.js.map +1 -1
- package/out/syntaxes/agentlang.monarch.js +1 -1
- package/out/syntaxes/agentlang.monarch.js.map +1 -1
- package/package.json +1 -1
- package/src/api/http.ts +51 -25
- package/src/cli/main.ts +20 -0
- package/src/language/agentlang.langium +3 -1
- package/src/language/generated/ast.ts +32 -8
- package/src/language/generated/grammar.ts +215 -164
- package/src/runtime/defs.ts +47 -0
- package/src/runtime/exec-graph.ts +13 -0
- package/src/runtime/interpreter.ts +12 -0
- package/src/runtime/jsmodules.ts +189 -22
- package/src/runtime/loader.ts +10 -2
- package/src/runtime/module-transform.ts +277 -0
- package/src/runtime/modules/ai.ts +20 -6
- package/src/runtime/modules/core.ts +80 -5
- package/src/syntaxes/agentlang.monarch.ts +1 -1
package/src/runtime/defs.ts
CHANGED
|
@@ -94,6 +94,26 @@ export function setSubscriptionFn(f: Function) {
|
|
|
94
94
|
SetSubscription = f;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
// Module loader configuration for browser-compatible resolver loading
|
|
98
|
+
export type FileReader = (filePath: string) => Promise<string>;
|
|
99
|
+
export type DependencyProvider = (moduleName: string) => any | undefined;
|
|
100
|
+
|
|
101
|
+
export interface ModuleLoaderConfig {
|
|
102
|
+
fileReader: FileReader;
|
|
103
|
+
dependencyProvider?: DependencyProvider;
|
|
104
|
+
basePath?: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let _moduleLoader: ModuleLoaderConfig | undefined = undefined;
|
|
108
|
+
|
|
109
|
+
export function setModuleLoader(config: ModuleLoaderConfig) {
|
|
110
|
+
_moduleLoader = config;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function getModuleLoader(): ModuleLoaderConfig | undefined {
|
|
114
|
+
return _moduleLoader;
|
|
115
|
+
}
|
|
116
|
+
|
|
97
117
|
export const ForceReadPermFlag = 'f-r-f';
|
|
98
118
|
export const FlowSuspensionTag = `--`;
|
|
99
119
|
|
|
@@ -105,6 +125,7 @@ export enum SubGraphType {
|
|
|
105
125
|
PURGE,
|
|
106
126
|
RETURN,
|
|
107
127
|
AGENT,
|
|
128
|
+
THROW,
|
|
108
129
|
NONE,
|
|
109
130
|
}
|
|
110
131
|
|
|
@@ -386,3 +407,29 @@ export function isRuntimeMode_generate_migration(): boolean {
|
|
|
386
407
|
export function isRuntimeMode_undo_migration(): boolean {
|
|
387
408
|
return RuntimeMode === RuntimeModeTag.UNDO_MIGRATION;
|
|
388
409
|
}
|
|
410
|
+
|
|
411
|
+
let UpdateEventEndpoints: Function | undefined;
|
|
412
|
+
let UpdateEntityEndpoints: Function | undefined;
|
|
413
|
+
|
|
414
|
+
export function setEventEndpointsUpdater(f: Function) {
|
|
415
|
+
UpdateEventEndpoints = f;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function setEntityEndpointsUpdater(f: Function) {
|
|
419
|
+
UpdateEntityEndpoints = f;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
export function updateEndpoints(moduleName: string) {
|
|
423
|
+
if (UpdateEventEndpoints !== undefined) {
|
|
424
|
+
UpdateEventEndpoints(moduleName);
|
|
425
|
+
}
|
|
426
|
+
if (UpdateEntityEndpoints !== undefined) {
|
|
427
|
+
UpdateEntityEndpoints(moduleName);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export let InternDynamicModule: Function | undefined;
|
|
432
|
+
|
|
433
|
+
export function setInternDynamicModuleFn(f: Function) {
|
|
434
|
+
InternDynamicModule = f;
|
|
435
|
+
}
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
Purge,
|
|
14
14
|
Return,
|
|
15
15
|
Statement,
|
|
16
|
+
ThrowError,
|
|
16
17
|
} from '../language/generated/ast.js';
|
|
17
18
|
import { parseModule, parseStatement } from '../language/parser.js';
|
|
18
19
|
import { ExecGraph, ExecGraphNode, ExecGraphWalker, SubGraphType } from './defs.js';
|
|
@@ -157,6 +158,15 @@ class GraphGenerator extends PatternHandler {
|
|
|
157
158
|
this.handleSubPattern(SubGraphType.RETURN, ret.pattern, env);
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
override async handleThrow(throwErr: ThrowError, env: Environment) {
|
|
162
|
+
const handler = new GraphGenerator();
|
|
163
|
+
await handler.handleExpression(
|
|
164
|
+
throwErr.reason,
|
|
165
|
+
Environment.from(env).setActiveUserData(throwErr.reason)
|
|
166
|
+
);
|
|
167
|
+
this.addSubGraph(SubGraphType.THROW, handler.getGraph(), env);
|
|
168
|
+
}
|
|
169
|
+
|
|
160
170
|
getGraph(): ExecGraph {
|
|
161
171
|
return this.graph;
|
|
162
172
|
}
|
|
@@ -261,6 +271,9 @@ export async function executeGraph(execGraph: ExecGraph, env: Environment): Prom
|
|
|
261
271
|
case SubGraphType.RETURN:
|
|
262
272
|
await executeReturnSubGraph(subg, env);
|
|
263
273
|
return;
|
|
274
|
+
case SubGraphType.THROW:
|
|
275
|
+
await evaluateExpression(subg.getRootNodes()[0].code as Expr, env);
|
|
276
|
+
throw new Error(env.getLastResult());
|
|
264
277
|
default:
|
|
265
278
|
throw new Error(`Invalid sub-graph type: ${node.subGraphType}`);
|
|
266
279
|
}
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
SelectIntoSpec,
|
|
28
28
|
SetAttribute,
|
|
29
29
|
Statement,
|
|
30
|
+
ThrowError,
|
|
30
31
|
} from '../language/generated/ast.js';
|
|
31
32
|
import {
|
|
32
33
|
maybeInstanceAsString,
|
|
@@ -1217,6 +1218,10 @@ export class PatternHandler {
|
|
|
1217
1218
|
async handleReturn(ret: Return, env: Environment) {
|
|
1218
1219
|
await evaluatePattern(ret.pattern, env);
|
|
1219
1220
|
}
|
|
1221
|
+
|
|
1222
|
+
async handleThrow(throwErr: ThrowError, env: Environment) {
|
|
1223
|
+
await evaluateThrowError(throwErr, env);
|
|
1224
|
+
}
|
|
1220
1225
|
}
|
|
1221
1226
|
|
|
1222
1227
|
const DefaultPatternHandler = new PatternHandler();
|
|
@@ -1243,9 +1248,16 @@ export async function evaluatePattern(
|
|
|
1243
1248
|
} else if (pat.return) {
|
|
1244
1249
|
await handler.handleReturn(pat.return, env);
|
|
1245
1250
|
env.markForReturn();
|
|
1251
|
+
} else if (pat.throwError) {
|
|
1252
|
+
await handler.handleThrow(pat.throwError, env);
|
|
1246
1253
|
}
|
|
1247
1254
|
}
|
|
1248
1255
|
|
|
1256
|
+
async function evaluateThrowError(throwErr: ThrowError, env: Environment) {
|
|
1257
|
+
await evaluateExpression(throwErr.reason, env);
|
|
1258
|
+
throw new Error(env.getLastResult());
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1249
1261
|
async function evaluateFullTextSearch(fts: FullTextSearch, env: Environment): Promise<void> {
|
|
1250
1262
|
let n = escapeQueryName(fts.name);
|
|
1251
1263
|
if (!isFqName(n)) {
|
package/src/runtime/jsmodules.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { logger } from './logger.js';
|
|
2
2
|
import { now, splitRefs } from './util.js';
|
|
3
3
|
import { isNodeEnv } from '../utils/runtime.js';
|
|
4
|
-
import { setModuleFnFetcher } from './defs.js';
|
|
4
|
+
import { setModuleFnFetcher, getModuleLoader } from './defs.js';
|
|
5
|
+
import {
|
|
6
|
+
transformModule,
|
|
7
|
+
wrapModuleCode,
|
|
8
|
+
evaluateModule,
|
|
9
|
+
wrapCommonJSCode,
|
|
10
|
+
evaluateCommonJS,
|
|
11
|
+
} from './module-transform.js';
|
|
5
12
|
|
|
6
13
|
let dirname: any = undefined;
|
|
7
14
|
let sep: any = undefined;
|
|
@@ -24,39 +31,199 @@ if (isNodeEnv) {
|
|
|
24
31
|
|
|
25
32
|
const importedModules = new Map<string, any>();
|
|
26
33
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Load dependencies for a module using the configured dependency provider
|
|
36
|
+
*/
|
|
37
|
+
async function loadModuleDependencies(
|
|
38
|
+
imports: Array<{ defaultImport?: string; namedImports?: string[]; moduleSpecifier: string }>
|
|
39
|
+
): Promise<Map<string, any>> {
|
|
40
|
+
const loadedDeps = new Map<string, any>();
|
|
41
|
+
|
|
42
|
+
for (const imp of imports) {
|
|
43
|
+
const moduleSpecifier = imp.moduleSpecifier;
|
|
44
|
+
|
|
45
|
+
logger.debug(`Loading dependency: ${moduleSpecifier}`);
|
|
46
|
+
|
|
47
|
+
// Try to get from dependency provider first
|
|
48
|
+
let module: any;
|
|
49
|
+
const moduleLoader = getModuleLoader();
|
|
50
|
+
if (moduleLoader?.dependencyProvider) {
|
|
51
|
+
module = moduleLoader.dependencyProvider(moduleSpecifier);
|
|
32
52
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
53
|
+
|
|
54
|
+
if (!module) {
|
|
55
|
+
// In browser without dependency provider, we can't load external modules
|
|
56
|
+
if (!isNodeEnv) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Failed to load dependency "${moduleSpecifier}". ` +
|
|
59
|
+
`Please add it to the dependency provider via setModuleLoader().`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// In Node.js, try dynamic import
|
|
64
|
+
try {
|
|
65
|
+
module = await import(/* @vite-ignore */ moduleSpecifier);
|
|
66
|
+
logger.debug(`Loaded ${moduleSpecifier} via dynamic import`);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`Failed to load dependency "${moduleSpecifier}": ${error}`);
|
|
39
69
|
}
|
|
40
|
-
|
|
70
|
+
} else {
|
|
71
|
+
logger.debug(`Using pre-imported dependency: ${moduleSpecifier}`);
|
|
41
72
|
}
|
|
42
|
-
|
|
43
|
-
|
|
73
|
+
|
|
74
|
+
// Handle default import
|
|
75
|
+
if (imp.defaultImport) {
|
|
76
|
+
loadedDeps.set(imp.defaultImport, module.default || module);
|
|
44
77
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
78
|
+
|
|
79
|
+
// Handle named imports
|
|
80
|
+
if (imp.namedImports) {
|
|
81
|
+
for (const name of imp.namedImports) {
|
|
82
|
+
loadedDeps.set(name, module[name]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return loadedDeps;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Load a JavaScript module in the browser environment
|
|
92
|
+
*/
|
|
93
|
+
async function loadModuleInBrowser(
|
|
94
|
+
path: string,
|
|
95
|
+
name: string,
|
|
96
|
+
moduleFileName?: string
|
|
97
|
+
): Promise<any> {
|
|
98
|
+
const moduleLoader = getModuleLoader();
|
|
99
|
+
if (!moduleLoader) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
'ModuleLoader not configured. Call setModuleLoader() with a fileReader and dependencyProvider.'
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
logger.info(`Loading module in browser: ${path} as ${name}`);
|
|
106
|
+
|
|
107
|
+
// Resolve path relative to the module file
|
|
108
|
+
let resolvedPath = path;
|
|
109
|
+
if (moduleFileName) {
|
|
110
|
+
const dir = dirname(moduleFileName);
|
|
111
|
+
resolvedPath = dir ? `${dir}${sep}${path}` : path;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Add basePath if configured
|
|
115
|
+
if (moduleLoader.basePath) {
|
|
116
|
+
if (!resolvedPath.startsWith('/')) {
|
|
117
|
+
resolvedPath = `${moduleLoader.basePath}${sep}${resolvedPath}`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
logger.debug(`Resolved path: ${resolvedPath}`);
|
|
122
|
+
|
|
123
|
+
// Read the module content
|
|
124
|
+
const content = await moduleLoader.fileReader(resolvedPath);
|
|
125
|
+
|
|
126
|
+
if (!content || content.trim().length === 0) {
|
|
127
|
+
logger.warn(`Module file ${resolvedPath} is empty`);
|
|
52
128
|
return {};
|
|
53
129
|
}
|
|
130
|
+
|
|
131
|
+
logger.debug(`Read ${content.length} characters from ${resolvedPath}`);
|
|
132
|
+
|
|
133
|
+
// Check if the file uses ES6 import syntax
|
|
134
|
+
const hasImports = /\bimport\s+/.test(content);
|
|
135
|
+
|
|
136
|
+
let moduleExports: any;
|
|
137
|
+
|
|
138
|
+
if (hasImports) {
|
|
139
|
+
logger.debug(`Processing ES6 imports for: ${name}`);
|
|
140
|
+
|
|
141
|
+
// Transform the module
|
|
142
|
+
const { transformedCode, imports } = transformModule(content);
|
|
143
|
+
|
|
144
|
+
// Load all import dependencies
|
|
145
|
+
const loadedDeps = await loadModuleDependencies(imports);
|
|
146
|
+
|
|
147
|
+
logger.debug(`Loaded ${loadedDeps.size} dependencies, executing transformed code...`);
|
|
148
|
+
|
|
149
|
+
// Create wrapped code with injected dependencies
|
|
150
|
+
const wrappedCode = wrapModuleCode(transformedCode, loadedDeps);
|
|
151
|
+
|
|
152
|
+
// Evaluate and get exports
|
|
153
|
+
moduleExports = evaluateModule(wrappedCode, loadedDeps);
|
|
154
|
+
|
|
155
|
+
logger.debug(`Module exports:`, Object.keys(moduleExports));
|
|
156
|
+
} else {
|
|
157
|
+
logger.debug(`Loading as CommonJS module: ${name}`);
|
|
158
|
+
|
|
159
|
+
// Wrap and evaluate as CommonJS
|
|
160
|
+
const wrappedCode = wrapCommonJSCode(content);
|
|
161
|
+
moduleExports = evaluateCommonJS(wrappedCode);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return moduleExports;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Load a JavaScript module in Node.js environment
|
|
169
|
+
*/
|
|
170
|
+
async function loadModuleInNode(path: string, name: string, moduleFileName?: string): Promise<any> {
|
|
171
|
+
if (moduleFileName) {
|
|
172
|
+
let s: string = dirname(moduleFileName);
|
|
173
|
+
if (s.startsWith('./')) {
|
|
174
|
+
s = s.substring(2);
|
|
175
|
+
} else if (s == '.') {
|
|
176
|
+
s = process.cwd();
|
|
177
|
+
}
|
|
178
|
+
path = `${s}${sep}${path}`;
|
|
179
|
+
}
|
|
180
|
+
if (!(path.startsWith(sep) || path.startsWith('.'))) {
|
|
181
|
+
path = process.cwd() + sep + path;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const m = await import(/* @vite-ignore */ path);
|
|
185
|
+
return m;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Usage: importModule("./mymodels/acme.js")
|
|
189
|
+
export async function importModule(path: string, name: string, moduleFileName?: string) {
|
|
190
|
+
if (importedModules.has(name)) {
|
|
191
|
+
logger.warn(`Alias '${name}' will overwrite a previously imported module`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let moduleExports: any;
|
|
195
|
+
|
|
196
|
+
if (isNodeEnv) {
|
|
197
|
+
moduleExports = await loadModuleInNode(path, name, moduleFileName);
|
|
198
|
+
} else {
|
|
199
|
+
moduleExports = await loadModuleInBrowser(path, name, moduleFileName);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
importedModules.set(name, moduleExports);
|
|
203
|
+
|
|
204
|
+
logger.info(`Successfully imported module: ${name}`, Object.keys(moduleExports));
|
|
205
|
+
|
|
206
|
+
return moduleExports;
|
|
54
207
|
}
|
|
55
208
|
|
|
56
209
|
export function moduleImported(moduleName: string): boolean {
|
|
57
210
|
return importedModules.has(moduleName);
|
|
58
211
|
}
|
|
59
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Get an imported module by its alias name
|
|
215
|
+
*/
|
|
216
|
+
export function getImportedModule(moduleName: string): any | undefined {
|
|
217
|
+
return importedModules.get(moduleName);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get all imported module names
|
|
222
|
+
*/
|
|
223
|
+
export function getImportedModuleNames(): string[] {
|
|
224
|
+
return Array.from(importedModules.keys());
|
|
225
|
+
}
|
|
226
|
+
|
|
60
227
|
function maybeEvalFunction(fnName: string): Function | undefined {
|
|
61
228
|
try {
|
|
62
229
|
return eval(fnName);
|
package/src/runtime/loader.ts
CHANGED
|
@@ -1137,15 +1137,23 @@ export async function parseAndIntern(code: string, moduleName?: string) {
|
|
|
1137
1137
|
await internModule(r.parseResult.value);
|
|
1138
1138
|
}
|
|
1139
1139
|
|
|
1140
|
+
export async function refreshModuleDefinition(moduleName: string, moduleDefinition: string) {
|
|
1141
|
+
removeModule(moduleName);
|
|
1142
|
+
const r = await parse(moduleDefinition);
|
|
1143
|
+
maybeRaiseParserErrors(r);
|
|
1144
|
+
await internModule(r.parseResult.value);
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1140
1147
|
export async function internModule(
|
|
1141
1148
|
module: ModuleDefinition,
|
|
1142
1149
|
moduleFileName?: string
|
|
1143
1150
|
): Promise<Module> {
|
|
1144
1151
|
const mn = module.name;
|
|
1145
1152
|
const r = addModule(mn);
|
|
1146
|
-
|
|
1153
|
+
// Process imports sequentially to ensure all JS modules are loaded before definitions
|
|
1154
|
+
for (const imp of module.imports as Import[]) {
|
|
1147
1155
|
await importModule(imp.path, imp.name, moduleFileName);
|
|
1148
|
-
}
|
|
1156
|
+
}
|
|
1149
1157
|
for (let i = 0; i < module.defs.length; ++i) {
|
|
1150
1158
|
const def = module.defs[i];
|
|
1151
1159
|
await addFromDef(def, mn);
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ES6 Module Transformation Utilities
|
|
3
|
+
*
|
|
4
|
+
* Transforms ES6 import/export syntax into code that can be evaluated
|
|
5
|
+
* in a browser environment without native ES6 module support.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { logger } from './logger.js';
|
|
9
|
+
|
|
10
|
+
export interface ImportInfo {
|
|
11
|
+
defaultImport?: string;
|
|
12
|
+
namedImports?: string[];
|
|
13
|
+
moduleSpecifier: string;
|
|
14
|
+
fullStatement: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface DynamicImportInfo {
|
|
18
|
+
fullMatch: string;
|
|
19
|
+
templateString: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TransformResult {
|
|
23
|
+
transformedCode: string;
|
|
24
|
+
imports: ImportInfo[];
|
|
25
|
+
dynamicImports: DynamicImportInfo[];
|
|
26
|
+
exports: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parse ES6 import statements from code
|
|
31
|
+
* Matches: import ... from 'url'
|
|
32
|
+
*/
|
|
33
|
+
export function parseImports(content: string): ImportInfo[] {
|
|
34
|
+
const imports: ImportInfo[] = [];
|
|
35
|
+
|
|
36
|
+
// Match: import ... from 'url'
|
|
37
|
+
const importRegex = /import\s+(?:(\w+)|{([^}]+)})\s+from\s+['"]([^'"]+)['"]/g;
|
|
38
|
+
let match;
|
|
39
|
+
|
|
40
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
41
|
+
const [fullStatement, defaultImport, namedImports, moduleSpecifier] = match;
|
|
42
|
+
|
|
43
|
+
imports.push({
|
|
44
|
+
defaultImport,
|
|
45
|
+
namedImports: namedImports ? namedImports.split(',').map(n => n.trim()) : undefined,
|
|
46
|
+
moduleSpecifier,
|
|
47
|
+
fullStatement,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return imports;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Parse dynamic import statements
|
|
56
|
+
* Matches: await import(`${process.cwd()}/...`)
|
|
57
|
+
*/
|
|
58
|
+
export function parseDynamicImports(content: string): DynamicImportInfo[] {
|
|
59
|
+
const dynamicImports: DynamicImportInfo[] = [];
|
|
60
|
+
|
|
61
|
+
const dynamicImportRegex = /await\s+import\s*\(\s*`([^`]+)`\s*\)/g;
|
|
62
|
+
let match;
|
|
63
|
+
|
|
64
|
+
while ((match = dynamicImportRegex.exec(content)) !== null) {
|
|
65
|
+
const [fullMatch, templateString] = match;
|
|
66
|
+
dynamicImports.push({
|
|
67
|
+
fullMatch,
|
|
68
|
+
templateString,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return dynamicImports;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse export statements and extract exported names
|
|
77
|
+
*/
|
|
78
|
+
export function parseExports(content: string): string[] {
|
|
79
|
+
const exportedNames: string[] = [];
|
|
80
|
+
|
|
81
|
+
// Handle: export function name() {}
|
|
82
|
+
const exportFnRegex = /export\s+(async\s+)?function\s+(\w+)/g;
|
|
83
|
+
let match;
|
|
84
|
+
|
|
85
|
+
while ((match = exportFnRegex.exec(content)) !== null) {
|
|
86
|
+
exportedNames.push(match[2]);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Handle: export const name = ...
|
|
90
|
+
const exportConstRegex = /export\s+(const|let|var)\s+(\w+)/g;
|
|
91
|
+
|
|
92
|
+
while ((match = exportConstRegex.exec(content)) !== null) {
|
|
93
|
+
exportedNames.push(match[2]);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Handle: export { name1, name2 }
|
|
97
|
+
const exportListRegex = /export\s*{\s*([^}]+)\s*}/g;
|
|
98
|
+
|
|
99
|
+
while ((match = exportListRegex.exec(content)) !== null) {
|
|
100
|
+
const names = match[1].split(',').map(n => n.trim().split(' as ')[0].trim());
|
|
101
|
+
exportedNames.push(...names);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return exportedNames;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Remove import statements from code
|
|
109
|
+
*/
|
|
110
|
+
export function removeImportStatements(content: string, imports: ImportInfo[]): string {
|
|
111
|
+
let result = content;
|
|
112
|
+
|
|
113
|
+
for (const imp of imports) {
|
|
114
|
+
result = result.replace(imp.fullStatement, '');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Remove export keywords from code
|
|
122
|
+
*/
|
|
123
|
+
export function removeExportKeywords(content: string): string {
|
|
124
|
+
let result = content;
|
|
125
|
+
|
|
126
|
+
// Remove: export function name() {}
|
|
127
|
+
result = result.replace(
|
|
128
|
+
/export\s+(async\s+)?function\s+(\w+)/g,
|
|
129
|
+
(match, async, name) => `${async || ''}function ${name}`
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Remove: export const name = ...
|
|
133
|
+
result = result.replace(
|
|
134
|
+
/export\s+(const|let|var)\s+(\w+)/g,
|
|
135
|
+
(match, keyword, name) => `${keyword} ${name}`
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
// Remove: export { name1, name2 }
|
|
139
|
+
result = result.replace(/export\s*{\s*[^}]+\s*}/g, '');
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Replace dynamic imports with variable references
|
|
146
|
+
*/
|
|
147
|
+
export function replaceDynamicImports(
|
|
148
|
+
content: string,
|
|
149
|
+
dynamicImports: DynamicImportInfo[]
|
|
150
|
+
): { code: string; varNames: Map<string, string> } {
|
|
151
|
+
let result = content;
|
|
152
|
+
const varNames = new Map<string, string>();
|
|
153
|
+
|
|
154
|
+
for (const dynImp of dynamicImports) {
|
|
155
|
+
// Clean up process.cwd() references
|
|
156
|
+
const cleanedPath = dynImp.templateString.replace(/\$\{process\.cwd\(\)\}/g, '');
|
|
157
|
+
|
|
158
|
+
// Create a variable name from the path
|
|
159
|
+
const varName = cleanedPath.split('/').pop()?.replace(/\.js$/, '') || 'dynamicModule';
|
|
160
|
+
|
|
161
|
+
varNames.set(cleanedPath, varName);
|
|
162
|
+
result = result.replace(dynImp.fullMatch, varName);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return { code: result, varNames };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Generate export assignments
|
|
170
|
+
*/
|
|
171
|
+
export function generateExportAssignments(exportedNames: string[]): string {
|
|
172
|
+
if (exportedNames.length === 0) {
|
|
173
|
+
return '';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const assignments = exportedNames.map(name => `exports.${name} = ${name};`).join('\n');
|
|
177
|
+
|
|
178
|
+
return `\n\n// Export assignments\n${assignments}`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Transform ES6 module code to CommonJS-style code
|
|
183
|
+
*/
|
|
184
|
+
export function transformModule(content: string): TransformResult {
|
|
185
|
+
logger.debug('Transforming ES6 module code');
|
|
186
|
+
|
|
187
|
+
// Parse imports, exports, and dynamic imports
|
|
188
|
+
const imports = parseImports(content);
|
|
189
|
+
const dynamicImports = parseDynamicImports(content);
|
|
190
|
+
const exports = parseExports(content);
|
|
191
|
+
|
|
192
|
+
// Remove import statements
|
|
193
|
+
let transformedCode = removeImportStatements(content, imports);
|
|
194
|
+
|
|
195
|
+
// Handle dynamic imports
|
|
196
|
+
const { code: codeWithoutDynImports } = replaceDynamicImports(transformedCode, dynamicImports);
|
|
197
|
+
transformedCode = codeWithoutDynImports;
|
|
198
|
+
|
|
199
|
+
// Remove export keywords
|
|
200
|
+
transformedCode = removeExportKeywords(transformedCode);
|
|
201
|
+
|
|
202
|
+
// Add export assignments
|
|
203
|
+
transformedCode += generateExportAssignments(exports);
|
|
204
|
+
|
|
205
|
+
logger.debug(
|
|
206
|
+
`Found ${imports.length} imports, ${dynamicImports.length} dynamic imports, ${exports.length} exports`
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
transformedCode,
|
|
211
|
+
imports,
|
|
212
|
+
dynamicImports,
|
|
213
|
+
exports,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Create a wrapped function that evaluates the transformed code
|
|
219
|
+
* with injected dependencies
|
|
220
|
+
*/
|
|
221
|
+
export function wrapModuleCode(transformedCode: string, importedModules: Map<string, any>): string {
|
|
222
|
+
const importNames = Array.from(importedModules.keys());
|
|
223
|
+
|
|
224
|
+
const wrappedCode = `
|
|
225
|
+
(function(exports, ${importNames.join(', ')}) {
|
|
226
|
+
${transformedCode}
|
|
227
|
+
return exports;
|
|
228
|
+
})
|
|
229
|
+
`;
|
|
230
|
+
|
|
231
|
+
return wrappedCode;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Evaluate wrapped module code and return the exports
|
|
236
|
+
*/
|
|
237
|
+
export function evaluateModule(wrappedCode: string, importedModules: Map<string, any>): any {
|
|
238
|
+
const exportObj: any = {};
|
|
239
|
+
const importValues = Array.from(importedModules.values());
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const moduleFactory = eval(wrappedCode);
|
|
243
|
+
return moduleFactory(exportObj, ...importValues);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
logger.error('Failed to evaluate module:', error);
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Create CommonJS-style wrapper for code without ES6 imports
|
|
252
|
+
*/
|
|
253
|
+
export function wrapCommonJSCode(content: string): string {
|
|
254
|
+
return `
|
|
255
|
+
(function(module, exports) {
|
|
256
|
+
${content}
|
|
257
|
+
return module.exports || exports;
|
|
258
|
+
})
|
|
259
|
+
`;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Evaluate CommonJS-style code
|
|
264
|
+
*/
|
|
265
|
+
export function evaluateCommonJS(wrappedCode: string): any {
|
|
266
|
+
const moduleObj: any = { exports: {} };
|
|
267
|
+
const exportsObj: any = {};
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
const moduleFactory = eval(wrappedCode);
|
|
271
|
+
const result = moduleFactory(moduleObj, exportsObj);
|
|
272
|
+
return result || moduleObj.exports || exportsObj;
|
|
273
|
+
} catch (error) {
|
|
274
|
+
logger.error('Failed to evaluate CommonJS module:', error);
|
|
275
|
+
throw error;
|
|
276
|
+
}
|
|
277
|
+
}
|