agency-lang 0.0.98 → 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 +16 -24
- package/dist/lib/cli/debug.js +4 -9
- package/dist/lib/cli/util.d.ts +5 -0
- package/dist/lib/cli/util.js +35 -24
- 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.d.ts +0 -6
- package/dist/lib/importPaths.js +0 -23
- package/dist/lib/importPaths.test.js +55 -33
- 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/package.json +1 -1
- package/stdlib/array.js +1808 -1777
- package/stdlib/clipboard.js +230 -57
- package/stdlib/http.js +670 -0
- package/stdlib/index.agency +2 -3
- package/stdlib/index.js +1872 -1856
- package/stdlib/{_builtins.js → lib/builtins.js} +1 -1
- package/stdlib/lib/ui.js +1 -1
- package/stdlib/speech.js +249 -62
- package/stdlib/ui.js +1604 -0
- package/stdlib/weather.js +587 -0
- package/stdlib/{retry.js → wikipedia.js} +208 -147
- package/stdlib/_clipboard.js +0 -29
- package/stdlib/_math.js +0 -9
- package/stdlib/_speech.js +0 -69
- package/stdlib/_system.js +0 -29
- package/stdlib/consensus.js +0 -359
- package/stdlib/firstValid.js +0 -384
- package/stdlib/sample.js +0 -350
|
@@ -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
|
@@ -8,7 +8,8 @@ import { spawn } from "child_process";
|
|
|
8
8
|
import { transformSync } from "esbuild";
|
|
9
9
|
import * as fs from "fs";
|
|
10
10
|
import * as path from "path";
|
|
11
|
-
import { getStdlibDir, isPkgImport, isStdlibImport, resolveAgencyImportPath,
|
|
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,33 +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
147
|
if (node.type !== "importStatement")
|
|
142
148
|
return;
|
|
143
149
|
if (isStdlibImport(node.modulePath) || isPkgImport(node.modulePath))
|
|
144
150
|
return;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// For .js/.ts imports, resolve flexibly: if the specified file doesn't
|
|
150
|
-
// exist, try the other extension. This lets users write .js imports that
|
|
151
|
-
// work in dist/ while the debugger finds the .ts source (and vice versa).
|
|
152
|
-
if (node.modulePath.endsWith(".js") || node.modulePath.endsWith(".ts")) {
|
|
153
|
-
const resolved = resolveFlexibleExtension(node.modulePath, absoluteInputFile);
|
|
154
|
-
if (resolved === null) {
|
|
155
|
-
const altExt = node.modulePath.endsWith(".js") ? ".ts" : ".js";
|
|
156
|
-
const altPath = node.modulePath.replace(/\.(js|ts)$/, altExt);
|
|
157
|
-
console.error(`Error: Cannot resolve import '${node.modulePath}' from '${inputFile}'.\n` +
|
|
158
|
-
`Tried: ${node.modulePath}, ${altPath} — neither file exists.`);
|
|
159
|
-
process.exit(1);
|
|
160
|
-
}
|
|
161
|
-
// Rewrite the import to use whatever extension actually exists on disk
|
|
162
|
-
const resolvedExt = path.extname(resolved);
|
|
163
|
-
node.modulePath = node.modulePath.replace(/\.(js|ts)$/, resolvedExt);
|
|
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);
|
|
164
155
|
}
|
|
165
156
|
});
|
|
157
|
+
strategy.prepareDependencies(nonAgencyImports, absoluteInputFile);
|
|
166
158
|
const moduleId = path.relative(process.cwd(), absoluteInputFile);
|
|
167
159
|
const generatedCode = generateTypeScript(resolvedProgram, config, info, moduleId);
|
|
168
160
|
if (options?.ts) {
|
|
@@ -182,8 +174,8 @@ export function compile(config, inputFile, _outputFile, options) {
|
|
|
182
174
|
return outputFile;
|
|
183
175
|
}
|
|
184
176
|
export function run(config, inputFile, outputFile, resumeFile) {
|
|
185
|
-
// Compile the file
|
|
186
|
-
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() });
|
|
187
179
|
if (output === null) {
|
|
188
180
|
console.error("Error: No output file generated.");
|
|
189
181
|
process.exit(1);
|
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";
|
|
@@ -82,13 +83,7 @@ export async function debug(config, _inputFile, options = {}) {
|
|
|
82
83
|
let absOutput;
|
|
83
84
|
if (distDir) {
|
|
84
85
|
// distDir mode: import pre-compiled JS from the dist directory
|
|
85
|
-
const
|
|
86
|
-
const compiledPath = path.resolve(distDir, basename);
|
|
87
|
-
if (!fs.existsSync(compiledPath)) {
|
|
88
|
-
console.error(`Error: Compiled file not found: ${compiledPath}\n` +
|
|
89
|
-
`Make sure you have compiled your Agency files and that distDir is correct.`);
|
|
90
|
-
process.exit(1);
|
|
91
|
-
}
|
|
86
|
+
const compiledPath = resolveCompiledFile(distDir, inputFile);
|
|
92
87
|
// Warn if source is newer than compiled output
|
|
93
88
|
const sourceMtime = fs.statSync(inputFile).mtimeMs;
|
|
94
89
|
const compiledMtime = fs.statSync(compiledPath).mtimeMs;
|
|
@@ -101,7 +96,7 @@ export async function debug(config, _inputFile, options = {}) {
|
|
|
101
96
|
else {
|
|
102
97
|
// Normal mode: compile the .agency file to .js on the fly
|
|
103
98
|
const debugConfig = { ...config, debugger: true };
|
|
104
|
-
const outputFile = compile(debugConfig, inputFile);
|
|
99
|
+
const outputFile = compile(debugConfig, inputFile, undefined, { importStrategy: new RunStrategy() });
|
|
105
100
|
if (outputFile === null) {
|
|
106
101
|
console.error("Error: No output file generated.");
|
|
107
102
|
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,28 +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;
|
|
108
|
-
let
|
|
122
|
+
let compiledPath;
|
|
109
123
|
if (distDir) {
|
|
110
|
-
|
|
111
|
-
const basename = path.basename(agencyFile, ".agency") + ".js";
|
|
112
|
-
const compiledPath = path.resolve(distDir, basename);
|
|
113
|
-
if (!fs.existsSync(compiledPath)) {
|
|
114
|
-
throw new Error(`Compiled file not found: ${compiledPath}\n` +
|
|
115
|
-
`Make sure you have compiled your Agency files and that distDir is correct.`);
|
|
116
|
-
}
|
|
117
|
-
compiledFilename = compiledPath;
|
|
124
|
+
compiledPath = resolveCompiledFile(distDir, agencyFile);
|
|
118
125
|
}
|
|
119
126
|
else {
|
|
120
|
-
compile(config, agencyFile);
|
|
121
|
-
compiledFilename = path.basename(agencyFile).replace(".agency", ".js");
|
|
127
|
+
compiledPath = compile(config, agencyFile, undefined, { importStrategy: new RunStrategy() });
|
|
122
128
|
}
|
|
123
129
|
const baseName = agencyFile.replace(".agency", "");
|
|
124
130
|
const evaluateFile = `${baseName}.evaluate.js`;
|
|
125
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
|
+
}
|
|
126
138
|
const evaluateScript = renderEvaluate({
|
|
127
|
-
filename:
|
|
139
|
+
filename: importSpecifier,
|
|
128
140
|
nodeName,
|
|
129
141
|
hasArgs,
|
|
130
142
|
args: argsString,
|
|
@@ -153,22 +165,22 @@ export async function executeNodeAsync({ config, agencyFile, nodeName, hasArgs,
|
|
|
153
165
|
}
|
|
154
166
|
export function executeNode(args) {
|
|
155
167
|
const distDir = args.config.distDir;
|
|
156
|
-
let
|
|
168
|
+
let compiledPath;
|
|
157
169
|
if (distDir) {
|
|
158
|
-
|
|
159
|
-
const compiledPath = path.resolve(distDir, basename);
|
|
160
|
-
if (!fs.existsSync(compiledPath)) {
|
|
161
|
-
throw new Error(`Compiled file not found: ${compiledPath}\n` +
|
|
162
|
-
`Make sure you have compiled your Agency files and that distDir is correct.`);
|
|
163
|
-
}
|
|
164
|
-
compiledFilename = compiledPath;
|
|
170
|
+
compiledPath = resolveCompiledFile(distDir, args.agencyFile);
|
|
165
171
|
}
|
|
166
172
|
else {
|
|
167
|
-
compile(args.config, args.agencyFile);
|
|
168
|
-
|
|
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}`;
|
|
169
181
|
}
|
|
170
182
|
const evaluateScript = renderEvaluate({
|
|
171
|
-
filename:
|
|
183
|
+
filename: importSpecifier,
|
|
172
184
|
nodeName: args.nodeName,
|
|
173
185
|
hasArgs: args.hasArgs,
|
|
174
186
|
args: args.argsString,
|
|
@@ -178,7 +190,6 @@ export function executeNode(args) {
|
|
|
178
190
|
: undefined,
|
|
179
191
|
resultsFilename: "__evaluate.json",
|
|
180
192
|
});
|
|
181
|
-
const evaluateFile = "__evaluate.js";
|
|
182
193
|
fs.writeFileSync(evaluateFile, evaluateScript);
|
|
183
194
|
execFileSync("node", [evaluateFile], { stdio: "inherit" });
|
|
184
195
|
const results = readFileSync("__evaluate.json", "utf-8");
|
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) {
|
|
@@ -64,12 +64,6 @@ export declare function resolvePkgAgencyPath(importPath: string, fromFile: strin
|
|
|
64
64
|
* - "./foo.js" -> resolved relative to the importing file (non-agency, kept as-is)
|
|
65
65
|
*/
|
|
66
66
|
export declare function resolveAgencyImportPath(importPath: string, fromFile: string): string;
|
|
67
|
-
/**
|
|
68
|
-
* Resolve a .js or .ts import path, trying the other extension if the
|
|
69
|
-
* specified file doesn't exist. Returns the resolved absolute path, or
|
|
70
|
-
* null if neither extension exists on disk.
|
|
71
|
-
*/
|
|
72
|
-
export declare function resolveFlexibleExtension(importPath: string, fromFile: string): string | null;
|
|
73
67
|
/**
|
|
74
68
|
* Convert an Agency import path to the path that should appear in generated
|
|
75
69
|
* TypeScript import statements.
|
package/dist/lib/importPaths.js
CHANGED
|
@@ -216,29 +216,6 @@ export function resolveAgencyImportPath(importPath, fromFile) {
|
|
|
216
216
|
// Relative or other imports: resolve against the importing file's directory
|
|
217
217
|
return path.resolve(path.dirname(fromFile), importPath);
|
|
218
218
|
}
|
|
219
|
-
const FLEXIBLE_EXTENSIONS = {
|
|
220
|
-
".js": ".ts",
|
|
221
|
-
".ts": ".js",
|
|
222
|
-
};
|
|
223
|
-
/**
|
|
224
|
-
* Resolve a .js or .ts import path, trying the other extension if the
|
|
225
|
-
* specified file doesn't exist. Returns the resolved absolute path, or
|
|
226
|
-
* null if neither extension exists on disk.
|
|
227
|
-
*/
|
|
228
|
-
export function resolveFlexibleExtension(importPath, fromFile) {
|
|
229
|
-
const ext = path.extname(importPath);
|
|
230
|
-
const altExt = FLEXIBLE_EXTENSIONS[ext];
|
|
231
|
-
if (!altExt)
|
|
232
|
-
return null; // not a .js/.ts import
|
|
233
|
-
const dir = path.dirname(fromFile);
|
|
234
|
-
const resolved = path.resolve(dir, importPath);
|
|
235
|
-
if (fs.existsSync(resolved))
|
|
236
|
-
return resolved;
|
|
237
|
-
const altPath = resolved.slice(0, -ext.length) + altExt;
|
|
238
|
-
if (fs.existsSync(altPath))
|
|
239
|
-
return altPath;
|
|
240
|
-
return null;
|
|
241
|
-
}
|
|
242
219
|
/**
|
|
243
220
|
* Convert an Agency import path to the path that should appear in generated
|
|
244
221
|
* TypeScript import statements.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
-
import { findPackageRoot, resolveAgencyImportPath,
|
|
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,52 +236,73 @@ describe("buildSymbolTable with pkg:: imports", () => {
|
|
|
235
236
|
}
|
|
236
237
|
});
|
|
237
238
|
});
|
|
238
|
-
describe("
|
|
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", () => {
|
|
239
274
|
let tmpDir;
|
|
275
|
+
const strategy = new RunStrategy();
|
|
240
276
|
beforeEach(() => {
|
|
241
|
-
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agency-
|
|
277
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agency-run-strategy-"));
|
|
242
278
|
});
|
|
243
279
|
afterEach(() => {
|
|
244
280
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
245
281
|
});
|
|
246
|
-
it("should
|
|
282
|
+
it("should compile .ts to .js when .js doesn't exist", () => {
|
|
247
283
|
const tsFile = path.join(tmpDir, "bar.ts");
|
|
248
|
-
fs.writeFileSync(tsFile, "export const x = 1;");
|
|
249
|
-
const fromFile = path.join(tmpDir, "main.agency");
|
|
250
|
-
const result = resolveFlexibleExtension("./bar.ts", fromFile);
|
|
251
|
-
expect(result).toBe(tsFile);
|
|
252
|
-
});
|
|
253
|
-
it("should fall back from .js to .ts when .js doesn't exist", () => {
|
|
254
|
-
const tsFile = path.join(tmpDir, "bar.ts");
|
|
255
|
-
fs.writeFileSync(tsFile, "export const x = 1;");
|
|
256
|
-
const fromFile = path.join(tmpDir, "main.agency");
|
|
257
|
-
const result = resolveFlexibleExtension("./bar.js", fromFile);
|
|
258
|
-
expect(result).toBe(tsFile);
|
|
259
|
-
});
|
|
260
|
-
it("should fall back from .ts to .js when .ts doesn't exist", () => {
|
|
261
284
|
const jsFile = path.join(tmpDir, "bar.js");
|
|
262
|
-
fs.writeFileSync(
|
|
285
|
+
fs.writeFileSync(tsFile, "export const x: number = 1;");
|
|
263
286
|
const fromFile = path.join(tmpDir, "main.agency");
|
|
264
|
-
|
|
265
|
-
expect(
|
|
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");
|
|
266
291
|
});
|
|
267
|
-
it("should
|
|
292
|
+
it("should not overwrite existing .js files", () => {
|
|
293
|
+
const jsFile = path.join(tmpDir, "bar.js");
|
|
294
|
+
fs.writeFileSync(jsFile, "// original");
|
|
268
295
|
const fromFile = path.join(tmpDir, "main.agency");
|
|
269
|
-
|
|
270
|
-
expect(
|
|
296
|
+
strategy.prepareDependencies(["./bar.js"], fromFile);
|
|
297
|
+
expect(fs.readFileSync(jsFile, "utf-8")).toBe("// original");
|
|
271
298
|
});
|
|
272
|
-
it("should
|
|
299
|
+
it("should throw when neither .js nor .ts exists", () => {
|
|
273
300
|
const fromFile = path.join(tmpDir, "main.agency");
|
|
274
|
-
|
|
275
|
-
expect(result).toBeNull();
|
|
301
|
+
expect(() => strategy.prepareDependencies(["./bar.js"], fromFile)).toThrow(/Cannot resolve import/);
|
|
276
302
|
});
|
|
277
|
-
it("should
|
|
278
|
-
const jsFile = path.join(tmpDir, "bar.js");
|
|
279
|
-
const tsFile = path.join(tmpDir, "bar.ts");
|
|
280
|
-
fs.writeFileSync(jsFile, "export const x = 1;");
|
|
281
|
-
fs.writeFileSync(tsFile, "export const x = 1;");
|
|
303
|
+
it("should skip bare specifiers", () => {
|
|
282
304
|
const fromFile = path.join(tmpDir, "main.agency");
|
|
283
|
-
|
|
284
|
-
|
|
305
|
+
// Should not throw — bare specifiers are skipped
|
|
306
|
+
strategy.prepareDependencies(["nanoid"], fromFile);
|
|
285
307
|
});
|
|
286
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";
|