agency-lang 0.0.97 → 0.0.99
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/dist/lib/cli/commands.d.ts +2 -0
- package/dist/lib/cli/commands.js +19 -6
- package/dist/lib/cli/debug.d.ts +1 -0
- package/dist/lib/cli/debug.js +31 -10
- package/dist/lib/cli/util.d.ts +5 -0
- package/dist/lib/cli/util.js +45 -6
- package/dist/lib/config.d.ts +4 -0
- package/dist/lib/debugger/ui.js +1 -1
- package/dist/lib/debugger/uiState.d.ts +6 -0
- package/dist/lib/debugger/uiState.js +25 -7
- package/dist/lib/importPaths.test.js +72 -1
- package/dist/lib/importStrategy.d.ts +37 -0
- package/dist/lib/importStrategy.js +64 -0
- package/dist/lib/version.d.ts +1 -1
- package/dist/lib/version.js +1 -1
- package/dist/scripts/agency.js +2 -0
- package/package.json +1 -1
- package/stdlib/agent.js +43 -43
- package/stdlib/array.js +1812 -1732
- package/stdlib/clipboard.js +463 -0
- package/stdlib/fs.js +95 -95
- package/stdlib/http.js +670 -0
- package/stdlib/index.agency +2 -3
- package/stdlib/index.js +1875 -1860
- package/stdlib/{_builtins.js → lib/builtins.js} +1 -1
- package/stdlib/lib/ui.js +1 -1
- package/stdlib/math.js +43 -43
- package/stdlib/object.js +836 -1160
- package/stdlib/path.js +95 -95
- package/stdlib/shell.js +95 -95
- package/stdlib/speech.js +494 -0
- package/stdlib/strategy.js +69 -69
- package/stdlib/system.js +56 -56
- package/stdlib/ui.js +1 -119
- package/stdlib/weather.js +16 -123
- package/stdlib/wikipedia.js +579 -0
- package/stdlib/_math.js +0 -9
- package/stdlib/_utils.js +0 -51
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AgencyConfig } from "../config.js";
|
|
2
2
|
import { AgencyProgram } from "../index.js";
|
|
3
3
|
import { type SymbolTable } from "../symbolTable.js";
|
|
4
|
+
import { type ImportStrategy } from "../importStrategy.js";
|
|
4
5
|
export declare function loadConfig(configPath?: string, verbose?: boolean): AgencyConfig;
|
|
5
6
|
export declare function readStdin(): Promise<string>;
|
|
6
7
|
export declare function parse(contents: string, config: AgencyConfig, applyTemplate?: boolean): AgencyProgram;
|
|
@@ -9,6 +10,7 @@ export declare function resetCompilationCache(): void;
|
|
|
9
10
|
export declare function compile(config: AgencyConfig, inputFile: string, _outputFile?: string, options?: {
|
|
10
11
|
ts?: boolean;
|
|
11
12
|
symbolTable?: SymbolTable;
|
|
13
|
+
importStrategy?: ImportStrategy;
|
|
12
14
|
}): string | null;
|
|
13
15
|
export declare function run(config: AgencyConfig, inputFile: string, outputFile?: string, resumeFile?: string): void;
|
|
14
16
|
export declare function format(contents: string, config: AgencyConfig): Promise<string>;
|
package/dist/lib/cli/commands.js
CHANGED
|
@@ -9,6 +9,7 @@ import { transformSync } from "esbuild";
|
|
|
9
9
|
import * as fs from "fs";
|
|
10
10
|
import * as path from "path";
|
|
11
11
|
import { getStdlibDir, isPkgImport, isStdlibImport, resolveAgencyImportPath, } from "../importPaths.js";
|
|
12
|
+
import { CompileStrategy, RunStrategy } from "../importStrategy.js";
|
|
12
13
|
import { parseAgency } from "../parser.js";
|
|
13
14
|
import { findRecursively, getImports } from "./util.js";
|
|
14
15
|
// Load configuration from agency.json
|
|
@@ -126,8 +127,11 @@ export function compile(config, inputFile, _outputFile, options) {
|
|
|
126
127
|
}
|
|
127
128
|
const imports = getImports(resolvedProgram);
|
|
128
129
|
for (const importPath of imports) {
|
|
130
|
+
// stdlib and pkg imports are pre-compiled; don't recompile them
|
|
131
|
+
if (isStdlibImport(importPath) || isPkgImport(importPath))
|
|
132
|
+
continue;
|
|
129
133
|
const absPath = resolveAgencyImportPath(importPath, absoluteInputFile);
|
|
130
|
-
if (config.restrictImports
|
|
134
|
+
if (config.restrictImports) {
|
|
131
135
|
const projectRoot = process.cwd();
|
|
132
136
|
if (!absPath.startsWith(projectRoot + path.sep) &&
|
|
133
137
|
absPath !== projectRoot) {
|
|
@@ -136,12 +140,21 @@ export function compile(config, inputFile, _outputFile, options) {
|
|
|
136
140
|
}
|
|
137
141
|
compile(config, absPath, undefined, { ...options, symbolTable });
|
|
138
142
|
}
|
|
139
|
-
//
|
|
143
|
+
// Rewrite import paths in the AST using the import strategy
|
|
144
|
+
const strategy = options?.importStrategy ?? new CompileStrategy({ targetExt: ext });
|
|
145
|
+
const nonAgencyImports = [];
|
|
140
146
|
resolvedProgram.nodes.forEach((node) => {
|
|
141
|
-
if (node.type
|
|
142
|
-
|
|
147
|
+
if (node.type !== "importStatement")
|
|
148
|
+
return;
|
|
149
|
+
if (isStdlibImport(node.modulePath) || isPkgImport(node.modulePath))
|
|
150
|
+
return;
|
|
151
|
+
node.modulePath = strategy.rewriteImport(node.modulePath, absoluteInputFile);
|
|
152
|
+
// Collect non-Agency imports for dependency preparation
|
|
153
|
+
if (!node.modulePath.endsWith(".agency")) {
|
|
154
|
+
nonAgencyImports.push(node.modulePath);
|
|
143
155
|
}
|
|
144
156
|
});
|
|
157
|
+
strategy.prepareDependencies(nonAgencyImports, absoluteInputFile);
|
|
145
158
|
const moduleId = path.relative(process.cwd(), absoluteInputFile);
|
|
146
159
|
const generatedCode = generateTypeScript(resolvedProgram, config, info, moduleId);
|
|
147
160
|
if (options?.ts) {
|
|
@@ -161,8 +174,8 @@ export function compile(config, inputFile, _outputFile, options) {
|
|
|
161
174
|
return outputFile;
|
|
162
175
|
}
|
|
163
176
|
export function run(config, inputFile, outputFile, resumeFile) {
|
|
164
|
-
// Compile the file
|
|
165
|
-
const output = compile(config, inputFile, outputFile);
|
|
177
|
+
// Compile the file with RunStrategy so dependencies are prepared for execution
|
|
178
|
+
const output = compile(config, inputFile, outputFile, { importStrategy: new RunStrategy() });
|
|
166
179
|
if (output === null) {
|
|
167
180
|
console.error("Error: No output file generated.");
|
|
168
181
|
process.exit(1);
|
package/dist/lib/cli/debug.d.ts
CHANGED
package/dist/lib/cli/debug.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { compile } from "./commands.js";
|
|
2
|
-
import {
|
|
2
|
+
import { RunStrategy } from "../importStrategy.js";
|
|
3
|
+
import { pickANode, resolveCompiledFile } from "./util.js";
|
|
3
4
|
import { parseAgency } from "../parser.js";
|
|
4
5
|
import { getNodesOfType } from "../utils/node.js";
|
|
5
6
|
import { DebuggerDriver } from "../debugger/driver.js";
|
|
@@ -77,25 +78,45 @@ export async function debug(config, _inputFile, options = {}) {
|
|
|
77
78
|
}
|
|
78
79
|
traceCheckpoints = [cp];
|
|
79
80
|
}
|
|
80
|
-
//
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
// Resolve distDir from CLI flag or config
|
|
82
|
+
const distDir = options.distDir ?? config.distDir;
|
|
83
|
+
let absOutput;
|
|
84
|
+
if (distDir) {
|
|
85
|
+
// distDir mode: import pre-compiled JS from the dist directory
|
|
86
|
+
const compiledPath = resolveCompiledFile(distDir, inputFile);
|
|
87
|
+
// Warn if source is newer than compiled output
|
|
88
|
+
const sourceMtime = fs.statSync(inputFile).mtimeMs;
|
|
89
|
+
const compiledMtime = fs.statSync(compiledPath).mtimeMs;
|
|
90
|
+
if (sourceMtime > compiledMtime) {
|
|
91
|
+
console.warn(`Warning: ${inputFile} is newer than ${compiledPath}.\n` +
|
|
92
|
+
`You may need to recompile before debugging.`);
|
|
93
|
+
}
|
|
94
|
+
absOutput = compiledPath;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
// Normal mode: compile the .agency file to .js on the fly
|
|
98
|
+
const debugConfig = { ...config, debugger: true };
|
|
99
|
+
const outputFile = compile(debugConfig, inputFile, undefined, { importStrategy: new RunStrategy() });
|
|
100
|
+
if (outputFile === null) {
|
|
101
|
+
console.error("Error: No output file generated.");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
absOutput = path.resolve(outputFile);
|
|
87
105
|
}
|
|
88
106
|
// Signal to stdlib UI components that they should fall back to console.log
|
|
89
107
|
// instead of drawing their own TUI, which would conflict with the debugger.
|
|
90
108
|
process.env.AGENCY_DEBUGGER = "1";
|
|
91
109
|
// Dynamically import the compiled module
|
|
92
|
-
const absOutput = path.resolve(outputFile);
|
|
93
110
|
const mod = await import(absOutput);
|
|
94
111
|
// Get the source map from the module
|
|
95
112
|
const sourceMap = mod.__sourceMap ?? {};
|
|
113
|
+
if (distDir && Object.keys(sourceMap).length === 0) {
|
|
114
|
+
console.warn("Warning: The compiled module has an empty source map. Was it compiled with instrument: false?\n" +
|
|
115
|
+
"The debugger may not be able to step through code.");
|
|
116
|
+
}
|
|
96
117
|
// Parse the .agency file to get the list of graph nodes
|
|
97
118
|
const contents = fs.readFileSync(inputFile, "utf-8");
|
|
98
|
-
const parseResult = parseAgency(contents,
|
|
119
|
+
const parseResult = parseAgency(contents, config);
|
|
99
120
|
if (!parseResult.success) {
|
|
100
121
|
console.error("Error: Could not parse Agency file.");
|
|
101
122
|
process.exit(1);
|
package/dist/lib/cli/util.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ export type InterruptHandler = {
|
|
|
21
21
|
resolvedValue?: any;
|
|
22
22
|
expectedMessage?: string;
|
|
23
23
|
};
|
|
24
|
+
/**
|
|
25
|
+
* Resolve the compiled .js file for an .agency file from a distDir.
|
|
26
|
+
* Throws if the compiled file doesn't exist.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCompiledFile(distDir: string, agencyFile: string): string;
|
|
24
29
|
type ExecuteNodeArgs = {
|
|
25
30
|
config: AgencyConfig;
|
|
26
31
|
agencyFile: string;
|
package/dist/lib/cli/util.js
CHANGED
|
@@ -11,6 +11,7 @@ import { isAgencyImport, resolveAgencyImportPath, getStdlibDir } from "../import
|
|
|
11
11
|
import renderEvaluate from "../templates/cli/evaluate.js";
|
|
12
12
|
import renderJudgeEvaluate from "../templates/cli/judgeEvaluate.js";
|
|
13
13
|
import { compile } from "./commands.js";
|
|
14
|
+
import { RunStrategy } from "../importStrategy.js";
|
|
14
15
|
import { parseAgency } from "../parser.js";
|
|
15
16
|
export function parseTarget(target) {
|
|
16
17
|
const colonIndex = target.lastIndexOf(":");
|
|
@@ -103,13 +104,39 @@ export async function promptForArgs(selectedNode) {
|
|
|
103
104
|
}
|
|
104
105
|
return { hasArgs, argsString };
|
|
105
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Resolve the compiled .js file for an .agency file from a distDir.
|
|
109
|
+
* Throws if the compiled file doesn't exist.
|
|
110
|
+
*/
|
|
111
|
+
export function resolveCompiledFile(distDir, agencyFile) {
|
|
112
|
+
const basename = path.basename(agencyFile, ".agency") + ".js";
|
|
113
|
+
const compiledPath = path.resolve(distDir, basename);
|
|
114
|
+
if (!fs.existsSync(compiledPath)) {
|
|
115
|
+
throw new Error(`Compiled file not found: ${compiledPath}\n` +
|
|
116
|
+
`Make sure you have compiled your Agency files and that distDir is correct.`);
|
|
117
|
+
}
|
|
118
|
+
return compiledPath;
|
|
119
|
+
}
|
|
106
120
|
export async function executeNodeAsync({ config, agencyFile, nodeName, hasArgs, argsString, interruptHandlers, }) {
|
|
107
|
-
|
|
121
|
+
const distDir = config.distDir;
|
|
122
|
+
let compiledPath;
|
|
123
|
+
if (distDir) {
|
|
124
|
+
compiledPath = resolveCompiledFile(distDir, agencyFile);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
compiledPath = compile(config, agencyFile, undefined, { importStrategy: new RunStrategy() });
|
|
128
|
+
}
|
|
108
129
|
const baseName = agencyFile.replace(".agency", "");
|
|
109
130
|
const evaluateFile = `${baseName}.evaluate.js`;
|
|
110
131
|
const resultsFile = `${baseName}.evaluate.json`;
|
|
132
|
+
// The template imports via "./${filename}", so compute a relative path
|
|
133
|
+
// from the evaluate script's directory to the compiled module.
|
|
134
|
+
let importSpecifier = path.relative(path.dirname(evaluateFile), compiledPath).replace(/\\/g, "/");
|
|
135
|
+
if (!importSpecifier.startsWith(".")) {
|
|
136
|
+
importSpecifier = `./${importSpecifier}`;
|
|
137
|
+
}
|
|
111
138
|
const evaluateScript = renderEvaluate({
|
|
112
|
-
filename:
|
|
139
|
+
filename: importSpecifier,
|
|
113
140
|
nodeName,
|
|
114
141
|
hasArgs,
|
|
115
142
|
args: argsString,
|
|
@@ -137,10 +164,23 @@ export async function executeNodeAsync({ config, agencyFile, nodeName, hasArgs,
|
|
|
137
164
|
}
|
|
138
165
|
}
|
|
139
166
|
export function executeNode(args) {
|
|
140
|
-
const
|
|
141
|
-
|
|
167
|
+
const distDir = args.config.distDir;
|
|
168
|
+
let compiledPath;
|
|
169
|
+
if (distDir) {
|
|
170
|
+
compiledPath = resolveCompiledFile(distDir, args.agencyFile);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
compiledPath = compile(args.config, args.agencyFile, undefined, { importStrategy: new RunStrategy() });
|
|
174
|
+
}
|
|
175
|
+
const evaluateFile = "__evaluate.js";
|
|
176
|
+
// The template imports via "./${filename}", so compute a relative path
|
|
177
|
+
// from the evaluate script's directory to the compiled module.
|
|
178
|
+
let importSpecifier = path.relative(path.dirname(evaluateFile), compiledPath).replace(/\\/g, "/");
|
|
179
|
+
if (!importSpecifier.startsWith(".")) {
|
|
180
|
+
importSpecifier = `./${importSpecifier}`;
|
|
181
|
+
}
|
|
142
182
|
const evaluateScript = renderEvaluate({
|
|
143
|
-
filename:
|
|
183
|
+
filename: importSpecifier,
|
|
144
184
|
nodeName: args.nodeName,
|
|
145
185
|
hasArgs: args.hasArgs,
|
|
146
186
|
args: args.argsString,
|
|
@@ -150,7 +190,6 @@ export function executeNode(args) {
|
|
|
150
190
|
: undefined,
|
|
151
191
|
resultsFilename: "__evaluate.json",
|
|
152
192
|
});
|
|
153
|
-
const evaluateFile = "__evaluate.js";
|
|
154
193
|
fs.writeFileSync(evaluateFile, evaluateScript);
|
|
155
194
|
execFileSync("node", [evaluateFile], { stdio: "inherit" });
|
|
156
195
|
const results = readFileSync("__evaluate.json", "utf-8");
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -102,6 +102,10 @@ export interface AgencyConfig {
|
|
|
102
102
|
/** Directory for auto-generated trace files. Each execution creates a new file
|
|
103
103
|
* named <timestamp>_<id>.agencytrace. */
|
|
104
104
|
traceDir?: string;
|
|
105
|
+
/** Directory containing pre-compiled JS output (e.g., "dist").
|
|
106
|
+
* When set, the debugger imports compiled modules from this directory
|
|
107
|
+
* instead of compiling on the fly. Resolved relative to cwd. */
|
|
108
|
+
distDir?: string;
|
|
105
109
|
/** Test runner configuration */
|
|
106
110
|
test?: {
|
|
107
111
|
/** Number of test files to run in parallel. Default: 1 (sequential). */
|
package/dist/lib/debugger/ui.js
CHANGED
|
@@ -395,7 +395,7 @@ export class DebuggerUI {
|
|
|
395
395
|
renderSourcePane() {
|
|
396
396
|
const moduleId = this.state.getModuleId();
|
|
397
397
|
const currentLine = this.state.getCurrentLine();
|
|
398
|
-
const filePath = moduleId;
|
|
398
|
+
const filePath = this.state.resolveModulePath(moduleId, [".agency"]) ?? moduleId;
|
|
399
399
|
const fileContent = readSourceFile(filePath);
|
|
400
400
|
const highlighted = syntaxHighlight(fileContent, "ts");
|
|
401
401
|
const lines = highlighted.split("\n");
|
|
@@ -19,6 +19,12 @@ export declare class UIState {
|
|
|
19
19
|
private sourceMapCache;
|
|
20
20
|
constructor();
|
|
21
21
|
static fromCheckpoint(checkpoint: Checkpoint | undefined): UIState;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a moduleId (e.g., "stdlib/index.agency") to an actual file path,
|
|
24
|
+
* trying the given extensions. Handles stdlib modules that live inside
|
|
25
|
+
* node_modules/agency-lang/stdlib/ when running from an external project.
|
|
26
|
+
*/
|
|
27
|
+
resolveModulePath(moduleId: string, extensions: string[]): string | null;
|
|
22
28
|
private agencyToJsFile;
|
|
23
29
|
setCheckpoint(checkpoint: Checkpoint | undefined): Promise<void>;
|
|
24
30
|
setWithOverrides(obj: Record<string, unknown>, array: ValWithOverride[]): void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Checkpoint } from "../runtime/state/checkpointStore.js";
|
|
2
2
|
import { GlobalStore } from "../runtime/state/globalStore.js";
|
|
3
|
+
import { getStdlibDir } from "../importPaths.js";
|
|
3
4
|
import fs from "fs";
|
|
4
5
|
import path from "path";
|
|
5
6
|
export class UIState {
|
|
@@ -30,17 +31,34 @@ export class UIState {
|
|
|
30
31
|
uiState.setCheckpoint(checkpoint);
|
|
31
32
|
return uiState;
|
|
32
33
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a moduleId (e.g., "stdlib/index.agency") to an actual file path,
|
|
36
|
+
* trying the given extensions. Handles stdlib modules that live inside
|
|
37
|
+
* node_modules/agency-lang/stdlib/ when running from an external project.
|
|
38
|
+
*/
|
|
39
|
+
resolveModulePath(moduleId, extensions) {
|
|
40
|
+
// Try the path as-is first (works when running from the agency-lang repo)
|
|
41
|
+
for (const ext of extensions) {
|
|
42
|
+
const candidate = moduleId.replace(/\.agency$/, ext);
|
|
43
|
+
if (fs.existsSync(candidate))
|
|
44
|
+
return candidate;
|
|
37
45
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
// If the moduleId refers to a stdlib file, resolve against the actual
|
|
47
|
+
// stdlib directory (handles npm-installed packages where stdlib lives
|
|
48
|
+
// in node_modules/agency-lang/stdlib/)
|
|
49
|
+
if (moduleId.includes("stdlib")) {
|
|
50
|
+
const basename = path.basename(moduleId);
|
|
51
|
+
for (const ext of extensions) {
|
|
52
|
+
const candidate = path.join(getStdlibDir(), basename).replace(/\.agency$/, ext);
|
|
53
|
+
if (fs.existsSync(candidate))
|
|
54
|
+
return candidate;
|
|
55
|
+
}
|
|
41
56
|
}
|
|
42
57
|
return null;
|
|
43
58
|
}
|
|
59
|
+
agencyToJsFile(agencyFile) {
|
|
60
|
+
return this.resolveModulePath(agencyFile, [".js", ".ts"]);
|
|
61
|
+
}
|
|
44
62
|
async setCheckpoint(checkpoint) {
|
|
45
63
|
const cp = Checkpoint.fromJSON(checkpoint);
|
|
46
64
|
if (!cp) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
2
|
import { findPackageRoot, resolveAgencyImportPath, getStdlibDir, toCompiledImportPath, isPkgImport, isAgencyImport, parsePkgImport, resolvePkgAgencyPath, } from "./importPaths.js";
|
|
3
|
+
import { CompileStrategy, RunStrategy } from "./importStrategy.js";
|
|
3
4
|
import { buildSymbolTable } from "./symbolTable.js";
|
|
4
5
|
import * as fs from "fs";
|
|
5
6
|
import * as os from "os";
|
|
@@ -235,3 +236,73 @@ describe("buildSymbolTable with pkg:: imports", () => {
|
|
|
235
236
|
}
|
|
236
237
|
});
|
|
237
238
|
});
|
|
239
|
+
describe("CompileStrategy", () => {
|
|
240
|
+
const jsStrategy = new CompileStrategy({ targetExt: ".js" });
|
|
241
|
+
const tsStrategy = new CompileStrategy({ targetExt: ".ts" });
|
|
242
|
+
it("should rewrite .agency to .js", () => {
|
|
243
|
+
expect(jsStrategy.rewriteImport("./foo.agency", "/src/main.agency")).toBe("./foo.js");
|
|
244
|
+
});
|
|
245
|
+
it("should rewrite .agency to .ts with --ts", () => {
|
|
246
|
+
expect(tsStrategy.rewriteImport("./foo.agency", "/src/main.agency")).toBe("./foo.ts");
|
|
247
|
+
});
|
|
248
|
+
it("should leave .js imports untouched", () => {
|
|
249
|
+
expect(jsStrategy.rewriteImport("./tools.js", "/src/main.agency")).toBe("./tools.js");
|
|
250
|
+
});
|
|
251
|
+
it("should leave .ts imports untouched", () => {
|
|
252
|
+
expect(jsStrategy.rewriteImport("./tools.ts", "/src/main.agency")).toBe("./tools.ts");
|
|
253
|
+
});
|
|
254
|
+
it("should leave bare specifiers untouched", () => {
|
|
255
|
+
expect(jsStrategy.rewriteImport("nanoid", "/src/main.agency")).toBe("nanoid");
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
describe("RunStrategy", () => {
|
|
259
|
+
const strategy = new RunStrategy();
|
|
260
|
+
it("should rewrite .agency to .js", () => {
|
|
261
|
+
expect(strategy.rewriteImport("./foo.agency", "/src/main.agency")).toBe("./foo.js");
|
|
262
|
+
});
|
|
263
|
+
it("should rewrite .ts to .js", () => {
|
|
264
|
+
expect(strategy.rewriteImport("./tools.ts", "/src/main.agency")).toBe("./tools.js");
|
|
265
|
+
});
|
|
266
|
+
it("should leave .js imports as-is", () => {
|
|
267
|
+
expect(strategy.rewriteImport("./tools.js", "/src/main.agency")).toBe("./tools.js");
|
|
268
|
+
});
|
|
269
|
+
it("should leave bare specifiers untouched", () => {
|
|
270
|
+
expect(strategy.rewriteImport("nanoid", "/src/main.agency")).toBe("nanoid");
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
describe("RunStrategy.prepareDependencies", () => {
|
|
274
|
+
let tmpDir;
|
|
275
|
+
const strategy = new RunStrategy();
|
|
276
|
+
beforeEach(() => {
|
|
277
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agency-run-strategy-"));
|
|
278
|
+
});
|
|
279
|
+
afterEach(() => {
|
|
280
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
281
|
+
});
|
|
282
|
+
it("should compile .ts to .js when .js doesn't exist", () => {
|
|
283
|
+
const tsFile = path.join(tmpDir, "bar.ts");
|
|
284
|
+
const jsFile = path.join(tmpDir, "bar.js");
|
|
285
|
+
fs.writeFileSync(tsFile, "export const x: number = 1;");
|
|
286
|
+
const fromFile = path.join(tmpDir, "main.agency");
|
|
287
|
+
strategy.prepareDependencies(["./bar.js"], fromFile);
|
|
288
|
+
expect(fs.existsSync(jsFile)).toBe(true);
|
|
289
|
+
const content = fs.readFileSync(jsFile, "utf-8");
|
|
290
|
+
expect(content).toContain("const x = 1");
|
|
291
|
+
});
|
|
292
|
+
it("should not overwrite existing .js files", () => {
|
|
293
|
+
const jsFile = path.join(tmpDir, "bar.js");
|
|
294
|
+
fs.writeFileSync(jsFile, "// original");
|
|
295
|
+
const fromFile = path.join(tmpDir, "main.agency");
|
|
296
|
+
strategy.prepareDependencies(["./bar.js"], fromFile);
|
|
297
|
+
expect(fs.readFileSync(jsFile, "utf-8")).toBe("// original");
|
|
298
|
+
});
|
|
299
|
+
it("should throw when neither .js nor .ts exists", () => {
|
|
300
|
+
const fromFile = path.join(tmpDir, "main.agency");
|
|
301
|
+
expect(() => strategy.prepareDependencies(["./bar.js"], fromFile)).toThrow(/Cannot resolve import/);
|
|
302
|
+
});
|
|
303
|
+
it("should skip bare specifiers", () => {
|
|
304
|
+
const fromFile = path.join(tmpDir, "main.agency");
|
|
305
|
+
// Should not throw — bare specifiers are skipped
|
|
306
|
+
strategy.prepareDependencies(["nanoid"], fromFile);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type CompileOptions = {
|
|
2
|
+
/** Extension for .agency rewrites: ".js" or ".ts" */
|
|
3
|
+
targetExt: ".js" | ".ts";
|
|
4
|
+
};
|
|
5
|
+
export interface ImportStrategy {
|
|
6
|
+
/**
|
|
7
|
+
* Rewrite an import path for the output.
|
|
8
|
+
* Handles .agency, .js, and .ts imports.
|
|
9
|
+
*/
|
|
10
|
+
rewriteImport(modulePath: string, sourceFile: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Ensure all non-Agency dependencies are available for execution.
|
|
13
|
+
* Called after compilation, before the output is executed.
|
|
14
|
+
* Errors if a dependency can't be resolved.
|
|
15
|
+
*/
|
|
16
|
+
prepareDependencies(imports: string[], sourceFile: string): void;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Strategy for `agency compile`: produces output for a downstream build pipeline.
|
|
20
|
+
* Leaves .js/.ts imports untouched. Only rewrites .agency imports.
|
|
21
|
+
*/
|
|
22
|
+
export declare class CompileStrategy implements ImportStrategy {
|
|
23
|
+
protected options: CompileOptions;
|
|
24
|
+
constructor(options: CompileOptions);
|
|
25
|
+
rewriteImport(modulePath: string, _sourceFile: string): string;
|
|
26
|
+
prepareDependencies(_imports: string[], _sourceFile: string): void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Strategy for `agency run` / `agency debug` / `agency test`: compiles and
|
|
30
|
+
* immediately executes. All imports must resolve to .js files that exist on disk.
|
|
31
|
+
* Compiles .ts dependencies to .js via esbuild when needed.
|
|
32
|
+
*/
|
|
33
|
+
export declare class RunStrategy extends CompileStrategy {
|
|
34
|
+
constructor();
|
|
35
|
+
rewriteImport(modulePath: string, sourceFile: string): string;
|
|
36
|
+
prepareDependencies(imports: string[], sourceFile: string): void;
|
|
37
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { transformSync } from "esbuild";
|
|
4
|
+
/**
|
|
5
|
+
* Strategy for `agency compile`: produces output for a downstream build pipeline.
|
|
6
|
+
* Leaves .js/.ts imports untouched. Only rewrites .agency imports.
|
|
7
|
+
*/
|
|
8
|
+
export class CompileStrategy {
|
|
9
|
+
options;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
}
|
|
13
|
+
rewriteImport(modulePath, _sourceFile) {
|
|
14
|
+
if (modulePath.endsWith(".agency")) {
|
|
15
|
+
return modulePath.replace(/\.agency$/, this.options.targetExt);
|
|
16
|
+
}
|
|
17
|
+
return modulePath;
|
|
18
|
+
}
|
|
19
|
+
prepareDependencies(_imports, _sourceFile) {
|
|
20
|
+
// No-op — user's build pipeline handles dependencies
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Strategy for `agency run` / `agency debug` / `agency test`: compiles and
|
|
25
|
+
* immediately executes. All imports must resolve to .js files that exist on disk.
|
|
26
|
+
* Compiles .ts dependencies to .js via esbuild when needed.
|
|
27
|
+
*/
|
|
28
|
+
export class RunStrategy extends CompileStrategy {
|
|
29
|
+
constructor() {
|
|
30
|
+
super({ targetExt: ".js" });
|
|
31
|
+
}
|
|
32
|
+
rewriteImport(modulePath, sourceFile) {
|
|
33
|
+
if (modulePath.endsWith(".agency")) {
|
|
34
|
+
return super.rewriteImport(modulePath, sourceFile);
|
|
35
|
+
}
|
|
36
|
+
// Always produce .js — Node needs .js at runtime
|
|
37
|
+
return modulePath.replace(/\.ts$/, ".js");
|
|
38
|
+
}
|
|
39
|
+
prepareDependencies(imports, sourceFile) {
|
|
40
|
+
for (const imp of imports) {
|
|
41
|
+
if (!imp.startsWith("./") && !imp.startsWith("../"))
|
|
42
|
+
continue;
|
|
43
|
+
if (!imp.endsWith(".js"))
|
|
44
|
+
continue;
|
|
45
|
+
const resolved = path.resolve(path.dirname(sourceFile), imp);
|
|
46
|
+
if (fs.existsSync(resolved))
|
|
47
|
+
continue;
|
|
48
|
+
const tsPath = resolved.replace(/\.js$/, ".ts");
|
|
49
|
+
if (fs.existsSync(tsPath)) {
|
|
50
|
+
const tsCode = fs.readFileSync(tsPath, "utf-8");
|
|
51
|
+
const result = transformSync(tsCode, {
|
|
52
|
+
loader: "ts",
|
|
53
|
+
format: "esm",
|
|
54
|
+
supported: { "top-level-await": true },
|
|
55
|
+
});
|
|
56
|
+
fs.writeFileSync(resolved, result.code);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
throw new Error(`Cannot resolve import '${imp}' from '${sourceFile}'.\n` +
|
|
60
|
+
`Tried: ${resolved}, ${tsPath} — neither file exists.`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
package/dist/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.0.
|
|
1
|
+
export declare const VERSION = "0.0.99";
|
package/dist/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.0.
|
|
1
|
+
export const VERSION = "0.0.99";
|
package/dist/scripts/agency.js
CHANGED
|
@@ -339,6 +339,7 @@ program
|
|
|
339
339
|
.option("--rewind-size <n>", "Rolling checkpoint window size", "30")
|
|
340
340
|
.option("--trace <file>", "Load and inspect a trace file")
|
|
341
341
|
.option("--checkpoint <file>", "Load and inspect a checkpoint file")
|
|
342
|
+
.option("--dist-dir <dir>", "Import pre-compiled JS from this directory instead of compiling on the fly")
|
|
342
343
|
.action(async (file, options) => {
|
|
343
344
|
const config = getConfig();
|
|
344
345
|
await debug(config, file, {
|
|
@@ -346,6 +347,7 @@ program
|
|
|
346
347
|
rewindSize: parseInt(options.rewindSize, 10),
|
|
347
348
|
trace: options.trace,
|
|
348
349
|
checkpoint: options.checkpoint,
|
|
350
|
+
distDir: options.distDir,
|
|
349
351
|
});
|
|
350
352
|
});
|
|
351
353
|
program
|