Skip to content

Commit 2f36f23

Browse files
authored
feat: Introduce HttpProgramResolver, for resolving recipe dependencies over HTTP (#1772)
1 parent fe7fb56 commit 2f36f23

File tree

11 files changed

+97
-47
lines changed

11 files changed

+97
-47
lines changed

packages/cli/lib/charm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { CharmsController } from "@commontools/charm/ops";
1919
import { join } from "@std/path";
2020
import { isVNode } from "@commontools/html";
21-
import { FileSystemProgramResolver } from "@commontools/js-runtime/deno";
21+
import { FileSystemProgramResolver } from "@commontools/js-runtime";
2222

2323
export interface EntryConfig {
2424
mainPath: string;

packages/cli/lib/dev.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type JsScript } from "@commontools/js-runtime";
2-
import { FileSystemProgramResolver } from "@commontools/js-runtime/deno";
2+
import { FileSystemProgramResolver } from "@commontools/js-runtime";
33
import { Identity } from "@commontools/identity";
44
import { Engine, Runtime } from "@commontools/runner";
55
import { basename } from "@std/path";

packages/js-runtime/deno.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
},
1010
"exports": {
1111
".": "./mod.ts",
12-
"./typescript": "./typescript/mod.ts",
13-
"./deno": "./deno.ts"
12+
"./typescript": "./typescript/mod.ts"
1413
},
1514
"fmt": {
1615
"exclude": ["test/fixtures/"]

packages/js-runtime/deno.ts

Lines changed: 0 additions & 35 deletions
This file was deleted.

packages/js-runtime/interface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export interface Compiler<T> {
4848
// A program's entry point with a resolver to
4949
// resolve other sources used in the program.
5050
export interface ProgramResolver {
51-
main(): Source;
51+
main(): Promise<Source>;
5252
resolveSource(identifier: string): Promise<Source | undefined>;
5353
}
5454

packages/js-runtime/mod.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,9 @@ export {
1919
UnsafeEvalJsValue,
2020
UnsafeEvalRuntime,
2121
} from "./runtime/mod.ts";
22-
export { InMemoryProgram } from "./program.ts";
22+
export {
23+
FileSystemProgramResolver,
24+
HttpProgramResolver,
25+
InMemoryProgram,
26+
} from "./program.ts";
2327
export { getTypeScriptEnvironmentTypes } from "./utils.ts";

packages/js-runtime/program.ts

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { isDeno } from "@commontools/utils/env";
12
import { ProgramResolver, Source } from "./interface.ts";
3+
import { dirname, join } from "@std/path";
24

35
export class InMemoryProgram implements ProgramResolver {
46
private modules: Record<string, string>;
@@ -8,12 +10,12 @@ export class InMemoryProgram implements ProgramResolver {
810
this._main = main;
911
}
1012

11-
main(): Source {
13+
main(): Promise<Source> {
1214
const main = this.modules[this._main];
1315
if (!main) {
1416
throw new Error(`${this._main} not in modules.`);
1517
}
16-
return { name: this._main, contents: main };
18+
return Promise.resolve({ name: this._main, contents: main });
1719
}
1820

1921
resolveSource(identifier: string): Promise<Source | undefined> {
@@ -22,3 +24,83 @@ export class InMemoryProgram implements ProgramResolver {
2224
return Promise.resolve({ contents, name: identifier });
2325
}
2426
}
27+
28+
// Resolve a program using the file system.
29+
// Deno-only.
30+
export class FileSystemProgramResolver implements ProgramResolver {
31+
private fsRoot: string;
32+
private _main: Source;
33+
constructor(mainPath: string) {
34+
this.fsRoot = dirname(mainPath);
35+
this._main = {
36+
name: mainPath.substring(this.fsRoot.length),
37+
contents: this.#readFile(mainPath),
38+
};
39+
}
40+
41+
main(): Promise<Source> {
42+
return Promise.resolve(this._main);
43+
}
44+
45+
resolveSource(specifier: string): Promise<Source | undefined> {
46+
if (!specifier || specifier[0] !== "/") {
47+
return Promise.resolve(undefined);
48+
}
49+
const absPath = join(
50+
this.fsRoot,
51+
specifier.substring(1, specifier.length),
52+
);
53+
return Promise.resolve({
54+
name: specifier,
55+
contents: this.#readFile(absPath),
56+
});
57+
}
58+
59+
#readFile(path: string): string {
60+
if (!isDeno()) {
61+
throw new Error(
62+
"FileSystemProgramResolver is not supported in this environment.",
63+
);
64+
}
65+
return Deno.readTextFileSync(path);
66+
}
67+
}
68+
69+
// Resolve a program from HTTP.
70+
export class HttpProgramResolver implements ProgramResolver {
71+
#httpRoot: string;
72+
#mainUrl: URL;
73+
#main?: Promise<Source>;
74+
constructor(main: string | URL) {
75+
this.#mainUrl = !(main instanceof URL) ? new URL(main) : main;
76+
this.#httpRoot = dirname(this.#mainUrl.pathname);
77+
}
78+
79+
main(): Promise<Source> {
80+
if (!this.#main) {
81+
this.#main = this.#fetch(this.#mainUrl);
82+
}
83+
return this.#main;
84+
}
85+
86+
resolveSource(specifier: string): Promise<Source | undefined> {
87+
if (!specifier || specifier[0] !== "/") {
88+
return Promise.resolve(undefined);
89+
}
90+
const url = new URL(this.#mainUrl);
91+
url.pathname = join(
92+
this.#httpRoot,
93+
specifier.substring(1, specifier.length),
94+
);
95+
return this.#fetch(url);
96+
}
97+
98+
async #fetch(url: URL): Promise<Source> {
99+
const res = await fetch(url);
100+
const contents = await res.text();
101+
return {
102+
name: url.pathname.substring(this.#httpRoot.length),
103+
contents,
104+
};
105+
}
106+
}

packages/js-runtime/typescript/resolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export async function resolveProgram(
1818
{ unresolvedModules, target, resolveUnresolvedModuleTypes }:
1919
ResolveModuleConfig,
2020
): Promise<Program> {
21-
const main = graph.main();
21+
const main = await graph.main();
2222
const sources = new Map([[main.name, main]]);
2323
const toProcess = [main.name];
2424
const processed: string[] = [];

packages/patterns/integration/all.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { afterAll, beforeAll, describe, it } from "@std/testing/bdd";
44
import { join } from "@std/path";
55
import { assert } from "@std/assert";
66
import { Identity } from "@commontools/identity";
7-
import { FileSystemProgramResolver } from "@commontools/js-runtime/deno";
7+
import { FileSystemProgramResolver } from "@commontools/js-runtime";
88
import { RuntimeProgram } from "@commontools/runner";
99

1010
const { API_URL, SPACE_NAME } = env;

packages/patterns/integration/counter.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { afterAll, beforeAll, describe, it } from "@std/testing/bdd";
55
import { join } from "@std/path";
66
import { assert, assertEquals } from "@std/assert";
77
import { Identity } from "@commontools/identity";
8-
import { FileSystemProgramResolver } from "@commontools/js-runtime/deno";
8+
import { FileSystemProgramResolver } from "@commontools/js-runtime";
99

1010
const { API_URL, FRONTEND_URL, SPACE_NAME } = env;
1111

0 commit comments

Comments
 (0)