Skip to content

Commit c86b667

Browse files
authored
refactor(cli): refactor runtime bootstrapping (#22)
1 parent 5850a4d commit c86b667

File tree

9 files changed

+150
-64
lines changed

9 files changed

+150
-64
lines changed

examples/basic.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/// <reference path="https://deno.land/x/[email protected]/types.d.ts" />
1+
/// <reference path="../types.d.ts" />
22

33
const helloWorld = await $`echo 'Hello World!'`;
44
console.log(`${$.blue("helloWorld")} result was %o`, helloWorld);

src/cli/bundle.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Command, copy, ValidationError } from "./deps.ts";
22
import { error } from "../_utils.ts";
33
import { path } from "../runtime/mod.ts";
4+
import { bootstrapModule } from "./lib/bootstrap.ts";
45

56
export function bundleCommand() {
67
return new Command<void>()
@@ -42,16 +43,11 @@ export async function preBundle(
4243
script: string,
4344
options?: Deno.EmitOptions,
4445
): Promise<string> {
45-
const scriptResult = await bundleFile(script, options);
46-
47-
const bundleContent = `import "${new URL("./mod.ts", Deno.mainModule).href}";
48-
$.mainModule = import.meta.url;
49-
$.args = Deno.args;
50-
${scriptResult}
51-
if ($.verbose) {
52-
const end = Date.now();
53-
console.log($.bold("time: %ss"), Math.round($.time) / 1000);
54-
}`;
46+
const bundleContent = bootstrapModule({
47+
mainModule: "import.meta.url",
48+
args: "Deno.args",
49+
code: await bundleFile(script, options),
50+
});
5551

5652
const tmpDir = await Deno.makeTempDir();
5753
const tmpFile = path.join(tmpDir, path.basename(script));

src/cli/lib/bootstrap.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { $ } from "../../runtime/mod.ts";
2+
3+
export interface BootstrapOptions {
4+
startTime?: number;
5+
mainModule?: string;
6+
args?: Array<string> | string;
7+
base64?: boolean;
8+
}
9+
10+
export function base64Module(code: string) {
11+
return `data:application/typescript,${encodeURIComponent(code)}`;
12+
}
13+
14+
export function stringifyArgs(args: Array<string>) {
15+
return args?.length
16+
? `$.args = JSON.parse(decodeURIComponent("${
17+
encodeURIComponent(JSON.stringify(args))
18+
}"));`
19+
: "";
20+
}
21+
22+
export function bootstrap(options: BootstrapOptions): string {
23+
const code = [
24+
`import "${new URL("../../../mod.ts", import.meta.url)}";`,
25+
options.startTime ? `$.startTime = ${options.startTime};` : "",
26+
options.mainModule ? `$.mainModule = ${options.mainModule};` : "",
27+
typeof options.args === "string"
28+
? options.args
29+
: options.args
30+
? stringifyArgs(options.args)
31+
: "",
32+
].filter((line) => line).join("\n");
33+
34+
return options.base64 ? base64Module(code) : code;
35+
}
36+
37+
export function teardown(): string {
38+
return [
39+
`if ($.verbose) {`,
40+
` console.log($.bold("time: %ss"), Math.round($.time) / 1000);`,
41+
`}`,
42+
`self.close();`,
43+
].join("\n");
44+
}
45+
46+
export interface BootstrapModuleOptions extends BootstrapOptions {
47+
code?: string;
48+
}
49+
50+
export function bootstrapScript(code: string) {
51+
return base64Module(`
52+
/// <reference path="${new URL(
53+
"../../../types.d.ts",
54+
import.meta.url,
55+
)}" />
56+
{\n${code}\n}
57+
`);
58+
}
59+
60+
export function bootstrapModule(options: BootstrapModuleOptions) {
61+
return [
62+
bootstrap(options),
63+
options.code ? `{\n${options.code}\n}` : "",
64+
teardown(),
65+
].filter((line) => line).join("\n");
66+
}
67+
68+
export interface ImportModuleOptions {
69+
mainModule: string;
70+
args?: Array<string>;
71+
}
72+
73+
export async function importModule(options: ImportModuleOptions) {
74+
$.mainModule = options.mainModule;
75+
if (options.args) {
76+
$.args = options.args;
77+
}
78+
await import($.mainModule);
79+
if ($.verbose) {
80+
console.log($.bold("time: %ss"), Math.round($.time) / 1000);
81+
}
82+
}

src/cli/markdown.ts renamed to src/cli/lib/markdown.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { tokens } from "https://deno.land/x/[email protected]/mod.ts";
2-
import { addProtocol } from "../_utils.ts";
2+
import { addProtocol } from "../../_utils.ts";
33

44
export async function getMarkdownModule(url: string) {
55
let mdContent;
@@ -34,15 +34,16 @@ export async function getMarkdownModule(url: string) {
3434
}
3535
});
3636

37-
const finalCode = codeContent.join("")
37+
const code = codeContent.join("")
3838
.replaceAll("import.meta.url", `\"${url}\"`);
3939

4040
return `data:application/typescript,${
4141
encodeURIComponent(`
42-
/// <reference path="${new URL("./types.d.ts", Deno.mainModule)}" />
43-
{
44-
${finalCode}
45-
}
42+
/// <reference path="${new URL(
43+
"../../../types.d.ts",
44+
import.meta.url,
45+
)}" />
46+
{\n${code}\n}
4647
`)
4748
}`;
4849
}

