@dirwell/cli 0.0.1-alpha → 0.0.1-alpha.1

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/README.md CHANGED
@@ -1,19 +1,84 @@
1
1
  # @dirwell/cli
2
2
 
3
- Unreleased alpha workspace. Install a theme separately; no default theme is selected.
3
+ Build a static file explorer or serve a live folder. Plain is included as the
4
+ minimal default: no browser JavaScript, framework, fonts or share images.
5
+ Other themes remain separate installs.
6
+
7
+ Starting with `0.0.1-alpha.1`, the CLI defaults to Plain and supports short theme
8
+ names and automatic `dirwell.config.mjs` discovery. Version `0.0.1-alpha` requires an explicit
9
+ `--theme '@dirwell/theme-plain#createPlainTheme'` and a project-installed theme.
10
+
11
+ ## Open or publish a folder
12
+
13
+ ```sh
14
+ npx @dirwell/cli@alpha server ./files
15
+ npx @dirwell/cli@alpha build ./files --out ./generated
16
+ ```
17
+
18
+ No project, configuration file or global installation is required. Open the URL
19
+ printed by `server`; Ctrl+C stops it. New filesystem data appears on subsequent
20
+ requests. `build` replaces a dedicated static output tree: keep unrelated files
21
+ out of that directory. Both commands require an explicit source folder.
22
+
23
+ Server defaults to loopback, an ephemeral port and safe media previews. HTML,
24
+ SVG and other active formats download. Use `--port`, `--host` and `--mount`
25
+ explicitly when needed. `PORT` is accepted for proxies such as Portless. Port precedence is `--port`, then
26
+ `PORT`, then `config.server.port`, then an ephemeral port. Config can also set
27
+ `server.host`; `--host` overrides it.
28
+ `--live` enables automatic Svelte/Vue updates through shared SSE notifications;
29
+ Plain remains script-free. Server mode does not generate a tree or need a
30
+ filesystem watcher. Theme-source HMR belongs to the optional authoring package.
31
+
32
+ ## Select or customize a theme
33
+
34
+ For repeated project use, install the CLI and the theme you want. Both this CLI
35
+ and the compatibility package expose `dirwell`; replace the old CLI dependency
36
+ instead of installing both in the same project. Keep compatibility API consumers
37
+ on their existing line until they are migrated:
4
38
 
5
39
  ```sh
6
- npm install @dirwell/cli@alpha @dirwell/theme-plain@alpha
7
- npx dirwell build ./files --out ./generated --theme '@dirwell/theme-plain#createPlainTheme'
8
- npx dirwell server ./files --theme '@dirwell/theme-plain#createPlainTheme'
40
+ npm install --save-dev @dirwell/cli@alpha @dirwell/theme-ledger@alpha
41
+ npx dirwell server ./files --theme ledger
9
42
  ```
10
43
 
11
- `--live` enables shared SSE revision notifications in Svelte/Vue clients. Plain remains script-free.
44
+ `--theme plain`, `--theme ledger` and `--theme crosswave` select the official
45
+ factories. Custom packages or local modules use `--theme 'module#factory'`, or a
46
+ module's default theme/factory export. Resolution prefers the working project,
47
+ then packages installed beside the CLI by `npm exec --package=...`. The CLI
48
+ never downloads a theme itself or falls back to Plain when an explicit theme fails.
12
49
 
13
- `build` writes a dedicated static output tree. `server` renders HTML and reads listings/files live without generating a tree or using a watcher. It defaults to loopback, an ephemeral port, and safe media previews; HTML, SVG and other files download. Use `--port`, `--host` and `--mount` explicitly when needed. `PORT` is accepted for development proxies such as Portless.
50
+ For theme options, write `dirwell.config.mjs` in the working directory:
51
+
52
+ ```js
53
+ import { createLedgerTheme } from "@dirwell/theme-ledger";
54
+
55
+ export default {
56
+ theme: createLedgerTheme({ title: "Downloads" }),
57
+ build: { exclude: ["drafts/**"] },
58
+ server: { exclude: ["drafts/**"] },
59
+ };
60
+ ```
14
61
 
