Skip to content

Commit 51c234b

Browse files
committed
refactor: use cliffy for command line interfaces
1 parent 9daafd2 commit 51c234b

File tree

14 files changed

+211
-123
lines changed

14 files changed

+211
-123
lines changed

β€Ždzx.tsβ€Ž

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,12 @@
11
/// <reference path="./types.d.ts" />
2-
import { $, io, path } from "./mod.ts";
3-
import { bundle } from "./src/bundle.ts";
4-
import { compile } from "./src/compile.ts";
5-
import { error } from "./src/_utils.ts";
62

7-
if (import.meta.main) {
8-
if (Deno.args[0] === "bundle") {
9-
const script = Deno.args[Deno.args.length - 1];
10-
console.log(
11-
await bundle(script, new URL("./mod.ts", import.meta.url).href),
12-
);
13-
Deno.exit(0);
14-
} else if (Deno.args[0] === "compile") {
15-
const args = [...Deno.args];
16-
args.shift();
17-
const script = args.pop();
18-
if (!script) {
19-
throw error(`usage: dzx compile <script>`);
20-
}
21-
await compile(
22-
script,
23-
args,
24-
new URL("./mod.ts", import.meta.url).href,
25-
);
26-
Deno.exit(0);
27-
}
3+
import { dzx } from "./src/cli/mod.ts";
284

29-
const script = Deno.args[Deno.args.length - 1];
30-
31-
try {
32-
if (!script) {
33-
if (!Deno.isatty(Deno.stdin.rid)) {
34-
const data = new TextDecoder().decode(await io.readAll(Deno.stdin));
35-
if (data) {
36-
await import(
37-
`data:application/typescript,${encodeURIComponent(data)}`
38-
);
39-
} else {
40-
error(`usage: dzx <script>`, 2);
41-
}
42-
} else {
43-
error(`usage: dzx <script>`);
44-
}
45-
} else if (
46-
script.startsWith("http://") || script.startsWith("https://") ||
47-
script.startsWith("file://")
48-
) {
49-
await import(script);
50-
} else if (script) {
51-
await import("file://" + path.join($.cwd, script));
52-
} else {
53-
error(`usage: dzx <script>`);
54-
}
55-
} catch (err) {
56-
error(err);
5+
if (import.meta.main) {
6+
const start = performance.now();
7+
await dzx().parse(Deno.args);
8+
if ($.verbose) {
9+
const end = performance.now();
10+
console.log($.bold("time: %ss"), Math.round(end - start) / 1000);
5711
}
5812
}

β€Žmod.tsβ€Ž

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1 @@
1-
import { colors, flags, fs, io, log, path, shq } from "./deps.ts";
2-
import { cd } from "./src/cd.ts";
3-
import { exec } from "./src/exec.ts";
4-
import { quote } from "./src/quote.ts";
5-
6-
export type $ = typeof exec & typeof colors & {
7-
verbose: boolean;
8-
cwd: string;
9-
shell: string;
10-
quote: typeof shq;
11-
throwErors: boolean;
12-
};
13-
14-
export const $: $ = exec as $;
15-
16-
Object.setPrototypeOf($, Object.getPrototypeOf(colors));
17-
18-
$._stack = [];
19-
$.shell = "/bin/sh";
20-
$.verbose = false;
21-
$.cwd = Deno.cwd();
22-
$.quote = shq;
23-
$.throwErors = false;
24-
25-
// dzx
26-
window.$ = $;
27-
window.cd = cd;
28-
window.quote = quote;
29-
30-
// x
31-
window.path = path;
32-
window.io = io;
33-
window.fs = fs;
34-
window.log = log;
35-
window.flags = flags;
36-
37-
export { cd, flags, fs, io, log, path, quote };
1+
export * from "./src/runtime/mod.ts";

β€Žsrc/bundle.tsβ€Ž renamed to β€Žsrc/cli/bundle.tsβ€Ž

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,37 @@
1-
import { error } from "./_utils.ts";
1+
import { Command, ValidationError } from "./deps.ts";
2+
import { error } from "../_utils.ts";
3+
import { fs, path } from "../../mod.ts";
4+
5+
export function bundleCommand() {
6+
return new Command<void>()
7+
.description("Bundle an dzx script to a standalone deno sript.")
8+
.arguments("[script:string]")
9+
.option<{ check: boolean }>("--no-check", "Skip type checking modules.")
10+
.action(async ({ check }, script?: string) => {
11+
if (!script) {
12+
if (Deno.isatty(Deno.stdin.rid)) {
13+
throw new ValidationError(`Missing argument(s): script`);
14+
}
15+
script = await Deno.makeTempFile({ suffix: ".ts" });
16+
const tmpFile = await Deno.open(script, { write: true });
17+
await Deno.copy(Deno.stdin, tmpFile);
18+
tmpFile.close();
19+
}
20+
console.log("Deno.mainModule:", Deno.mainModule);
21+
console.log(
22+
await bundle(script, {
23+
check,
24+
}),
25+
);
26+
});
27+
}
228

329
export async function bundle(
430
script: string,
5-
dzxModuleUrl: string,
631
options: Deno.EmitOptions = {},
732
): Promise<string> {
833
const { shebang, tmpFile } = await prepareBundle(
934
script,
10-
dzxModuleUrl,
1135
options,
1236
);
1337

@@ -24,7 +48,6 @@ export async function bundle(
2448

2549
export async function prepareBundle(
2650
script: string,
27-
dzxModuleUrl: string,
2851
options: Deno.EmitOptions = {},
2952
): Promise<{ shebang: string; tmpFile: string }> {
3053
if (!await fs.exists(script)) {
@@ -42,7 +65,8 @@ export async function prepareBundle(
4265
check: true,
4366
...options,
4467
});
45-
const bundleContent = `import "${dzxModuleUrl}";\n` +
68+
const bundleContent =
69+
`import "${new URL("./mod.ts", Deno.mainModule).href}";\n` +
4670
Object.values(scriptResult.files)[0] as string;
4771

4872
const tmpDir = await Deno.makeTempDir();

β€Žsrc/cli/compile.tsβ€Ž

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { prepareBundle } from "./bundle.ts";
2+
import { error } from "../_utils.ts";
3+
import { Command, ValidationError } from "./deps.ts";
4+
5+
export function compileCommand() {
6+
return new Command<void>()
7+
.description("Combile an dzx script to a standalone binary.")
8+
.arguments("[script:string] [compile-options...:string[]]")
9+
.option("-A, --allow-all", "Allow all permissions.")
10+
.option("--allow-env [allow-env:string]", "Allow environment access.")
11+
.option("--allow-hrtime", "Allow high resolution time measurement.")
12+
.option("--allow-net [allow-net:string]", "Allow network access.")
13+
.option("--allow-plugin", "Allow loading plugins.")
14+
.option(
15+
"--allow-read [allow-read:string]",
16+
"Allow file system read access.",
17+
)
18+
.option(
19+
"--allow-run [allow-run:string]",
20+
"Allow running subprocesses.",
21+
)
22+
.option(
23+
"--allow-write [allow-write:string]",
24+
"Allow file system write access.",
25+
)
26+
.option("--no-check", "Skip type checking modules.")
27+
.option("--lite", "Use lite deno runtime.")
28+
.option("--unstable", "Enable unstable features and APIs of Deno.")
29+
.useRawArgs()
30+
.action(
31+
async function (_: void, script?: string, ...args: Array<string>) {
32+
if (script && args[0]?.[0] === "-") {
33+
args = [script, ...args];
34+
script = args.pop();
35+
}
36+
if (!script) {
37+
if (Deno.isatty(Deno.stdin.rid)) {
38+
throw new ValidationError(`Missing argument(s): script`);
39+
}
40+
script = await Deno.makeTempFile();
41+
const tmpFile = await Deno.open(script);
42+
await Deno.copy(Deno.stdin, tmpFile);
43+
}
44+
if (["-h", "--help"].includes(script)) {
45+
this.showHelp();
46+
} else {
47+
await compile(
48+
script,
49+
args,
50+
);
51+
}
52+
},
53+
);
54+
}
55+
56+
export async function compile(
57+
script: string,
58+
args: Array<string>,
59+
): Promise<void> {
60+
const { tmpFile } = await prepareBundle(script);
61+
62+
const p = Deno.run({
63+
cmd: [
64+
"deno",
65+
"compile",
66+
"--no-check",
67+
...args.filter((arg) => arg !== "--no-check"),
68+
tmpFile,
69+
],
70+
});
71+
72+
const { success } = await p.status();
73+
p.close();
74+
75+
if (!success) {
76+
error(`Failed to compile: ${script}`);
77+
}
78+
}

β€Žsrc/cli/deps.tsβ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export {
2+
Command,
3+
CompletionsCommand,
4+
HelpCommand,
5+
ValidationError,
6+
} from "https://deno.land/x/[email protected]/command/mod.ts";

β€Žsrc/cli/mod.tsβ€Ž

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { io, path } from "../../mod.ts";
2+
import { bundleCommand } from "./bundle.ts";
3+
import { compileCommand } from "./compile.ts";
4+
import {
5+
Command,
6+
CompletionsCommand,
7+
HelpCommand,
8+
ValidationError,
9+
} from "./deps.ts";
10+
11+
export function dzx() {
12+
return new Command<void>()
13+
.version("0.2.0")
14+
.name("dzx")
15+
.description("πŸ¦• A custom deno runtime for writing quickly scripts.")
16+
.stopEarly()
17+
.arguments<[script?: string, args?: Array<string>]>(
18+
"[script:string] [args...:string[]]",
19+
)
20+
.action(async (_, script?: string, _args?: Array<string>) => {
21+
if (!script) {
22+
if (!Deno.isatty(Deno.stdin.rid)) {
23+
const data = new TextDecoder().decode(await io.readAll(Deno.stdin));
24+
if (data) {
25+
await import(
26+
`data:application/typescript,${encodeURIComponent(data)}`
27+
);
28+
} else {
29+
// @TODO: add support for exit code in ValidationError
30+
// throw new ValidationError(`Failed to read from stdin.`, 2);
31+
throw new ValidationError(`Failed to read from stdin.`);
32+
}
33+
} else {
34+
throw new ValidationError(`Missing argument(s): script`);
35+
}
36+
} else if (
37+
script.startsWith("http://") || script.startsWith("https://") ||
38+
script.startsWith("file://")
39+
) {
40+
await import(script);
41+
} else {
42+
await import(
43+
"file://" +
44+
(path.isAbsolute(script) ? script : path.join(Deno.cwd(), script))
45+
);
46+
}
47+
})
48+
.command("bundle", bundleCommand())
49+
.command("compile", compileCommand())
50+
.command("help", new HelpCommand().global())
51+
.command("completions", new CompletionsCommand());
52+
}

β€Žsrc/compile.tsβ€Ž

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

β€Žsrc/cd.tsβ€Ž renamed to β€Žsrc/runtime/cd.tsβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { error } from "./_utils.ts";
1+
import { error } from "../_utils.ts";
22

33
export function cd(path: string) {
44
if ($.verbose) {
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
Β (0)