@kaathewise/ssg 0.6.0 → 0.7.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAKzC,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,iBA4BzC"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAIzC,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwBzD"}
package/dist/build.js CHANGED
@@ -1,13 +1,11 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import * as path from "node:path";
3
- import { Glob, write } from "bun";
4
3
  import { renderAll } from "./render.js";
5
- const glob = new Glob("**/*.{js,jsx,ts,tsx}");
4
+ import { walk } from "./utils.js";
6
5
  export async function build(config) {
7
6
  const pages = [];
8
- for await (const relPath of glob.scan(config.pagesDir)) {
9
- const modulePath = path.join(config.pagesDir, relPath);
10
- const p = await renderAll(modulePath, config.pagesDir);
7
+ for await (const filePath of walk(config.pagesDir)) {
8
+ const p = await renderAll(filePath, config.pagesDir);
11
9
  pages.push(...p);
12
10
  }
13
11
  await fs.rm(config.outputDir, { recursive: true, force: true });
@@ -15,13 +13,10 @@ export async function build(config) {
15
13
  for (const page of pages) {
16
14
  const destPath = path.join(config.outputDir, page.path);
17
15
  const dir = path.dirname(destPath);
18
- if (!(await fs.exists(dir))) {
19
- await fs.mkdir(path.dirname(destPath), {
20
- recursive: true,
21
- });
22
- }
23
- const file = Bun.file(destPath);
24
- await write(file, page.src);
16
+ await fs.mkdir(path.dirname(destPath), {
17
+ recursive: true,
18
+ });
19
+ await fs.writeFile(destPath, page.src);
25
20
  }
26
21
  if (config.assetDir) {
27
22
  await fs.cp(config.assetDir, config.outputDir, {
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,MAAM,GAAG;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;CACb,UAaA"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,MAAM,GAAG;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACZ,CAAA;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;CACb,GAAG,MAAM,CAaT"}
package/dist/render.d.ts CHANGED
@@ -1,6 +1,4 @@
1
- interface Params {
2
- [name: string]: string;
3
- }
1
+ import type { Params } from "./router.ts";
4
2
  export type Page = {
5
3
  path: string;
6
4
  src: string;
@@ -8,5 +6,4 @@ export type Page = {
8
6
  };
9
7
  export declare function render(modulePath: string, pagesDir: string, params?: Params): Promise<Page>;
10
8
  export declare function renderAll(modulePath: string, pagesDir: string): Promise<Page[]>;
11
- export {};
12
9
  //# sourceMappingURL=render.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAEA,UAAU,MAAM;IACf,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CACtB;AAED,MAAM,MAAM,IAAI,GAAG;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAAA;AAED,wBAAsB,MAAM,CAC3B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAsB,SAAS,CAC9B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,EAAE,CAAC,CAgBjB"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEzC,MAAM,MAAM,IAAI,GAAG;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1B,CAAA;AAED,wBAAsB,MAAM,CAC3B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,MAAW,GACjB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAsB,SAAS,CAC9B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,EAAE,CAAC,CAgBjB"}
package/dist/render.js CHANGED
@@ -53,10 +53,14 @@ export async function renderAll(modulePath, pagesDir) {
53
53
  function substituteParams(inputPath, params) {
54
54
  let path = inputPath;
55
55
  for (const [key, value] of Object.entries(params)) {
56
- const single = `[${key}]`;
57
- const multiple = `[[...${key}]]`;
58
- path = path.replace(single, value);
59
- path = path.replace(multiple, value);
56
+ const single = `:${key}`;
57
+ const multiple = `*${key}`;
58
+ if (typeof value === "string") {
59
+ path = path.replace(single, value);
60
+ }
61
+ else {
62
+ path = path.replace(multiple, value.join("/"));
63
+ }
60
64
  }
61
65
  path = path.replace(/\.tsx?$/, "");
62
66
  return path;
@@ -0,0 +1,22 @@
1
+ export type Match = {
2
+ filePath: string;
3
+ params: Params;
4
+ };
5
+ export type Params = Record<string, string | string[]>;
6
+ export declare class Router {
7
+ #private;
8
+ constructor(root: string, paths: Path[]);
9
+ static new(dir: string): Promise<Router>;
10
+ match(pathname: string): Match | null;
11
+ reload(): Promise<void>;
12
+ }
13
+ type Path = {
14
+ filePath: string;
15
+ fragments: Fragment[];
16
+ };
17
+ type Fragment = {
18
+ kind: "literal" | "segment" | "repeat";
19
+ value: string;
20
+ };
21
+ export {};
22
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,KAAK,GAAG;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAA;AAEtD,qBAAa,MAAM;;gBAIN,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE;WAK1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAW9C,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,IAAI;IAe/B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAI7B;AAED,KAAK,IAAI,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,QAAQ,EAAE,CAAA;CACrB,CAAA;AA+CD,KAAK,QAAQ,GAAG;IACf,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;IACtC,KAAK,EAAE,MAAM,CAAA;CACb,CAAA"}
package/dist/router.js ADDED
@@ -0,0 +1,90 @@
1
+ import path from "node:path";
2
+ import { walk } from "./utils.js";
3
+ export class Router {
4
+ #root;
5
+ #paths;
6
+ constructor(root, paths) {
7
+ this.#root = root;
8
+ this.#paths = paths;
9
+ }
10
+ static async new(dir) {
11
+ const paths = [];
12
+ for await (const pagePath of walk(dir)) {
13
+ const rel = path.relative(dir, pagePath);
14
+ paths.push(toPath(rel));
15
+ }
16
+ return new Router(dir, paths);
17
+ }
18
+ match(pathname) {
19
+ for (const pagePath of this.#paths) {
20
+ const match = matchPath(pagePath, pathname);
21
+ if (match) {
22
+ match.filePath = path.join(this.#root, match.filePath);
23
+ return match;
24
+ }
25
+ }
26
+ return null;
27
+ }
28
+ async reload() {
29
+ const replacement = await Router.new(this.#root);
30
+ this.#paths = replacement.#paths;
31
+ }
32
+ }
33
+ function toPath(pagePath) {
34
+ return {
35
+ filePath: pagePath,
36
+ fragments: toFragments(pagePath),
37
+ };
38
+ }
39
+ function matchPath(pagePath, pathname) {
40
+ const params = {};
41
+ const parts = pathname.split("/").slice(1);
42
+ for (const [i, fragment] of pagePath.fragments.entries()) {
43
+ const part = parts[i];
44
+ if (!part) {
45
+ if (fragment.value === "index") {
46
+ break;
47
+ }
48
+ else {
49
+ return null;
50
+ }
51
+ }
52
+ switch (fragment.kind) {
53
+ case "literal": {
54
+ if (fragment.value !== part) {
55
+ return null;
56
+ }
57
+ break;
58
+ }
59
+ case "segment": {
60
+ params[fragment.value] = part;
61
+ break;
62
+ }
63
+ case "repeat": {
64
+ params[fragment.value] = parts.slice(i);
65
+ break;
66
+ }
67
+ }
68
+ }
69
+ return {
70
+ filePath: pagePath.filePath,
71
+ params,
72
+ };
73
+ }
74
+ function toFragments(pagePath) {
75
+ const barePath = pagePath.replace(/\.tsx?/, "");
76
+ const pieces = barePath.split("/");
77
+ const out = [];
78
+ for (const piece of pieces) {
79
+ if (piece.startsWith(":")) {
80
+ out.push({ kind: "segment", value: piece.slice(1) });
81
+ }
82
+ else if (piece.startsWith("*")) {
83
+ out.push({ kind: "repeat", value: piece.slice(1) });
84
+ }
85
+ else {
86
+ out.push({ kind: "literal", value: piece });
87
+ }
88
+ }
89
+ return out;
90
+ }
package/dist/run.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  export {};
3
3
  //# sourceMappingURL=run.d.ts.map
package/dist/run.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env bun
1
+ #!/usr/bin/env node
2
2
  var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
3
3
  if (typeof path === "string" && /^\.\.?\//.test(path)) {
4
4
  return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
@@ -7,8 +7,10 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
7
7
  }
8
8
  return path;
9
9
  };
10
+ import console from "node:console";
10
11
  import * as fs from "node:fs/promises";
11
12
  import * as path from "node:path";
13
+ import process from "node:process";
12
14
  import { build, serve } from "./index.js";
13
15
  const CONFIG_PATH = "ssg.config.ts";
14
16
  const HELP = `Usage: ssg <command>
@@ -19,7 +21,10 @@ Commands:
19
21
  `;
20
22
  const args = process.argv.slice(2);
21
23
  const configPath = path.resolve(CONFIG_PATH);
22
- if (!(await fs.exists(configPath))) {
24
+ try {
25
+ fs.access(configPath);
26
+ }
27
+ catch {
23
28
  console.warn("Configuration file not found");
24
29
  process.exit(10);
25
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAKzC,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA+CzD"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAMzC,wBAAsB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqCzD"}
package/dist/server.js CHANGED
@@ -1,82 +1,75 @@
1
1
  import * as fs from "node:fs/promises";
2
2
  import { watch } from "node:fs/promises";
3
+ import http from "node:http";
3
4
  import * as path from "node:path";
5
+ import url from "node:url";
4
6
  import { render } from "./render.js";
7
+ import { Router } from "./router.js";
5
8
  const EVENT_PATH = "/__ssg_dev_sse";
6
9
  export async function serve(config) {
7
10
  const clients = new Set();
8
- const router = new Bun.FileSystemRouter({
9
- style: "nextjs",
10
- dir: config.pagesDir,
11
- });
12
- Bun.serve({
13
- port: config.port,
14
- // for SSE
15
- idleTimeout: 0,
16
- fetch: async (request) => {
17
- const url = new URL(request.url);
18
- if (url.pathname === EVENT_PATH) {
19
- return createStream(request, clients);
20
- }
21
- const route = router.match(url.href);
22
- if (route) {
23
- return createHtml(config.pagesDir, route);
24
- }
25
- const asset = await fetchStaticFile(url, config.assetDir);
26
- if (asset) {
27
- return asset;
28
- }
29
- console.warn(`Path '${url.pathname}' not found`);
30
- return new Response("Page or file not found", {
31
- status: 404,
32
- });
33
- },
11
+ const router = await Router.new(config.pagesDir);
12
+ const server = http.createServer(async (request, response) => {
13
+ const rUrl = new url.URL(request.url ?? "/", "http://localhost");
14
+ if (rUrl.pathname === EVENT_PATH) {
15
+ createStream(response, clients);
16
+ return;
17
+ }
18
+ const route = router.match(rUrl.pathname);
19
+ if (route) {
20
+ await serveHtml(response, route, config.pagesDir);
21
+ return;
22
+ }
23
+ if (await fetchStaticFile(response, rUrl, config.assetDir)) {
24
+ return;
25
+ }
26
+ console.warn(`Path '${rUrl.pathname}' not found`);
27
+ response.writeHead(404);
28
+ response.end("Page or file not found");
34
29
  });
30
+ server.listen(config.port);
35
31
  console.log(`Listening on :${config.port}`);
36
32
  const watcher = watch(config.sourceDir, { recursive: true });
37
33
  for await (const _ of watcher) {
38
- router.reload();
39
- clearCache(config.sourceDir);
34
+ await router.reload();
40
35
  for (const client of clients) {
41
- client.enqueue("data: RELOAD\n\n");
36
+ client.write("data: RELOAD\n\n");
42
37
  }
43
38
  }
44
39
  }
45
- function createStream(request, clients) {
46
- const stream = new ReadableStream({
47
- start(controller) {
48
- clients.add(controller);
49
- // workaround because @ts-expect-error doesn't fail for
50
- // aspartik/website for some reason, breaking the check.
51
- // biome-ignore lint/suspicious/noExplicitAny: above
52
- const signal = request.signal;
53
- signal.addEventListener("abort", () => {
54
- controller.close();
55
- clients.delete(controller);
56
- });
57
- },
40
+ function createStream(response, clients) {
41
+ response.writeHead(200, {
42
+ "Content-Type": "text/event-stream",
43
+ "Cache-Control": "no-cache",
44
+ Connection: "keep-alive",
58
45
  });
59
- return new Response(stream, {
60
- headers: {
61
- "Content-Type": "text/event-stream",
62
- "Cache-Control": "no-cache",
63
- },
46
+ clients.add(response);
47
+ response.on("close", () => {
48
+ clients.delete(response);
49
+ response.end();
64
50
  });
65
51
  }
66
- async function fetchStaticFile(url, assetDir) {
52
+ async function fetchStaticFile(response, url, assetDir) {
67
53
  if (!assetDir) {
68
- return null;
54
+ return false;
69
55
  }
70
56
  let assetPath = path.join(assetDir, url.pathname.slice(1));
71
57
  assetPath = path.resolve(assetPath);
72
58
  if (!assetPath.startsWith(assetDir)) {
73
- return new Response("Tried to get a file outside of the asset directory", { status: 403 });
59
+ response.writeHead(403);
60
+ response.end("Tried to get a file outside of the asset directory");
61
+ return true;
62
+ }
63
+ try {
64
+ const contents = await fs.readFile(assetPath);
65
+ response.writeHead(200);
66
+ response.end(contents);
67
+ return true;
74
68
  }
75
- const exists = await fs.exists(assetPath);
76
- if (!exists) {
77
- return null;
69
+ catch {
70
+ // the file doesn't exist
71
+ return false;
78
72
  }
79
- return new Response(Bun.file(assetPath));
80
73
  }
81
74
  const RELOAD_SCRIPT = `
82
75
  <script type="module">
@@ -89,25 +82,13 @@ const RELOAD_SCRIPT = `
89
82
  window.addEventListener("beforeunload", () => sse.close())
90
83
  </script>
91
84
  `;
92
- async function createHtml(pagesDir, route) {
85
+ async function serveHtml(response, route, pagesDir) {
93
86
  const page = await render(route.filePath, pagesDir, route.params);
94
- const response = new Response(page.src, {
95
- headers: {
96
- "Content-Type": page.contentType ?? "text/html",
97
- },
98
- });
99
- return new HTMLRewriter()
100
- .onDocument({
101
- end: (el) => {
102
- el.append(RELOAD_SCRIPT, { html: true });
103
- },
104
- })
105
- .transform(response);
106
- }
107
- function clearCache(prefix) {
108
- for (const path in import.meta.require.cache) {
109
- if (path.startsWith(prefix)) {
110
- delete import.meta.require.cache[path];
111
- }
87
+ if (page.contentType === "text/html" || !page.contentType) {
88
+ page.src += RELOAD_SCRIPT;
112
89
  }
90
+ response.writeHead(200, {
91
+ "Content-Type": page.contentType ?? "text/html",
92
+ });
93
+ response.end(page.src);
113
94
  }
@@ -0,0 +1,2 @@
1
+ export declare function walk(dir: string): AsyncGenerator<string>;
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAGA,wBAAuB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAS/D"}
package/dist/utils.js ADDED
@@ -0,0 +1,13 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ export async function* walk(dir) {
4
+ for await (const d of await fs.opendir(dir)) {
5
+ const entry = path.join(dir, d.name);
6
+ if (d.isDirectory()) {
7
+ yield* walk(entry);
8
+ }
9
+ else if (d.isFile()) {
10
+ yield entry;
11
+ }
12
+ }
13
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kaathewise/ssg",
3
3
  "description": "JSX static site generator",
4
- "version": "0.6.0",
4
+ "version": "0.7.0",
5
5
  "license": "MPL-2.0",
6
6
  "author": "Andrej Kolčin",
7
7
  "type": "module",
@@ -11,18 +11,17 @@
11
11
  },
12
12
  "scripts": {
13
13
  "check": "biome check && tsc --noEmit",
14
- "build": "tsc --project tsconfig.build.json"
14
+ "build": "tsc"
15
15
  },
16
16
  "main": "dist/index.ts",
17
17
  "bin": {
18
- "ssg": "src/run.ts"
18
+ "ssg": "dist/run.js"
19
19
  },
20
20
  "files": [
21
21
  "dist"
22
22
  ],
23
23
  "devDependencies": {
24
24
  "@biomejs/biome": "^2.2.3",
25
- "@types/bun": "^1.2.21",
26
25
  "@types/node": "^24.3.1",
27
26
  "typescript": "^5.9.2",
28
27
  "typescript-language-server": "^4.4.0"
package/src/run.ts DELETED
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env bun
2
-
3
- import * as fs from "node:fs/promises"
4
- import * as path from "node:path"
5
- import { build, type Config, serve } from "./index.ts"
6
-
7
- const CONFIG_PATH = "ssg.config.ts"
8
- const HELP = `Usage: ssg <command>
9
-
10
- Commands:
11
- build: build the website
12
- dev: start a hot-reloading development server
13
- `
14
-
15
- const args = process.argv.slice(2)
16
-
17
- const configPath = path.resolve(CONFIG_PATH)
18
- if (!(await fs.exists(configPath))) {
19
- console.warn("Configuration file not found")
20
- process.exit(10)
21
- }
22
-
23
- const configModule = await import(configPath)
24
- const config = configModule.default as Config
25
-
26
- switch (args[0]) {
27
- case "build": {
28
- await build(config)
29
- break
30
- }
31
- case "dev": {
32
- await serve(config)
33
- break
34
- }
35
- case undefined: {
36
- console.log(HELP)
37
- break
38
- }
39
- default: {
40
- console.log(
41
- `The command must be either 'build' or 'dev', got ${args[0]}`,
42
- )
43
- process.exit(11)
44
- }
45
- }