15
- A JavaScript module passed through `--config` exports `{ theme, listing, build, server }`. Build options retain core filtering, sorting, metadata, symlinks and output-name policies. Server options provide filtering, sorting, authorization and header hooks. This is executable project configuration. For theme factories, use `--theme 'package#exportName'`; the package resolves from the working project. Unsupported legacy dev/daemon commands do not silently change behavior.
62
+ Then run either command without `--theme`. `--config ./other.mjs` selects another
63
+ JavaScript module. Configuration is executable trusted project code, loaded by
64
+ Node without a config loader or TypeScript compiler. Theme precedence is
65
+ `--theme`, then `config.theme`, then Plain. A missing default file is optional;
66
+ an explicit missing file or a broken config fails visibly. If only a legacy
67
+ `dirwell.config.ts`, `.mts`, `.js` or `.cjs` exists, the CLI stops and asks for
68
+ migration or explicit `--config` selection rather than ignoring selection rules.
69
+ Automatic discovery uses only `.mjs`; explicit config must run as native JavaScript
70
+ on your supported Node version.
16
71
 
17
- `--page-size` and `--block-size` override the corresponding static policy as a pair. When only one is given, the other publication strategy is `all`. HTTP cache defaults to no-store; headers are configurable through the server config.
72
+ The config exports only `{ theme, listing, build, server }`; unknown top-level
73
+ keys fail instead of silently dropping legacy options. Put `exclude`/`include`
74
+ in `build` and/or `server`. Export a resolved object, using top-level await
75
+ for asynchronous setup. Shared `listing` accepts
76
+ fixed plans or per-directory callbacks. `build` retains core filtering, sorting,
77
+ metadata, symlinks and output-name policies. `server` provides filtering, sorting,
78
+ authorization and header hooks; HTTP cache defaults to no-store. Build metadata
79
+ and archive search are not live-server features.
18
80
 
19
- Server mode replaces file-data rebuild/watch loops. Editing theme source still requires its framework compiler and a server restart; this is not framework HMR. Farm is not part of this server runtime.
81
+ `--page-size` and `--block-size` override the corresponding static policy as a
82
+ pair. When only one is given, the other publication strategy is `all`.
83
+ Mode-specific flags fail when used with the other mode. Legacy dev/daemon
84
+ commands are unsupported; the CLI does not silently translate them.
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runCli } from "./src-7nVpwBzA.mjs";
2
+ import { t as runCli } from "./src-BDKeXf0W.mjs";
3
3
  //#region src/bin.ts
