@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 +74 -9
- package/dist/bin.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/{src-7nVpwBzA.mjs → src-BDKeXf0W.mjs} +84 -15
- package/dist/src-BDKeXf0W.mjs.map +1 -0
- package/package.json +5 -4
- package/dist/src-7nVpwBzA.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,19 +1,84 @@
|
|
|
1
1
|
# @dirwell/cli
|
|
2
2
|
|
|
3
|
-
|
|
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-
|
|
7
|
-
npx dirwell
|
|
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
|
-
`--
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
package/dist/index.d.mts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { ExplorerTheme, GenerateOptions, ListingPolicy } from "@dirwell/core";
|
|
2
|
-
import {
|
|
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<
|
|
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-
|
|
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>
|
|
10
|
-
dirwell server <source>
|
|
11
|
+
dirwell build <source> --out <output>
|
|
12
|
+
dirwell server <source> [--port 3000]
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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 ??
|
|
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-
|
|
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": "
|
|
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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"}
|