src/cli/lib/stream.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ValidationError } from "../deps.ts";
2+
import { bootstrapScript } from "./bootstrap.ts";
3+
4+
export async function getModuleFromStdin(): Promise<string> {
5+
const code = new TextDecoder().decode(await streams.readAll(Deno.stdin));
6+
if (!code) {
7+
throw new ValidationError(`Failed to read from stdin.`, { exitCode: 2 });
8+
}
9+
return bootstrapScript(code);
10+
}

src/cli/worker.ts renamed to src/cli/lib/worker.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { $ } from "../runtime/mod.ts";
1+
import { bootstrapModule, BootstrapOptions } from "./bootstrap.ts";
22

33
export interface Permissions {
44
allowAll?: boolean;
@@ -11,24 +11,26 @@ export interface Permissions {
1111
allowRead?: boolean | (string | URL)[];
1212
}
1313

14-
export function spawnWorker(perms: Permissions): void {
14+
export interface SpawnWorkerOptions extends Omit<BootstrapOptions, "base64"> {
15+
perms: Permissions;
16+
}
17+
18+
export function spawnWorker({
19+
args,
20+
startTime,
21+
mainModule,
22+
perms,
23+
}: SpawnWorkerOptions): void {
1524
new Worker(
16-
`data:application/typescript,${
17-
encodeURIComponent(`
18-
import "${new URL("./src/runtime/mod.ts", Deno.mainModule)}";
19-
$.mainModule = "${$.mainModule}";
20-
$.startTime = ${$.startTime};
21-
$.args = JSON.parse(decodeURIComponent("${
22-
encodeURIComponent(JSON.stringify($.args))
23-
}"));
24-
await import("${$.mainModule}");
25-
if ($.verbose) {
26-
console.log($.bold("time: %ss"), Math.round($.time) / 1000);
27-
}
28-
self.close();`)
29-
}`,
25+
bootstrapModule({
26+
args: args,
27+
startTime: startTime,
28+
mainModule: mainModule,
29+
base64: true,
30+
code: `await import("${mainModule}");`,
31+
}),
3032
{
31-
name: $.mainModule,
33+
name: mainModule,
3234
type: "module",
3335
deno: {
3436
namespace: true,

src/cli/mod.ts

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import {
99
ValidationError,
1010
} from "./deps.ts";
1111
import { addProtocol } from "../_utils.ts";
12-
import { getMarkdownModule } from "./markdown.ts";
13-
import { spawnWorker } from "./worker.ts";
12+
import { importModule } from "./lib/bootstrap.ts";
13+
import { getModuleFromStdin } from "./lib/stream.ts";
14+
import { getMarkdownModule } from "./lib/markdown.ts";
15+
import { spawnWorker } from "./lib/worker.ts";
1416
import { replCommand } from "./repl.ts";
1517

1618
export function dzx() {
@@ -72,26 +74,29 @@ export function dzx() {
7274
script?: string,
7375
args: Array<string> = [],
7476
) => {
75-
$.args = args;
77+
if (!script && Deno.isatty(Deno.stdin.rid)) {
78+
throw new ValidationError(`Missing argument(s): script`);
79+
}
80+
81+
let mainModule: string;
7682
if (script) {
7783
const scriptExt = path.extname(script);
78-
79-
$.mainModule = [".md", ".markdown"].includes(scriptExt)
84+
mainModule = [".md", ".markdown"].includes(scriptExt)
8085
? await getMarkdownModule(script)
8186
: addProtocol(script);
82-
83-
if (worker) {
84-
spawnWorker(perms);
85-
} else {
86-
await import($.mainModule);
87-
}
88-
} else if (Deno.isatty(Deno.stdin.rid)) {
89-
throw new ValidationError(`Missing argument(s): script`);
9087
} else {
91-
await importFromStdin();
88+
mainModule = await getModuleFromStdin();
9289
}
93-
if ($.verbose) {
94-
console.log($.bold("time: %ss"), Math.round($.time) / 1000);
90+
91+
if (worker) {
92+
spawnWorker({
93+
perms,
94+
mainModule,
95+
args,
96+
startTime: $.startTime,
97+
});
98+
} else {
99+
await importModule({ mainModule, args });
95100
}
96101
},
97102
)
@@ -105,14 +110,4 @@ export function dzx() {
105110
provider: new DenoLandProvider(),
106111
}),
107112
);
108-
109-
async function importFromStdin(): Promise<void> {
110-
const data = new TextDecoder().decode(await streams.readAll(Deno.stdin));
111-
if (data) {
112-
$.mainModule = `data:application/typescript,${encodeURIComponent(data)}`;
113-
await import($.mainModule);
114-
} else {
115-
throw new ValidationError(`Failed to read from stdin.`, { exitCode: 2 });
116-
}
117-
}
118113
}

src/cli/repl.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ export function replCommand() {
99
"Node compatibility mode. Currently only enables built-in node modules like 'fs' and globals like 'process'",
1010
)
1111
.option(
12-
"--inspect=<HOST:PORT>",
12+
"--inspect=<host:string>",
1313
"Activate inspector on host:port (default: 127.0.0.1:9229)",
1414
)
1515
.option(
16-
"--inspect-brk=<HOST:PORT>",
16+
"--inspect-brk=<host:string>",
1717
"Activate inspector on host:port and break at start of user script",
1818
)
1919
.option("--no-check", "Skip type checking modules.")
@@ -38,7 +38,7 @@ export async function repl(
3838
"repl",
3939
"--unstable", // dzx requires unstable
4040
"--eval",
41-
`import * as dzx from "${new URL("../../mod.ts", import.meta.url)}"`,
41+
`import "${new URL("../../mod.ts", import.meta.url)}"`,
4242
...args.filter((e) => e !== "--eval"), // we already use eval, and it can only be used once
4343
],
4444
});

src/runtime/mod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ Object.setPrototypeOf($, Object.getPrototypeOf(colors));
4242
$._stack = [];
4343
$.shell = "/bin/bash";
4444
$.prefix = "set -euo pipefail;";
45-
$.mainModule = "";
45+
$.mainModule = Deno.mainModule;
4646
$.verbose = false;
4747
$.stdout = "piped";
4848
$.stderr = "piped";

0 commit comments

Comments
 (0)