4
4
  try {
5
5
  const server = await runCli(process.argv.slice(2));
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { ExplorerTheme, GenerateOptions, ListingPolicy } from "@dirwell/core";
2
- import { ExplorerHandlerOptions } from "@dirwell/server";
2
+ import { startExplorerServer } from "@dirwell/server/node";
3
3
  //#region src/index.d.ts
4
4
  export interface CliConfig {
5
5
  readonly build?: Omit<GenerateOptions, "sourceDir" | "outputDir" | "theme" | "listing">;
6
6
  readonly theme?: ExplorerTheme;
7
7
  readonly listing?: ListingPolicy;
8
- readonly server?: Omit<ExplorerHandlerOptions, "root" | "theme" | "listing">;
8
+ readonly server?: Omit<Parameters<typeof startExplorerServer>[0], "root" | "theme" | "listing">;
9
9
  }
10
10
  /** Parse explicit commands; return server ownership to the caller for graceful shutdown. */
11
11
  export declare function runCli(args: readonly string[], cwd?: string, log?: (value: string) => void): Promise<{
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as runCli } from "./src-7nVpwBzA.mjs";
1
+ import { t as runCli } from "./src-BDKeXf0W.mjs";
2
2
  export { runCli };
@@ -1,16 +1,21 @@
1
1
  import path from "node:path";
2
+ import { access, stat } from "node:fs/promises";
2
3
  import { pathToFileURL } from "node:url";
3
4
  import { resolve } from "import-meta-resolve";
4
5
  import { parseArgs } from "node:util";
5
6
  import { generateExplorer } from "@dirwell/core";
7
+ import { createPlainTheme } from "@dirwell/theme-plain";
6
8
  import { startExplorerServer } from "@dirwell/server/node";
7
9
  //#region src/index.ts
8
10
  const help = `Usage:
9
- dirwell build <source> --out <output> --theme <module>#<factory>
10
- dirwell server <source> --theme <module>#<factory> [--port 3000]
11
+ dirwell build <source> --out <output>
12
+ dirwell server <source> [--port 3000]
11
13
 
12
- Install your theme separately. No theme is installed or selected implicitly.
13
- Use --config <file.mjs> for a default export with theme, listing, and server hooks.
14
+ Plain is included and selected by default. Install other themes separately.
15
+ Official themes: --theme plain|ledger|crosswave
16
+ Custom themes: --theme <module>#<factory>, or a module's default export.
17
+ Reads dirwell.config.mjs in the working folder; --config <file.mjs> selects another file.
18
+ Config exports theme, listing, build options, and server hooks.
14
19
  Build options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>
15
20
  Server options: --host <host>, --port <port>, --mount <path>, --live
16
21
  Both modes: --page-size <n>, --block-size <n>
@@ -22,12 +27,34 @@ const positive = (value, name) => {
22
27
  if (!Number.isSafeInteger(number) || number < 1) throw new TypeError(`${name} must be a positive integer`);
23
28
  return number;
24
29
  };
30
+ function assertTheme(candidate) {
31
+ if (typeof candidate !== "object" || candidate === null || !("name" in candidate) || typeof candidate.name !== "string" || !("render" in candidate) || typeof candidate.render !== "function") throw new TypeError("Theme must be an object with name and render(context)");
32
+ }
25
33
  async function loadTheme(specifier, cwd) {
34
+ const official = {
35
+ plain: "@dirwell/theme-plain#createPlainTheme",
36
+ ledger: "@dirwell/theme-ledger#createLedgerTheme",
37
+ crosswave: "@dirwell/theme-crosswave#createCrosswaveTheme"
38
+ };
39
+ if (Object.hasOwn(official, specifier)) specifier = official[specifier];
26
40
  const delimiter = specifier.lastIndexOf("#"), name = delimiter < 0 ? specifier : specifier.slice(0, delimiter), exported = delimiter < 0 ? "default" : specifier.slice(delimiter + 1);
27
41
  if (!name || !exported) throw new TypeError("Theme needs a module and export name");
28
- const module = await (name.startsWith(".") || path.isAbsolute(name) ? import(pathToFileURL(path.resolve(cwd, name)).href) : import(resolve(name, pathToFileURL(path.join(cwd, "package.json")).href)));
42
+ let moduleUrl;
43
+ if (name.startsWith(".") || path.isAbsolute(name)) moduleUrl = pathToFileURL(path.resolve(cwd, name)).href;
44
+ else try {
45
+ moduleUrl = resolve(name, pathToFileURL(path.join(cwd, "package.json")).href);
46
+ } catch (error) {
47
+ if (!(error instanceof Error && "code" in error && error.code === "ERR_MODULE_NOT_FOUND")) throw error;
48
+ try {
49
+ moduleUrl = resolve(name, import.meta.url);
50
+ } catch (fallbackError) {
51
+ if (fallbackError instanceof Error && "code" in fallbackError && fallbackError.code === "ERR_MODULE_NOT_FOUND") throw new TypeError(`Theme package "${name}" was not found. Install it in your project or include it with npm exec --package=${name}.`);
52
+ throw fallbackError;
53
+ }
54
+ }
55
+ const module = await import(moduleUrl);
29
56
  const candidate = typeof module[exported] === "function" ? await module[exported]() : module[exported];
30
- if (typeof candidate !== "object" || candidate === null || !("name" in candidate) || typeof candidate.name !== "string" || !("render" in candidate) || typeof candidate.render !== "function") throw new TypeError("Theme export must be a theme or theme factory");
57
+ assertTheme(candidate);
31
58
  return candidate;
32
59
  }
33
60
  /** Parse explicit commands; return server ownership to the caller for graceful shutdown. */
@@ -59,15 +86,56 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
59
86
  return null;
60
87
  }
61
88
  const [command, source] = parsed.positionals;
62
- if (!["build", "server"].includes(command ?? "") || source === void 0 || parsed.positionals.length !== 2) throw new TypeError(help);
89
+ if (!["build", "server"].includes(command ?? "") || source === void 0 || source === "" || parsed.positionals.length !== 2) throw new TypeError(help);
90
+ const otherModeOptions = command === "build" ? [
91
+ "host",
92
+ "port",
93
+ "mount",
94
+ "live"
95
+ ] : [
96
+ "out",
97
+ "mode",
98
+ "urls",
99
+ "base"
100
+ ];
101
+ for (const option of otherModeOptions) if (Object.hasOwn(parsed.values, option)) throw new TypeError(`--${option} is not supported in ${command} mode`);
63
102
  let config = {};
64
- if (parsed.values.config) {
65
- const module = await import(pathToFileURL(path.resolve(cwd, parsed.values.config)).href);
66
- if (typeof module.default !== "object" || module.default === null) throw new TypeError("Config must export an object");
103
+ const configPath = path.resolve(cwd, parsed.values.config ?? "dirwell.config.mjs");
104
+ let hasConfig = parsed.values.config !== void 0;
105
+ if (!hasConfig) try {
106
+ await access(configPath);
107
+ hasConfig = true;
108
+ } catch (error) {
109
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
110
+ }
111
+ if (!hasConfig) for (const filename of [
112
+ "dirwell.config.ts",
113
+ "dirwell.config.js",
114
+ "dirwell.config.mts",
115
+ "dirwell.config.cjs"
116
+ ]) {
117
+ try {
118
+ await access(path.resolve(cwd, filename));
119
+ } catch (error) {
120
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
121
+ throw error;
122
+ }
123
+ throw new TypeError(`Found ${filename}. This CLI discovers dirwell.config.mjs only. Migrate the config or select a native JavaScript module with --config; existing rules will not be silently ignored.`);
124
+ }
125
+ if (hasConfig) {
126
+ const module = await import(pathToFileURL(configPath).href);
127
+ if (typeof module.default !== "object" || module.default === null || Array.isArray(module.default)) throw new TypeError("Config must export an object");
128
+ for (const key of Object.keys(module.default)) if (![
129
+ "theme",
130
+ "listing",
131
+ "build",
132
+ "server"
133
+ ].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, listing, build, server }; selection rules belong in build/server.`);
134
+ if (typeof module.default.then === "function") throw new TypeError("Config must export a resolved object; use top-level await for async setup");
67
135
  config = module.default;
