@dirwell/cli 0.0.1 → 0.0.3
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 +13 -3
- package/dist/bin.mjs +1 -1
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +1 -1
- package/dist/{src-DapzDTGv.mjs → src-CFuBl9jc.mjs} +31 -14
- package/dist/src-CFuBl9jc.mjs.map +1 -0
- package/package.json +4 -4
- package/dist/src-DapzDTGv.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ The CLI supports short theme names and automatic `dirwell.config.mjs` discovery.
|
|
|
11
11
|
## Open or publish a folder
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
npx @dirwell/cli@0.0.
|
|
15
|
-
npx @dirwell/cli@0.0.
|
|
14
|
+
npx @dirwell/cli@0.0.3 server ./files
|
|
15
|
+
npx @dirwell/cli@0.0.3 build ./files --out ./generated
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
No project, configuration file or global installation is required. Open the URL
|
|
@@ -42,7 +42,7 @@ Builds replace missing, empty or Dirwell-marked output directories. A nonempty u
|
|
|
42
42
|
For repeated project use, install the CLI and the theme you want:
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
|
-
npm install --save-dev @dirwell/cli@0.0.
|
|
45
|
+
npm install --save-dev @dirwell/cli@0.0.3 @dirwell/theme-ledger@0.0.3
|
|
46
46
|
npx dirwell server ./files --theme ledger
|
|
47
47
|
```
|
|
48
48
|
|
|
@@ -89,3 +89,13 @@ Mode-specific flags fail when used with the other mode. Legacy dev/daemon
|
|
|
89
89
|
commands are unsupported; the CLI does not silently translate them.
|
|
90
90
|
|
|
91
91
|
The 0.0.x API may evolve. Pin your qualified version and upgrade the official family together. See [support and verification](https://vp-tw.github.io/dirwell/support/) for host, browser and device boundaries.
|
|
92
|
+
|
|
93
|
+
## Directory URLs
|
|
94
|
+
|
|
95
|
+
Version 0.0.3 uses Core `routing: { directoryUrls: "explicit" }` by default
|
|
96
|
+
for every theme. Choose `slash` for directory-index hosts or `clean` for a
|
|
97
|
+
qualified extensionless host. Themes do not rewrite links. Existing source
|
|
98
|
+
homepages and download names are preserved; output conflicts fail. CLI config
|
|
99
|
+
puts routing at the top level, and `--directory-urls` overrides it.
|
|
100
|
+
See [directory URL guidance](https://vp-tw.github.io/dirwell/urls/) for output
|
|
101
|
+
placement, host requirements, native Plain file navigation and migration.
|
package/dist/bin.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -6,11 +6,12 @@ export declare function defineConfig<const T extends CliConfig>(config: T): T;
|
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/index.d.ts
|
|
8
8
|
export interface CliConfig {
|
|
9
|
+
readonly routing?: GenerateOptions["routing"];
|
|
9
10
|
readonly ignoreFiles?: GenerateOptions["ignoreFiles"];
|
|
10
|
-
readonly build?: Omit<GenerateOptions, "sourceDir" | "outputDir" | "theme" | "listing">;
|
|
11
|
+
readonly build?: Omit<GenerateOptions, "sourceDir" | "outputDir" | "theme" | "listing" | "routing">;
|
|
11
12
|
readonly theme?: ExplorerTheme;
|
|
12
13
|
readonly listing?: ListingPolicy;
|
|
13
|
-
readonly server?: Omit<Parameters<typeof startExplorerServer>[0], "root" | "theme" | "listing">;
|
|
14
|
+
readonly server?: Omit<Parameters<typeof startExplorerServer>[0], "root" | "theme" | "listing" | "routing">;
|
|
14
15
|
}
|
|
15
16
|
/** Parse explicit commands; return server ownership to the caller for graceful shutdown. */
|
|
16
17
|
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 { n as defineConfig, t as runCli } from "./src-
|
|
1
|
+
import { n as defineConfig, t as runCli } from "./src-CFuBl9jc.mjs";
|
|
2
2
|
export { defineConfig, runCli };
|
|
@@ -3,7 +3,7 @@ import { access, stat } from "node:fs/promises";
|
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
4
|
import { resolve } from "import-meta-resolve";
|
|
5
5
|
import { parseArgs } from "node:util";
|
|
6
|
-
import { generateExplorer, resolveIgnoreFiles } from "@dirwell/core";
|
|
6
|
+
import { directoryRoute, encodeLogicalPath, generateExplorer, resolveDirectoryUrls, resolveIgnoreFiles } from "@dirwell/core";
|
|
7
7
|
import { createPlainTheme } from "@dirwell/theme-plain";
|
|
8
8
|
import { startExplorerServer } from "@dirwell/server/node";
|
|
9
9
|
//#region src/config.ts
|
|
@@ -100,6 +100,7 @@ const serverKeys = {
|
|
|
100
100
|
entryData: true,
|
|
101
101
|
ignoreFiles: true,
|
|
102
102
|
live: true,
|
|
103
|
+
search: true,
|
|
103
104
|
blockSize: true,
|
|
104
105
|
mountPath: true,
|
|
105
106
|
files: true,
|
|
@@ -122,8 +123,10 @@ function validateCliConfig(value) {
|
|
|
122
123
|
"listing",
|
|
123
124
|
"build",
|
|
124
125
|
"server",
|
|
125
|
-
"ignoreFiles"
|
|
126
|
-
|
|
126
|
+
"ignoreFiles",
|
|
127
|
+
"routing"
|
|
128
|
+
].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, routing, listing, build, server }; selection rules belong in build/server.`);
|
|
129
|
+
resolveDirectoryUrls(config.routing);
|
|
127
130
|
listing(config.listing, "listing");
|
|
128
131
|
resolveIgnoreFiles(config.ignoreFiles);
|
|
129
132
|
for (const mode of ["build", "server"]) {
|
|
@@ -183,6 +186,22 @@ function validateCliConfig(value) {
|
|
|
183
186
|
keys(live, ["intervalMs", "maxClients"], "server.live");
|
|
184
187
|
for (const key of ["intervalMs", "maxClients"]) scalar(live[key], "number", `server.live.${key}`);
|
|
185
188
|
}
|
|
189
|
+
if (options.search !== void 0 && options.search !== false) {
|
|
190
|
+
const search = object(options.search, "server.search");
|
|
191
|
+
keys(search, [
|
|
192
|
+
"maxEntries",
|
|
193
|
+
"maxDirectories",
|
|
194
|
+
"maxConcurrent",
|
|
195
|
+
"maxResults",
|
|
196
|
+
"timeoutMs",
|
|
197
|
+
"refreshIntervalMs"
|
|
198
|
+
], "server.search");
|
|
199
|
+
if (typeof search.maxResults === "number" && search.maxResults > 1e4) throw new TypeError("server.search.maxResults must not exceed 10000");
|
|
200
|
+
for (const key of Object.keys(search)) {
|
|
201
|
+
scalar(search[key], "number", `server.search.${key}`);
|
|
202
|
+
if (!Number.isSafeInteger(search[key]) || Number(search[key]) < (key === "refreshIntervalMs" ? 1e3 : 1)) throw new TypeError(`server.search.${key} must be a positive safe integer`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
186
205
|
}
|
|
187
206
|
}
|
|
188
207
|
}
|
|
@@ -206,7 +225,7 @@ Build options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>
|
|
|
206
225
|
Build safety: --dry-run, --force (-f) replaces an unowned nonempty output.
|
|
207
226
|
Selection: --ignore-file <path> (repeatable), --no-ignore disables ignore files.
|
|
208
227
|
Server options: --host <host>, --port <port>, --mount <path>, --live
|
|
209
|
-
Both modes: --page-size <n>, --block-size <n>
|
|
228
|
+
Both modes: --directory-urls explicit|slash|clean, --page-size <n>, --block-size <n>
|
|
210
229
|
Live server reads on request. It does not build an output tree or require watch mode.
|
|
211
230
|
`;
|
|
212
231
|
const positive = (value, name) => {
|
|
@@ -271,6 +290,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
|
|
|
271
290
|
out: { type: "string" },
|
|
272
291
|
mode: { type: "string" },
|
|
273
292
|
urls: { type: "string" },
|
|
293
|
+
"directory-urls": { type: "string" },
|
|
274
294
|
base: { type: "string" },
|
|
275
295
|
host: { type: "string" },
|
|
276
296
|
port: { type: "string" },
|
|
@@ -325,19 +345,14 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
|
|
|
325
345
|
if (hasConfig) {
|
|
326
346
|
const module = await import(pathToFileURL(configPath).href);
|
|
327
347
|
if (typeof module.default !== "object" || module.default === null || Array.isArray(module.default)) throw new TypeError("Config must export an object");
|
|
328
|
-
for (const key of Object.keys(module.default)) if (![
|
|
329
|
-
"theme",
|
|
330
|
-
"listing",
|
|
331
|
-
"build",
|
|
332
|
-
"server",
|
|
333
|
-
"ignoreFiles"
|
|
334
|
-
].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, listing, build, server }; selection rules belong in build/server.`);
|
|
335
348
|
if (typeof module.default.then === "function") throw new TypeError("Config must export a resolved object; use top-level await for async setup");
|
|
336
349
|
validateCliConfig(module.default);
|
|
337
350
|
config = module.default;
|
|
338
351
|
}
|
|
339
352
|
const theme = parsed.values.theme !== void 0 ? await loadTheme(parsed.values.theme, cwd) : config.theme === void 0 ? createPlainTheme() : config.theme;
|
|
340
353
|
assertTheme(theme);
|
|
354
|
+
const directoryUrls = resolveDirectoryUrls(parsed.values["directory-urls"] === void 0 ? config.routing : { directoryUrls: parsed.values["directory-urls"] });
|
|
355
|
+
const routing = { directoryUrls };
|
|
341
356
|
const pageSize = positive(parsed.values["page-size"], "page-size"), blockSize = positive(parsed.values["block-size"], "block-size");
|
|
342
357
|
const listing = pageSize !== void 0 || blockSize !== void 0 ? {
|
|
343
358
|
html: pageSize === void 0 ? { strategy: "all" } : {
|
|
@@ -368,6 +383,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
|
|
|
368
383
|
].includes(urlStrategy)) throw new TypeError("Unknown URL strategy");
|
|
369
384
|
const report = await generateExplorer({
|
|
370
385
|
...config.build,
|
|
386
|
+
routing,
|
|
371
387
|
sourceDir: root,
|
|
372
388
|
outputDir: path.resolve(cwd, parsed.values.out),
|
|
373
389
|
theme,
|
|
@@ -383,7 +399,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
|
|
|
383
399
|
else {
|
|
384
400
|
log(`Built ${parsed.values.out}`);
|
|
385
401
|
log(`${report.selectedPaths.length} selected paths; ${report.pages.filter((page) => page.outputName !== null).length} directory pages; ${report.pages.filter((page) => page.outputName === null).length} skipped pages.`);
|
|
386
|
-
for (const page of report.pages) if (page.directory === "" || page.outputName === null) log(page.outputName === null ? `Skipped explorer page: ${page.directory || "/"}` : `Explorer entry: ${path.join(report.outputDir, page.
|
|
402
|
+
for (const page of report.pages) if (page.directory === "" || page.outputName === null) log(page.outputName === null ? `Skipped explorer page: ${page.directory || "/"}` : `Explorer entry: ${path.join(report.outputDir, page.outputPath)} (public: ${page.publicPath || "/"})`);
|
|
387
403
|
}
|
|
388
404
|
return null;
|
|
389
405
|
}
|
|
@@ -391,6 +407,7 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
|
|
|
391
407
|
if (!Number.isSafeInteger(port) || port < 0 || port > 65535) throw new TypeError("port must be between 0 and 65535");
|
|
392
408
|
const server = await startExplorerServer({
|
|
393
409
|
...config.server,
|
|
410
|
+
routing,
|
|
394
411
|
...parsed.values.live ? { live: config.server?.live || {} } : {},
|
|
395
412
|
root,
|
|
396
413
|
theme,
|
|
@@ -402,10 +419,10 @@ async function runCli(args, cwd = process.cwd(), log = console.log) {
|
|
|
402
419
|
...parsed.values.host === void 0 ? {} : { host: parsed.values.host },
|
|
403
420
|
...parsed.values.mount === void 0 ? {} : { mountPath: parsed.values.mount }
|
|
404
421
|
});
|
|
405
|
-
log(`Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? ""}/browse
|
|
422
|
+
log(`Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? ""}/browse${directoryUrls === "clean" ? "" : "/"}${encodeLogicalPath(directoryRoute("", "index.html", directoryUrls).publicPath)}`);
|
|
406
423
|
return server;
|
|
407
424
|
}
|
|
408
425
|
//#endregion
|
|
409
426
|
export { defineConfig as n, runCli as t };
|
|
410
427
|
|
|
411
|
-
//# sourceMappingURL=src-
|
|
428
|
+
//# sourceMappingURL=src-CFuBl9jc.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"src-CFuBl9jc.mjs","names":["resolveModule"],"sources":["../src/config.ts","../src/index.ts"],"sourcesContent":["import type { CliConfig } from \"./index.ts\";\nimport { resolveIgnoreFiles, resolveDirectoryUrls } from \"@dirwell/core\";\n\ntype RecordValue = Record<string, unknown>;\nfunction object(value: unknown, name: string): RecordValue {\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new TypeError(`${name} must be an object`);\n return value as RecordValue;\n}\nfunction distance(left: string, right: string): number {\n let previous = Array.from({ length: right.length + 1 }, (_, index) => index);\n for (let i = 1; i <= left.length; i++) {\n const current = [i];\n for (let j = 1; j <= right.length; j++)\n current[j] = Math.min(\n current[j - 1]! + 1,\n previous[j]! + 1,\n previous[j - 1]! + (left[i - 1] === right[j - 1] ? 0 : 1),\n );\n previous = current;\n }\n return previous[right.length]!;\n}\nfunction keys(value: RecordValue, allowed: readonly string[], name: string): void {\n for (const key of Object.keys(value)) {\n if (allowed.includes(key)) continue;\n const suggestion = allowed\n .map((candidate) => ({ candidate, distance: distance(key, candidate) }))\n .sort((left, right) => left.distance - right.distance)\n .find((item) => item.distance <= 2)?.candidate;\n throw new TypeError(\n `Unsupported config key \"${name}.${key}\".${suggestion ? ` Did you mean \"${name}.${suggestion}\"?` : ` Supported keys: ${allowed.join(\", \")}.`}`,\n );\n }\n}\nfunction scalar(value: unknown, type: \"string\" | \"boolean\" | \"number\" | \"function\", name: string) {\n if (\n value !== undefined &&\n (typeof value !== type || (type === \"number\" && !Number.isFinite(value)))\n )\n throw new TypeError(`${name} must be a ${type}`);\n}\nfunction patterns(value: unknown, name: string): void {\n if (\n value !== undefined &&\n typeof value !== \"string\" &&\n (!Array.isArray(value) || value.some((item) => typeof item !== \"string\"))\n )\n throw new TypeError(`${name} must be a string or an array of strings`);\n}\nfunction sort(value: unknown, name: string): void {\n if (value === undefined) return;\n const options = object(value, name);\n keys(options, [\"direction\", \"directoriesFirst\", \"field\", \"nameMode\"], name);\n scalar(options.directoriesFirst, \"boolean\", `${name}.directoriesFirst`);\n for (const [key, allowed] of Object.entries({\n direction: [\"asc\", \"desc\"],\n field: [\"name\", \"size\", \"modified\"],\n nameMode: [\"natural\", \"unicode\", \"locale\"],\n }))\n if (options[key] !== undefined && !allowed.includes(String(options[key])))\n throw new TypeError(`${name}.${key} must be one of ${allowed.join(\", \")}`);\n}\nfunction listing(value: unknown, name: string): void {\n if (value === undefined || typeof value === \"function\") return;\n const plan = object(value, name);\n keys(plan, [\"html\", \"data\"], name);\n for (const [key, sizedStrategy, sizeKey] of [\n [\"html\", \"paged\", \"pageSize\"],\n [\"data\", \"blocks\", \"blockSize\"],\n ] as const) {\n const strategy = object(plan[key], `${name}.${key}`);\n keys(\n strategy,\n strategy.strategy === sizedStrategy ? [\"strategy\", sizeKey] : [\"strategy\"],\n `${name}.${key}`,\n );\n if (strategy.strategy !== \"all\" && strategy.strategy !== sizedStrategy)\n throw new TypeError(`${name}.${key}.strategy must be all or ${sizedStrategy}`);\n if (\n strategy.strategy === sizedStrategy &&\n (!Number.isSafeInteger(strategy[sizeKey]) || Number(strategy[sizeKey]) < 1)\n )\n throw new TypeError(`${name}.${key}.${sizeKey} must be a positive integer`);\n }\n}\nconst buildKeys = {\n fileHref: true,\n entryData: true,\n dryRun: true,\n outputPolicy: true,\n ignoreFiles: true,\n metadata: true,\n base: true,\n include: true,\n exclude: true,\n mode: true,\n mirror: true,\n outputName: true,\n urlStrategy: true,\n symlinks: true,\n sort: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"build\"]>, true>;\nconst serverKeys = {\n entryData: true,\n ignoreFiles: true,\n live: true,\n search: true,\n blockSize: true,\n mountPath: true,\n files: true,\n pageSize: true,\n maxPageSize: true,\n fileHref: true,\n headers: true,\n authorize: true,\n include: true,\n exclude: true,\n sort: true,\n host: true,\n port: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"server\"]>, true>;\n\n/** Validate both modes before filesystem publication or server startup. */\nexport function validateCliConfig(value: unknown): asserts value is CliConfig {\n const config = object(value, \"Config\");\n // Preserve the top-level diagnostic used by CLI consumers.\n for (const key of Object.keys(config))\n if (![\"theme\", \"listing\", \"build\", \"server\", \"ignoreFiles\", \"routing\"].includes(key))\n throw new TypeError(\n `Unsupported config key \"${key}\". Use { theme, routing, listing, build, server }; selection rules belong in build/server.`,\n );\n resolveDirectoryUrls(config.routing);\n listing(config.listing, \"listing\");\n resolveIgnoreFiles(config.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n for (const mode of [\"build\", \"server\"] as const) {\n if (config[mode] === undefined) continue;\n const options = object(config[mode], mode);\n keys(options, Object.keys(mode === \"build\" ? buildKeys : serverKeys), mode);\n patterns(options.include, `${mode}.include`);\n patterns(options.exclude, `${mode}.exclude`);\n resolveIgnoreFiles(options.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n sort(options.sort, `${mode}.sort`);\n scalar(options.entryData, \"function\", `${mode}.entryData`);\n if (mode === \"build\") {\n scalar(options.fileHref, \"function\", \"build.fileHref\");\n for (const key of [\"base\", \"mode\", \"urlStrategy\"])\n scalar(options[key], \"string\", `${mode}.${key}`);\n scalar(options.mirror, \"boolean\", \"build.mirror\");\n scalar(options.dryRun, \"boolean\", \"build.dryRun\");\n if (\n options.outputPolicy !== undefined &&\n options.outputPolicy !== \"owned\" &&\n options.outputPolicy !== \"replace\"\n )\n throw new TypeError(\"build.outputPolicy must be owned or replace\");\n scalar(options.metadata, \"function\", \"build.metadata\");\n if (\n options.outputName !== undefined &&\n typeof options.outputName !== \"string\" &&\n typeof options.outputName !== \"function\"\n )\n throw new TypeError(\"build.outputName must be a string or function\");\n if (options.symlinks !== undefined) {\n const links = object(options.symlinks, \"build.symlinks\");\n keys(links, [\"follow\", \"boundary\", \"onCycle\"], \"build.symlinks\");\n scalar(links.follow, \"boolean\", \"build.symlinks.follow\");\n for (const [key, allowed] of Object.entries({\n boundary: [\"root\", \"anywhere\"],\n onCycle: [\"error\", \"skip\"],\n }))\n if (links[key] !== undefined && !allowed.includes(String(links[key])))\n throw new TypeError(`build.symlinks.${key} must be one of ${allowed.join(\", \")}`);\n }\n } else {\n for (const key of [\"host\", \"mountPath\"]) scalar(options[key], \"string\", `server.${key}`);\n for (const key of [\"port\", \"pageSize\", \"maxPageSize\", \"blockSize\"])\n scalar(options[key], \"number\", `server.${key}`);\n for (const key of [\"fileHref\", \"headers\", \"authorize\"])\n scalar(options[key], \"function\", `server.${key}`);\n if (\n options.files !== undefined &&\n options.files !== false &&\n ![\"media\", \"attachment\", \"inline\"].includes(String(options.files))\n )\n throw new TypeError(\"server.files must be false, media, attachment or inline\");\n if (options.live !== undefined && options.live !== false) {\n const live = object(options.live, \"server.live\");\n keys(live, [\"intervalMs\", \"maxClients\"], \"server.live\");\n for (const key of [\"intervalMs\", \"maxClients\"])\n scalar(live[key], \"number\", `server.live.${key}`);\n }\n if (options.search !== undefined && options.search !== false) {\n const search = object(options.search, \"server.search\");\n keys(\n search,\n [\n \"maxEntries\",\n \"maxDirectories\",\n \"maxConcurrent\",\n \"maxResults\",\n \"timeoutMs\",\n \"refreshIntervalMs\",\n ],\n \"server.search\",\n );\n if (typeof search.maxResults === \"number\" && search.maxResults > 10000)\n throw new TypeError(\"server.search.maxResults must not exceed 10000\");\n for (const key of Object.keys(search)) {\n scalar(search[key], \"number\", `server.search.${key}`);\n if (\n !Number.isSafeInteger(search[key]) ||\n Number(search[key]) < (key === \"refreshIntervalMs\" ? 1000 : 1)\n )\n throw new TypeError(`server.search.${key} must be a positive safe integer`);\n }\n }\n }\n }\n}\n\n/** Preserve literal types and validate JavaScript configuration at definition time. */\nexport function defineConfig<const T extends CliConfig>(config: T): T {\n validateCliConfig(config);\n return config;\n}\n","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 {\n generateExplorer,\n resolveIgnoreFiles,\n resolveDirectoryUrls,\n directoryRoute,\n encodeLogicalPath,\n} from \"@dirwell/core\";\nimport type { ExplorerTheme, ListingPolicy, GenerateOptions } from \"@dirwell/core\";\nimport { createPlainTheme } from \"@dirwell/theme-plain\";\nimport { startExplorerServer } from \"@dirwell/server/node\";\nimport { validateCliConfig } from \"./config.ts\";\nexport { defineConfig } from \"./config.ts\";\n\nexport interface CliConfig {\n readonly routing?: GenerateOptions[\"routing\"];\n readonly ignoreFiles?: GenerateOptions[\"ignoreFiles\"];\n readonly build?: Omit<\n GenerateOptions,\n \"sourceDir\" | \"outputDir\" | \"theme\" | \"listing\" | \"routing\"\n >;\n readonly theme?: ExplorerTheme;\n readonly listing?: ListingPolicy;\n readonly server?: Omit<\n Parameters<typeof startExplorerServer>[0],\n \"root\" | \"theme\" | \"listing\" | \"routing\"\n >;\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>\nBuild safety: --dry-run, --force (-f) replaces an unowned nonempty output.\nSelection: --ignore-file <path> (repeatable), --no-ignore disables ignore files.\nServer options: --host <host>, --port <port>, --mount <path>, --live\nBoth modes: --directory-urls explicit|slash|clean, --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 force: { type: \"boolean\", short: \"f\" },\n \"dry-run\": { type: \"boolean\" },\n \"ignore-file\": { type: \"string\", multiple: true },\n \"no-ignore\": { type: \"boolean\" },\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 \"directory-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\"\n ? [\"host\", \"port\", \"mount\", \"live\"]\n : [\"out\", \"mode\", \"urls\", \"base\", \"force\", \"dry-run\"];\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 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 validateCliConfig(module.default);\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 directoryUrls = resolveDirectoryUrls(\n parsed.values[\"directory-urls\"] === undefined\n ? config.routing\n : { directoryUrls: parsed.values[\"directory-urls\"] },\n );\n const routing = { directoryUrls };\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 (parsed.values[\"no-ignore\"] && parsed.values[\"ignore-file\"])\n throw new TypeError(\"--no-ignore cannot be combined with --ignore-file\");\n const modeOptions = command === \"build\" ? config.build : config.server;\n const ignoreFiles = resolveIgnoreFiles(\n parsed.values[\"no-ignore\"]\n ? []\n : (parsed.values[\"ignore-file\"] ??\n modeOptions?.ignoreFiles ??\n config.ignoreFiles ?? [{ path: \".gitignore\", optional: true }]),\n cwd,\n );\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 const report = await generateExplorer({\n ...config.build,\n routing,\n sourceDir: root,\n outputDir: path.resolve(cwd, parsed.values.out),\n theme,\n ignoreFiles,\n ...(parsed.values.force ? { outputPolicy: \"replace\" } : {}),\n ...(parsed.values[\"dry-run\"] ? { dryRun: true } : {}),\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 if (report.dryRun) log(JSON.stringify(report, null, 2));\n else {\n log(`Built ${parsed.values.out}`);\n log(\n `${report.selectedPaths.length} selected paths; ${report.pages.filter((page) => page.outputName !== null).length} directory pages; ${report.pages.filter((page) => page.outputName === null).length} skipped pages.`,\n );\n for (const page of report.pages)\n if (page.directory === \"\" || page.outputName === null)\n log(\n page.outputName === null\n ? `Skipped explorer page: ${page.directory || \"/\"}`\n : `Explorer entry: ${path.join(report.outputDir, page.outputPath!)} (public: ${page.publicPath || \"/\"})`,\n );\n }\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 routing,\n ...(parsed.values.live ? { live: config.server?.live || {} } : {}),\n root,\n theme,\n ignoreFiles,\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${directoryUrls === \"clean\" ? \"\" : \"/\"}${encodeLogicalPath(directoryRoute(\"\", \"index.html\", directoryUrls).publicPath)}`,\n );\n return server;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dirwell/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Minimal file explorer build and live-server commands with Plain included.",
|
|
5
5
|
"homepage": "https://vp-tw.github.io/dirwell/getting-started/",
|
|
6
6
|
"bugs": {
|
|
@@ -33,9 +33,9 @@
|
|
|
33
33
|
"tag": "latest"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@dirwell/core": "0.0.
|
|
37
|
-
"@dirwell/server": "0.0.
|
|
38
|
-
"@dirwell/theme-plain": "0.0.
|
|
36
|
+
"@dirwell/core": "0.0.3",
|
|
37
|
+
"@dirwell/server": "0.0.3",
|
|
38
|
+
"@dirwell/theme-plain": "0.0.3",
|
|
39
39
|
"import-meta-resolve": "4.2.0"
|
|
40
40
|
},
|
|
41
41
|
"engines": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"src-DapzDTGv.mjs","names":["resolveModule"],"sources":["../src/config.ts","../src/index.ts"],"sourcesContent":["import type { CliConfig } from \"./index.ts\";\nimport { resolveIgnoreFiles } from \"@dirwell/core\";\n\ntype RecordValue = Record<string, unknown>;\nfunction object(value: unknown, name: string): RecordValue {\n if (typeof value !== \"object\" || value === null || Array.isArray(value))\n throw new TypeError(`${name} must be an object`);\n return value as RecordValue;\n}\nfunction distance(left: string, right: string): number {\n let previous = Array.from({ length: right.length + 1 }, (_, index) => index);\n for (let i = 1; i <= left.length; i++) {\n const current = [i];\n for (let j = 1; j <= right.length; j++)\n current[j] = Math.min(\n current[j - 1]! + 1,\n previous[j]! + 1,\n previous[j - 1]! + (left[i - 1] === right[j - 1] ? 0 : 1),\n );\n previous = current;\n }\n return previous[right.length]!;\n}\nfunction keys(value: RecordValue, allowed: readonly string[], name: string): void {\n for (const key of Object.keys(value)) {\n if (allowed.includes(key)) continue;\n const suggestion = allowed\n .map((candidate) => ({ candidate, distance: distance(key, candidate) }))\n .sort((left, right) => left.distance - right.distance)\n .find((item) => item.distance <= 2)?.candidate;\n throw new TypeError(\n `Unsupported config key \"${name}.${key}\".${suggestion ? ` Did you mean \"${name}.${suggestion}\"?` : ` Supported keys: ${allowed.join(\", \")}.`}`,\n );\n }\n}\nfunction scalar(value: unknown, type: \"string\" | \"boolean\" | \"number\" | \"function\", name: string) {\n if (\n value !== undefined &&\n (typeof value !== type || (type === \"number\" && !Number.isFinite(value)))\n )\n throw new TypeError(`${name} must be a ${type}`);\n}\nfunction patterns(value: unknown, name: string): void {\n if (\n value !== undefined &&\n typeof value !== \"string\" &&\n (!Array.isArray(value) || value.some((item) => typeof item !== \"string\"))\n )\n throw new TypeError(`${name} must be a string or an array of strings`);\n}\nfunction sort(value: unknown, name: string): void {\n if (value === undefined) return;\n const options = object(value, name);\n keys(options, [\"direction\", \"directoriesFirst\", \"field\", \"nameMode\"], name);\n scalar(options.directoriesFirst, \"boolean\", `${name}.directoriesFirst`);\n for (const [key, allowed] of Object.entries({\n direction: [\"asc\", \"desc\"],\n field: [\"name\", \"size\", \"modified\"],\n nameMode: [\"natural\", \"unicode\", \"locale\"],\n }))\n if (options[key] !== undefined && !allowed.includes(String(options[key])))\n throw new TypeError(`${name}.${key} must be one of ${allowed.join(\", \")}`);\n}\nfunction listing(value: unknown, name: string): void {\n if (value === undefined || typeof value === \"function\") return;\n const plan = object(value, name);\n keys(plan, [\"html\", \"data\"], name);\n for (const [key, sizedStrategy, sizeKey] of [\n [\"html\", \"paged\", \"pageSize\"],\n [\"data\", \"blocks\", \"blockSize\"],\n ] as const) {\n const strategy = object(plan[key], `${name}.${key}`);\n keys(\n strategy,\n strategy.strategy === sizedStrategy ? [\"strategy\", sizeKey] : [\"strategy\"],\n `${name}.${key}`,\n );\n if (strategy.strategy !== \"all\" && strategy.strategy !== sizedStrategy)\n throw new TypeError(`${name}.${key}.strategy must be all or ${sizedStrategy}`);\n if (\n strategy.strategy === sizedStrategy &&\n (!Number.isSafeInteger(strategy[sizeKey]) || Number(strategy[sizeKey]) < 1)\n )\n throw new TypeError(`${name}.${key}.${sizeKey} must be a positive integer`);\n }\n}\nconst buildKeys = {\n fileHref: true,\n entryData: true,\n dryRun: true,\n outputPolicy: true,\n ignoreFiles: true,\n metadata: true,\n base: true,\n include: true,\n exclude: true,\n mode: true,\n mirror: true,\n outputName: true,\n urlStrategy: true,\n symlinks: true,\n sort: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"build\"]>, true>;\nconst serverKeys = {\n entryData: true,\n ignoreFiles: true,\n live: true,\n blockSize: true,\n mountPath: true,\n files: true,\n pageSize: true,\n maxPageSize: true,\n fileHref: true,\n headers: true,\n authorize: true,\n include: true,\n exclude: true,\n sort: true,\n host: true,\n port: true,\n} satisfies Record<keyof NonNullable<CliConfig[\"server\"]>, true>;\n\n/** Validate both modes before filesystem publication or server startup. */\nexport function validateCliConfig(value: unknown): asserts value is CliConfig {\n const config = object(value, \"Config\");\n // Preserve the top-level diagnostic used by CLI consumers.\n for (const key of Object.keys(config))\n if (![\"theme\", \"listing\", \"build\", \"server\", \"ignoreFiles\"].includes(key))\n throw new TypeError(\n `Unsupported config key \"${key}\". Use { theme, listing, build, server }; selection rules belong in build/server.`,\n );\n listing(config.listing, \"listing\");\n resolveIgnoreFiles(config.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n for (const mode of [\"build\", \"server\"] as const) {\n if (config[mode] === undefined) continue;\n const options = object(config[mode], mode);\n keys(options, Object.keys(mode === \"build\" ? buildKeys : serverKeys), mode);\n patterns(options.include, `${mode}.include`);\n patterns(options.exclude, `${mode}.exclude`);\n resolveIgnoreFiles(options.ignoreFiles as CliConfig[\"ignoreFiles\"]);\n sort(options.sort, `${mode}.sort`);\n scalar(options.entryData, \"function\", `${mode}.entryData`);\n if (mode === \"build\") {\n scalar(options.fileHref, \"function\", \"build.fileHref\");\n for (const key of [\"base\", \"mode\", \"urlStrategy\"])\n scalar(options[key], \"string\", `${mode}.${key}`);\n scalar(options.mirror, \"boolean\", \"build.mirror\");\n scalar(options.dryRun, \"boolean\", \"build.dryRun\");\n if (\n options.outputPolicy !== undefined &&\n options.outputPolicy !== \"owned\" &&\n options.outputPolicy !== \"replace\"\n )\n throw new TypeError(\"build.outputPolicy must be owned or replace\");\n scalar(options.metadata, \"function\", \"build.metadata\");\n if (\n options.outputName !== undefined &&\n typeof options.outputName !== \"string\" &&\n typeof options.outputName !== \"function\"\n )\n throw new TypeError(\"build.outputName must be a string or function\");\n if (options.symlinks !== undefined) {\n const links = object(options.symlinks, \"build.symlinks\");\n keys(links, [\"follow\", \"boundary\", \"onCycle\"], \"build.symlinks\");\n scalar(links.follow, \"boolean\", \"build.symlinks.follow\");\n for (const [key, allowed] of Object.entries({\n boundary: [\"root\", \"anywhere\"],\n onCycle: [\"error\", \"skip\"],\n }))\n if (links[key] !== undefined && !allowed.includes(String(links[key])))\n throw new TypeError(`build.symlinks.${key} must be one of ${allowed.join(\", \")}`);\n }\n } else {\n for (const key of [\"host\", \"mountPath\"]) scalar(options[key], \"string\", `server.${key}`);\n for (const key of [\"port\", \"pageSize\", \"maxPageSize\", \"blockSize\"])\n scalar(options[key], \"number\", `server.${key}`);\n for (const key of [\"fileHref\", \"headers\", \"authorize\"])\n scalar(options[key], \"function\", `server.${key}`);\n if (\n options.files !== undefined &&\n options.files !== false &&\n ![\"media\", \"attachment\", \"inline\"].includes(String(options.files))\n )\n throw new TypeError(\"server.files must be false, media, attachment or inline\");\n if (options.live !== undefined && options.live !== false) {\n const live = object(options.live, \"server.live\");\n keys(live, [\"intervalMs\", \"maxClients\"], \"server.live\");\n for (const key of [\"intervalMs\", \"maxClients\"])\n scalar(live[key], \"number\", `server.live.${key}`);\n }\n }\n }\n}\n\n/** Preserve literal types and validate JavaScript configuration at definition time. */\nexport function defineConfig<const T extends CliConfig>(config: T): T {\n validateCliConfig(config);\n return config;\n}\n","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, resolveIgnoreFiles } from \"@dirwell/core\";\nimport type { ExplorerTheme, ListingPolicy, GenerateOptions } from \"@dirwell/core\";\nimport { createPlainTheme } from \"@dirwell/theme-plain\";\nimport { startExplorerServer } from \"@dirwell/server/node\";\nimport { validateCliConfig } from \"./config.ts\";\nexport { defineConfig } from \"./config.ts\";\n\nexport interface CliConfig {\n readonly ignoreFiles?: GenerateOptions[\"ignoreFiles\"];\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>\nBuild safety: --dry-run, --force (-f) replaces an unowned nonempty output.\nSelection: --ignore-file <path> (repeatable), --no-ignore disables ignore files.\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 force: { type: \"boolean\", short: \"f\" },\n \"dry-run\": { type: \"boolean\" },\n \"ignore-file\": { type: \"string\", multiple: true },\n \"no-ignore\": { type: \"boolean\" },\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\"\n ? [\"host\", \"port\", \"mount\", \"live\"]\n : [\"out\", \"mode\", \"urls\", \"base\", \"force\", \"dry-run\"];\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\", \"ignoreFiles\"].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 validateCliConfig(module.default);\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 (parsed.values[\"no-ignore\"] && parsed.values[\"ignore-file\"])\n throw new TypeError(\"--no-ignore cannot be combined with --ignore-file\");\n const modeOptions = command === \"build\" ? config.build : config.server;\n const ignoreFiles = resolveIgnoreFiles(\n parsed.values[\"no-ignore\"]\n ? []\n : (parsed.values[\"ignore-file\"] ??\n modeOptions?.ignoreFiles ??\n config.ignoreFiles ?? [{ path: \".gitignore\", optional: true }]),\n cwd,\n );\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 const report = await generateExplorer({\n ...config.build,\n sourceDir: root,\n outputDir: path.resolve(cwd, parsed.values.out),\n theme,\n ignoreFiles,\n ...(parsed.values.force ? { outputPolicy: \"replace\" } : {}),\n ...(parsed.values[\"dry-run\"] ? { dryRun: true } : {}),\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 if (report.dryRun) log(JSON.stringify(report, null, 2));\n else {\n log(`Built ${parsed.values.out}`);\n log(\n `${report.selectedPaths.length} selected paths; ${report.pages.filter((page) => page.outputName !== null).length} directory pages; ${report.pages.filter((page) => page.outputName === null).length} skipped pages.`,\n );\n for (const page of report.pages)\n if (page.directory === \"\" || page.outputName === null)\n log(\n page.outputName === null\n ? `Skipped explorer page: ${page.directory || \"/\"}`\n : `Explorer entry: ${path.join(report.outputDir, page.outputName)}`,\n );\n }\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 ignoreFiles,\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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"}
|