68
136
  }
69
- const theme = parsed.values.theme ? await loadTheme(parsed.values.theme, cwd) : config.theme;
70
- if (theme === void 0) throw new TypeError("Choose an explicit --theme or config.theme. Install the theme package separately.");
137
+ const theme = parsed.values.theme !== void 0 ? await loadTheme(parsed.values.theme, cwd) : config.theme === void 0 ? createPlainTheme() : config.theme;
138
+ assertTheme(theme);
71
139
  const pageSize = positive(parsed.values["page-size"], "page-size"), blockSize = positive(parsed.values["block-size"], "block-size");
72
140
  const listing = pageSize !== void 0 || blockSize !== void 0 ? {
73
141
  html: pageSize === void 0 ? { strategy: "all" } : {
@@ -80,6 +148,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
80
148
  }
81
149
  } : config.listing;
82
150
  const root = path.resolve(cwd, source);
151
+ if (!(await stat(root)).isDirectory()) throw new TypeError("Source must be an existing directory");
83
152
  if (command === "build") {
84
153
  if (parsed.values.out === void 0) throw new TypeError("Build requires --out pointing to a dedicated generated output tree");
85
154
  const mode = parsed.values.mode ?? config.build?.mode ?? "ssg", urlStrategy = parsed.values.urls ?? config.build?.urlStrategy ?? "relative";
@@ -102,11 +171,11 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
102
171
  log(`Built ${parsed.values.out}`);
103
172
  return null;
104
173
  }
105
- const port = Number(parsed.values.port ?? process.env.PORT ?? "0");
174
+ const port = Number(parsed.values.port ?? process.env.PORT ?? config.server?.port ?? 0);
106
175
  if (!Number.isSafeInteger(port) || port < 0 || port > 65535) throw new TypeError("port must be between 0 and 65535");
107
176
  const server = await startExplorerServer({
108
177
  ...config.server,
109
- ...parsed.values.live ? { live: {} } : {},
178
+ ...parsed.values.live ? { live: config.server?.live || {} } : {},
110
179
  root,
111
180
  theme,
112
181
  port,
@@ -122,4 +191,4 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
122
191
  //#endregion
123
192
  export { runCli as t };
124
193
 
125
- //# sourceMappingURL=src-7nVpwBzA.mjs.map
194
+ //# sourceMappingURL=src-BDKeXf0W.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-BDKeXf0W.mjs","names":["resolveModule"],"sources":["../src/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport { access, stat } from \"node:fs/promises\";\nimport { pathToFileURL } from \"node:url\";\nimport { resolve as resolveModule } from \"import-meta-resolve\";\nimport { parseArgs } from \"node:util\";\nimport { generateExplorer } from \"@dirwell/core\";\nimport type { ExplorerTheme, ListingPolicy, GenerateOptions } from \"@dirwell/core\";\nimport { createPlainTheme } from \"@dirwell/theme-plain\";\nimport { startExplorerServer } from \"@dirwell/server/node\";\n\nexport interface CliConfig {\n readonly build?: Omit<GenerateOptions, \"sourceDir\" | \"outputDir\" | \"theme\" | \"listing\">;\n readonly theme?: ExplorerTheme;\n readonly listing?: ListingPolicy;\n readonly server?: Omit<Parameters<typeof startExplorerServer>[0], \"root\" | \"theme\" | \"listing\">;\n}\nconst help = `Usage:\n dirwell build <source> --out <output>\n dirwell server <source> [--port 3000]\n\nPlain is included and selected by default. Install other themes separately.\nOfficial themes: --theme plain|ledger|crosswave\nCustom themes: --theme <module>#<factory>, or a module's default export.\nReads dirwell.config.mjs in the working folder; --config <file.mjs> selects another file.\nConfig exports theme, listing, build options, and server hooks.\nBuild options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>\nServer options: --host <host>, --port <port>, --mount <path>, --live\nBoth modes: --page-size <n>, --block-size <n>\nLive server reads on request. It does not build an output tree or require watch mode.\n`;\nconst positive = (value: string | undefined, name: string): number | undefined => {\n if (value === undefined) return undefined;\n const number = Number(value);\n if (!Number.isSafeInteger(number) || number < 1)\n throw new TypeError(`${name} must be a positive integer`);\n return number;\n};\nfunction assertTheme(candidate: unknown): asserts candidate is ExplorerTheme {\n if (\n typeof candidate !== \"object\" ||\n candidate === null ||\n !(\"name\" in candidate) ||\n typeof candidate.name !== \"string\" ||\n !(\"render\" in candidate) ||\n typeof candidate.render !== \"function\"\n )\n throw new TypeError(\"Theme must be an object with name and render(context)\");\n}\nasync function loadTheme(specifier: string, cwd: string): Promise<ExplorerTheme> {\n const official: Record<string, string> = {\n plain: \"@dirwell/theme-plain#createPlainTheme\",\n ledger: \"@dirwell/theme-ledger#createLedgerTheme\",\n crosswave: \"@dirwell/theme-crosswave#createCrosswaveTheme\",\n };\n if (Object.hasOwn(official, specifier)) specifier = official[specifier]!;\n const delimiter = specifier.lastIndexOf(\"#\"),\n name = delimiter < 0 ? specifier : specifier.slice(0, delimiter),\n exported = delimiter < 0 ? \"default\" : specifier.slice(delimiter + 1);\n if (!name || !exported) throw new TypeError(\"Theme needs a module and export name\");\n let moduleUrl: string;\n if (name.startsWith(\".\") || path.isAbsolute(name)) {\n moduleUrl = pathToFileURL(path.resolve(cwd, name)).href;\n } else {\n try {\n moduleUrl = resolveModule(name, pathToFileURL(path.join(cwd, \"package.json\")).href);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ERR_MODULE_NOT_FOUND\"))\n throw error;\n // npm exec installs explicit --package entries beside the CLI, outside the user's folder.\n try {\n moduleUrl = resolveModule(name, import.meta.url);\n } catch (fallbackError) {\n if (\n fallbackError instanceof Error &&\n \"code\" in fallbackError &&\n fallbackError.code === \"ERR_MODULE_NOT_FOUND\"\n )\n throw new TypeError(\n `Theme package \"${name}\" was not found. Install it in your project or include it with npm exec --package=${name}.`,\n );\n throw fallbackError;\n }\n }\n }\n const module: Record<string, unknown> = await import(moduleUrl);\n const candidate =\n typeof module[exported] === \"function\" ? await module[exported]() : module[exported];\n assertTheme(candidate);\n return candidate;\n}\n/** Parse explicit commands; return server ownership to the caller for graceful shutdown. */\nexport async function runCli(\n args: readonly string[],\n cwd = process.cwd(),\n log: (value: string) => void = console.log,\n) {\n const parsed = parseArgs({\n args: [...args],\n allowPositionals: true,\n options: {\n help: { type: \"boolean\", short: \"h\" },\n live: { type: \"boolean\" },\n theme: { type: \"string\" },\n config: { type: \"string\" },\n out: { type: \"string\" },\n mode: { type: \"string\" },\n urls: { type: \"string\" },\n base: { type: \"string\" },\n host: { type: \"string\" },\n port: { type: \"string\" },\n mount: { type: \"string\" },\n \"page-size\": { type: \"string\" },\n \"block-size\": { type: \"string\" },\n },\n });\n if (parsed.values.help) {\n log(help);\n return null;\n }\n const [command, source] = parsed.positionals;\n if (\n ![\"build\", \"server\"].includes(command ?? \"\") ||\n source === undefined ||\n source === \"\" ||\n parsed.positionals.length !== 2\n )\n throw new TypeError(help);\n const otherModeOptions =\n command === \"build\" ? [\"host\", \"port\", \"mount\", \"live\"] : [\"out\", \"mode\", \"urls\", \"base\"];\n for (const option of otherModeOptions)\n if (Object.hasOwn(parsed.values, option))\n throw new TypeError(`--${option} is not supported in ${command} mode`);\n let config: CliConfig = {};\n const configPath = path.resolve(cwd, parsed.values.config ?? \"dirwell.config.mjs\");\n let hasConfig = parsed.values.config !== undefined;\n if (!hasConfig) {\n try {\n await access(configPath);\n hasConfig = true;\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"ENOENT\")) throw error;\n }\n }\n if (!hasConfig) {\n for (const filename of [\n \"dirwell.config.ts\",\n \"dirwell.config.js\",\n \"dirwell.config.mts\",\n \"dirwell.config.cjs\",\n ]) {\n try {\n await access(path.resolve(cwd, filename));\n } catch (error) {\n if (error instanceof Error && \"code\" in error && error.code === \"ENOENT\") continue;\n throw error;\n }\n throw new TypeError(\n `Found ${filename}. This CLI discovers dirwell.config.mjs only. Migrate the config or select a native JavaScript module with --config; existing rules will not be silently ignored.`,\n );\n }\n }\n if (hasConfig) {\n const module = await import(pathToFileURL(configPath).href);\n if (\n typeof module.default !== \"object\" ||\n module.default === null ||\n Array.isArray(module.default)\n )\n throw new TypeError(\"Config must export an object\");\n for (const key of Object.keys(module.default))\n if (![\"theme\", \"listing\", \"build\", \"server\"].includes(key))\n throw new TypeError(\n `Unsupported config key \"${key}\". Use { theme, listing, build, server }; selection rules belong in build/server.`,\n );\n if (typeof module.default.then === \"function\")\n throw new TypeError(\n \"Config must export a resolved object; use top-level await for async setup\",\n );\n config = module.default;\n }\n const theme =\n parsed.values.theme !== undefined\n ? await loadTheme(parsed.values.theme, cwd)\n : config.theme === undefined\n ? createPlainTheme()\n : config.theme;\n assertTheme(theme);\n const pageSize = positive(parsed.values[\"page-size\"], \"page-size\"),\n blockSize = positive(parsed.values[\"block-size\"], \"block-size\");\n const listing: ListingPolicy | undefined =\n pageSize !== undefined || blockSize !== undefined\n ? {\n html: pageSize === undefined ? { strategy: \"all\" } : { strategy: \"paged\", pageSize },\n data: blockSize === undefined ? { strategy: \"all\" } : { strategy: \"blocks\", blockSize },\n }\n : config.listing;\n const root = path.resolve(cwd, source);\n if (!(await stat(root)).isDirectory())\n throw new TypeError(\"Source must be an existing directory\");\n if (command === \"build\") {\n if (parsed.values.out === undefined)\n throw new TypeError(\"Build requires --out pointing to a dedicated generated output tree\");\n const mode = parsed.values.mode ?? config.build?.mode ?? \"ssg\",\n urlStrategy = parsed.values.urls ?? config.build?.urlStrategy ?? \"relative\";\n if (mode !== \"ssg\" && mode !== \"mpa\") throw new TypeError(\"mode must be ssg or mpa\");\n if (![\"relative\", \"base\", \"html-base\"].includes(urlStrategy))\n throw new TypeError(\"Unknown URL strategy\");\n await generateExplorer({\n ...config.build,\n sourceDir: root,\n outputDir: path.resolve(cwd, parsed.values.out),\n theme,\n mode,\n urlStrategy: urlStrategy as \"relative\" | \"base\" | \"html-base\",\n ...(parsed.values.base === undefined ? {} : { base: parsed.values.base }),\n ...(listing === undefined ? {} : { listing }),\n });\n log(`Built ${parsed.values.out}`);\n return null;\n }\n const port = Number(parsed.values.port ?? process.env.PORT ?? config.server?.port ?? 0);\n if (!Number.isSafeInteger(port) || port < 0 || port > 65535)\n throw new TypeError(\"port must be between 0 and 65535\");\n const server = await startExplorerServer({\n ...config.server,\n ...(parsed.values.live ? { live: config.server?.live || {} } : {}),\n root,\n theme,\n port,\n ...(listing === undefined ? {} : { listing }),\n ...(pageSize === undefined ? {} : { pageSize }),\n ...(blockSize === undefined ? {} : { blockSize }),\n ...(parsed.values.host === undefined ? {} : { host: parsed.values.host }),\n ...(parsed.values.mount === undefined ? {} : { mountPath: parsed.values.mount }),\n });\n log(\n `Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? \"\"}/browse/`,\n );\n return server;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dirwell/cli",
3
- "version": "0.0.1-alpha",
4
- "description": "Explicit-theme build and live-server commands for Dirwell.",
3
+ "version": "0.0.1-alpha.1",
4
+ "description": "Minimal file explorer build and live-server commands with Plain included.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -29,8 +29,9 @@
29
29
  "tag": "alpha"
30
30
  },
31
31
  "dependencies": {
32
- "@dirwell/core": "0.0.1-alpha",
33
- "@dirwell/server": "0.0.1-alpha",
32
+ "@dirwell/core": "0.0.1-alpha.1",
33
+ "@dirwell/server": "0.0.1-alpha.1",
34
+ "@dirwell/theme-plain": "0.0.1-alpha.1",
34
35
  "import-meta-resolve": "4.2.0"
35
36
  },
36
37
  "engines": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"src-7nVpwBzA.mjs","names":["resolveModule"],"sources":["../src/index.ts"],"sourcesContent":["import path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { resolve as resolveModule } from \"import-meta-resolve\";\nimport { parseArgs } from \"node:util\";\nimport { generateExplorer } from \"@dirwell/core\";\nimport type { ExplorerTheme, ListingPolicy, GenerateOptions } from \"@dirwell/core\";\nimport { startExplorerServer } from \"@dirwell/server/node\";\nimport type { ExplorerHandlerOptions } from \"@dirwell/server\";\n\nexport interface CliConfig {\n readonly build?: Omit<GenerateOptions, \"sourceDir\" | \"outputDir\" | \"theme\" | \"listing\">;\n readonly theme?: ExplorerTheme;\n readonly listing?: ListingPolicy;\n readonly server?: Omit<ExplorerHandlerOptions, \"root\" | \"theme\" | \"listing\">;\n}\nconst help = `Usage:\n dirwell build <source> --out <output> --theme <module>#<factory>\n dirwell server <source> --theme <module>#<factory> [--port 3000]\n\nInstall your theme separately. No theme is installed or selected implicitly.\nUse --config <file.mjs> for a default export with theme, listing, and server hooks.\nBuild options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>\nServer options: --host <host>, --port <port>, --mount <path>, --live\nBoth modes: --page-size <n>, --block-size <n>\nLive server reads on request. It does not build an output tree or require watch mode.\n`;\nconst positive = (value: string | undefined, name: string): number | undefined => {\n if (value === undefined) return undefined;\n const number = Number(value);\n if (!Number.isSafeInteger(number) || number < 1)\n throw new TypeError(`${name} must be a positive integer`);\n return number;\n};\nasync function loadTheme(specifier: string, cwd: string): Promise<ExplorerTheme> {\n const delimiter = specifier.lastIndexOf(\"#\"),\n name = delimiter < 0 ? specifier : specifier.slice(0, delimiter),\n exported = delimiter < 0 ? \"default\" : specifier.slice(delimiter + 1);\n if (!name || !exported) throw new TypeError(\"Theme needs a module and export name\");\n const moduleUrl =\n name.startsWith(\".\") || path.isAbsolute(name)\n ? pathToFileURL(path.resolve(cwd, name)).href\n : resolveModule(name, pathToFileURL(path.join(cwd, \"package.json\")).href);\n const module: Record<string, unknown> = await import(moduleUrl);\n const candidate =\n typeof module[exported] === \"function\" ? await module[exported]() : module[exported];\n if (\n typeof candidate !== \"object\" ||\n candidate === null ||\n !(\"name\" in candidate) ||\n typeof candidate.name !== \"string\" ||\n !(\"render\" in candidate) ||\n typeof candidate.render !== \"function\"\n )\n throw new TypeError(\"Theme export must be a theme or theme factory\");\n return candidate as ExplorerTheme;\n}\n/** Parse explicit commands; return server ownership to the caller for graceful shutdown. */\nexport async function runCli(\n args: readonly string[],\n cwd = process.cwd(),\n log: (value: string) => void = console.log,\n) {\n const parsed = parseArgs({\n args: [...args],\n allowPositionals: true,\n options: {\n help: { type: \"boolean\", short: \"h\" },\n live: { type: \"boolean\" },\n theme: { type: \"string\" },\n config: { type: \"string\" },\n out: { type: \"string\" },\n mode: { type: \"string\" },\n urls: { type: \"string\" },\n base: { type: \"string\" },\n host: { type: \"string\" },\n port: { type: \"string\" },\n mount: { type: \"string\" },\n \"page-size\": { type: \"string\" },\n \"block-size\": { type: \"string\" },\n },\n });\n if (parsed.values.help) {\n log(help);\n return null;\n }\n const [command, source] = parsed.positionals;\n if (\n ![\"build\", \"server\"].includes(command ?? \"\") ||\n source === undefined ||\n parsed.positionals.length !== 2\n )\n throw new TypeError(help);\n let config: CliConfig = {};\n if (parsed.values.config) {\n const module = await import(pathToFileURL(path.resolve(cwd, parsed.values.config)).href);\n if (typeof module.default !== \"object\" || module.default === null)\n throw new TypeError(\"Config must export an object\");\n config = module.default;\n }\n const theme = parsed.values.theme ? await loadTheme(parsed.values.theme, cwd) : config.theme;\n if (theme === undefined)\n throw new TypeError(\n \"Choose an explicit --theme or config.theme. Install the theme package separately.\",\n );\n const pageSize = positive(parsed.values[\"page-size\"], \"page-size\"),\n blockSize = positive(parsed.values[\"block-size\"], \"block-size\");\n const listing: ListingPolicy | undefined =\n pageSize !== undefined || blockSize !== undefined\n ? {\n html: pageSize === undefined ? { strategy: \"all\" } : { strategy: \"paged\", pageSize },\n data: blockSize === undefined ? { strategy: \"all\" } : { strategy: \"blocks\", blockSize },\n }\n : config.listing;\n const root = path.resolve(cwd, source);\n if (command === \"build\") {\n if (parsed.values.out === undefined)\n throw new TypeError(\"Build requires --out pointing to a dedicated generated output tree\");\n const mode = parsed.values.mode ?? config.build?.mode ?? \"ssg\",\n urlStrategy = parsed.values.urls ?? config.build?.urlStrategy ?? \"relative\";\n if (mode !== \"ssg\" && mode !== \"mpa\") throw new TypeError(\"mode must be ssg or mpa\");\n if (![\"relative\", \"base\", \"html-base\"].includes(urlStrategy))\n throw new TypeError(\"Unknown URL strategy\");\n await generateExplorer({\n ...config.build,\n sourceDir: root,\n outputDir: path.resolve(cwd, parsed.values.out),\n theme,\n mode,\n urlStrategy: urlStrategy as \"relative\" | \"base\" | \"html-base\",\n ...(parsed.values.base === undefined ? {} : { base: parsed.values.base }),\n ...(listing === undefined ? {} : { listing }),\n });\n log(`Built ${parsed.values.out}`);\n return null;\n }\n const port = Number(parsed.values.port ?? process.env.PORT ?? \"0\");\n if (!Number.isSafeInteger(port) || port < 0 || port > 65535)\n throw new TypeError(\"port must be between 0 and 65535\");\n const server = await startExplorerServer({\n ...config.server,\n ...(parsed.values.live ? { live: {} } : {}),\n root,\n theme,\n port,\n ...(listing === undefined ? {} : { listing }),\n ...(pageSize === undefined ? {} : { pageSize }),\n ...(blockSize === undefined ? {} : { blockSize }),\n ...(parsed.values.host === undefined ? {} : { host: parsed.values.host }),\n ...(parsed.values.mount === undefined ? {} : { mountPath: parsed.values.mount }),\n });\n log(\n `Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? \"\"}/browse/`,\n );\n return server;\n}\n"],"mappings":"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"}