@dirwell/cli 0.0.1-alpha.1 → 0.0.2

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,18 +1,18 @@
1
1
  # @dirwell/cli
2
2
 
3
+ Requires Node.js 22.12 or later. [Read the guide](https://vp-tw.github.io/dirwell/getting-started/).
4
+
3
5
  Build a static file explorer or serve a live folder. Plain is included as the
4
6
  minimal default: no browser JavaScript, framework, fonts or share images.
5
7
  Other themes remain separate installs.
6
8
 
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.
9
+ The CLI supports short theme names and automatic `dirwell.config.mjs` discovery.
10
10
 
11
11
  ## Open or publish a folder
12
12
 
13
13
  ```sh
14
- npx @dirwell/cli@alpha server ./files
15
- npx @dirwell/cli@alpha build ./files --out ./generated
14
+ npx @dirwell/cli@0.0.2 server ./files
15
+ npx @dirwell/cli@0.0.2 build ./files --out ./generated
16
16
  ```
17
17
 
18
18
  No project, configuration file or global installation is required. Open the URL
@@ -29,15 +29,20 @@ explicitly when needed. `PORT` is accepted for proxies such as Portless. Port pr
29
29
  Plain remains script-free. Server mode does not generate a tree or need a
30
30
  filesystem watcher. Theme-source HMR belongs to the optional authoring package.
31
31
 
32
- ## Select or customize a theme
32
+ ## Stable selection and output controls
33
+
34
+ These controls are included in `0.0.1`.
35
+
36
+ The CLI reads an optional `.gitignore` in cwd by default. Use repeatable `--ignore-file <path>` to replace that list, or `--no-ignore` to disable it. Config supports shared `ignoreFiles`, overridden by `build.ignoreFiles` or `server.ignoreFiles`; `[]` disables defaults. Paths resolve from cwd; rules use the file's directory as their base. Use `{ path, baseDir, optional }` for shared rules or optional files. Files sharing a pattern base are concatenated in order; deeper scopes override parent scopes. Glob exclusions always win, and include globs do not bypass ignore rules.
33
37
 
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:
38
+ Builds replace missing, empty or Dirwell-marked output directories. A nonempty unmarked directory requires `--force` / `-f`; force never permits replacing source or a symlink/file destination. `--dry-run` reports selection and planned directory pages without writing output. `defineConfig` from `@dirwell/cli` provides editor types and runtime validation; nested option typos fail before build/server startup.
39
+
40
+ ## Theme selection and customization
41
+
42
+ For repeated project use, install the CLI and the theme you want:
38
43
 
39
44
  ```sh
40
- npm install --save-dev @dirwell/cli@alpha @dirwell/theme-ledger@alpha
45
+ npm install --save-dev @dirwell/cli@0.0.2 @dirwell/theme-ledger@0.0.2
41
46
  npx dirwell server ./files --theme ledger
42
47
  ```
43
48
 
@@ -63,14 +68,14 @@ Then run either command without `--theme`. `--config ./other.mjs` selects anothe
63
68
  JavaScript module. Configuration is executable trusted project code, loaded by
64
69
  Node without a config loader or TypeScript compiler. Theme precedence is
65
70
  `--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
71
+ an explicit missing file or a broken config fails visibly. If only an unsupported
67
72
  `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.
73
+ renaming or explicit `--config` selection rather than ignoring selection rules.
69
74
  Automatic discovery uses only `.mjs`; explicit config must run as native JavaScript
70
75
  on your supported Node version.
71
76
 
72
77
  The config exports only `{ theme, listing, build, server }`; unknown top-level
73
- keys fail instead of silently dropping legacy options. Put `exclude`/`include`
78
+ keys fail instead of silently dropping options. Put `exclude`/`include`
74
79
  in `build` and/or `server`. Export a resolved object, using top-level await
75
80
  for asynchronous setup. Shared `listing` accepts
76
81
  fixed plans or per-directory callbacks. `build` retains core filtering, sorting,
@@ -82,3 +87,5 @@ and archive search are not live-server features.
82
87
  pair. When only one is given, the other publication strategy is `all`.
83
88
  Mode-specific flags fail when used with the other mode. Legacy dev/daemon
84
89
  commands are unsupported; the CLI does not silently translate them.
90
+
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.
package/dist/bin.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runCli } from "./src-BDKeXf0W.mjs";
2
+ import { t as runCli } from "./src-BadHyOPx.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,7 +1,12 @@
1
1
  import { ExplorerTheme, GenerateOptions, ListingPolicy } from "@dirwell/core";
2
2
  import { startExplorerServer } from "@dirwell/server/node";
3
+ //#region src/config.d.ts
4
+ /** Preserve literal types and validate JavaScript configuration at definition time. */
5
+ export declare function defineConfig<const T extends CliConfig>(config: T): T;
6
+ //#endregion
3
7
  //#region src/index.d.ts
4
8
  export interface CliConfig {
9
+ readonly ignoreFiles?: GenerateOptions["ignoreFiles"];
5
10
  readonly build?: Omit<GenerateOptions, "sourceDir" | "outputDir" | "theme" | "listing">;
6
11
  readonly theme?: ExplorerTheme;
7
12
  readonly listing?: ListingPolicy;
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as runCli } from "./src-BDKeXf0W.mjs";
2
- export { runCli };
1
+ import { n as defineConfig, t as runCli } from "./src-BadHyOPx.mjs";
2
+ export { defineConfig, runCli };
@@ -0,0 +1,428 @@
1
+ import path from "node:path";
2
+ import { access, stat } from "node:fs/promises";
3
+ import { pathToFileURL } from "node:url";
4
+ import { resolve } from "import-meta-resolve";
5
+ import { parseArgs } from "node:util";
6
+ import { generateExplorer, resolveIgnoreFiles } from "@dirwell/core";
7
+ import { createPlainTheme } from "@dirwell/theme-plain";
8
+ import { startExplorerServer } from "@dirwell/server/node";
9
+ //#region src/config.ts
10
+ function object(value, name) {
11
+ if (typeof value !== "object" || value === null || Array.isArray(value)) throw new TypeError(`${name} must be an object`);
12
+ return value;
13
+ }
14
+ function distance(left, right) {
15
+ let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
16
+ for (let i = 1; i <= left.length; i++) {
17
+ const current = [i];
18
+ for (let j = 1; j <= right.length; j++) current[j] = Math.min(current[j - 1] + 1, previous[j] + 1, previous[j - 1] + (left[i - 1] === right[j - 1] ? 0 : 1));
19
+ previous = current;
20
+ }
21
+ return previous[right.length];
22
+ }
23
+ function keys(value, allowed, name) {
24
+ for (const key of Object.keys(value)) {
25
+ if (allowed.includes(key)) continue;
26
+ const suggestion = allowed.map((candidate) => ({
27
+ candidate,
28
+ distance: distance(key, candidate)
29
+ })).sort((left, right) => left.distance - right.distance).find((item) => item.distance <= 2)?.candidate;
30
+ throw new TypeError(`Unsupported config key "${name}.${key}".${suggestion ? ` Did you mean "${name}.${suggestion}"?` : ` Supported keys: ${allowed.join(", ")}.`}`);
31
+ }
32
+ }
33
+ function scalar(value, type, name) {
34
+ if (value !== void 0 && (typeof value !== type || type === "number" && !Number.isFinite(value))) throw new TypeError(`${name} must be a ${type}`);
35
+ }
36
+ function patterns(value, name) {
37
+ if (value !== void 0 && typeof value !== "string" && (!Array.isArray(value) || value.some((item) => typeof item !== "string"))) throw new TypeError(`${name} must be a string or an array of strings`);
38
+ }
39
+ function sort(value, name) {
40
+ if (value === void 0) return;
41
+ const options = object(value, name);
42
+ keys(options, [
43
+ "direction",
44
+ "directoriesFirst",
45
+ "field",
46
+ "nameMode"
47
+ ], name);
48
+ scalar(options.directoriesFirst, "boolean", `${name}.directoriesFirst`);
49
+ for (const [key, allowed] of Object.entries({
50
+ direction: ["asc", "desc"],
51
+ field: [
52
+ "name",
53
+ "size",
54
+ "modified"
55
+ ],
56
+ nameMode: [
57
+ "natural",
58
+ "unicode",
59
+ "locale"
60
+ ]
61
+ })) if (options[key] !== void 0 && !allowed.includes(String(options[key]))) throw new TypeError(`${name}.${key} must be one of ${allowed.join(", ")}`);
62
+ }
63
+ function listing(value, name) {
64
+ if (value === void 0 || typeof value === "function") return;
65
+ const plan = object(value, name);
66
+ keys(plan, ["html", "data"], name);
67
+ for (const [key, sizedStrategy, sizeKey] of [[
68
+ "html",
69
+ "paged",
70
+ "pageSize"
71
+ ], [
72
+ "data",
73
+ "blocks",
74
+ "blockSize"
75
+ ]]) {
76
+ const strategy = object(plan[key], `${name}.${key}`);
77
+ keys(strategy, strategy.strategy === sizedStrategy ? ["strategy", sizeKey] : ["strategy"], `${name}.${key}`);
78
+ if (strategy.strategy !== "all" && strategy.strategy !== sizedStrategy) throw new TypeError(`${name}.${key}.strategy must be all or ${sizedStrategy}`);
79
+ if (strategy.strategy === sizedStrategy && (!Number.isSafeInteger(strategy[sizeKey]) || Number(strategy[sizeKey]) < 1)) throw new TypeError(`${name}.${key}.${sizeKey} must be a positive integer`);
80
+ }
81
+ }
82
+ const buildKeys = {
83
+ fileHref: true,
84
+ entryData: true,
85
+ dryRun: true,
86
+ outputPolicy: true,
87
+ ignoreFiles: true,
88
+ metadata: true,
89
+ base: true,
90
+ include: true,
91
+ exclude: true,
92
+ mode: true,
93
+ mirror: true,
94
+ outputName: true,
95
+ urlStrategy: true,
96
+ symlinks: true,
97
+ sort: true
98
+ };
99
+ const serverKeys = {
100
+ entryData: true,
101
+ ignoreFiles: true,
102
+ live: true,
103
+ search: true,
104
+ blockSize: true,
105
+ mountPath: true,
106
+ files: true,
107
+ pageSize: true,
108
+ maxPageSize: true,
109
+ fileHref: true,
110
+ headers: true,
111
+ authorize: true,
112
+ include: true,
113
+ exclude: true,
114
+ sort: true,
115
+ host: true,
116
+ port: true
117
+ };
118
+ /** Validate both modes before filesystem publication or server startup. */
119
+ function validateCliConfig(value) {
120
+ const config = object(value, "Config");
121
+ for (const key of Object.keys(config)) if (![
122
+ "theme",
123
+ "listing",
124
+ "build",
125
+ "server",
126
+ "ignoreFiles"
127
+ ].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, listing, build, server }; selection rules belong in build/server.`);
128
+ listing(config.listing, "listing");
129
+ resolveIgnoreFiles(config.ignoreFiles);
130
+ for (const mode of ["build", "server"]) {
131
+ if (config[mode] === void 0) continue;
132
+ const options = object(config[mode], mode);
133
+ keys(options, Object.keys(mode === "build" ? buildKeys : serverKeys), mode);
134
+ patterns(options.include, `${mode}.include`);
135
+ patterns(options.exclude, `${mode}.exclude`);
136
+ resolveIgnoreFiles(options.ignoreFiles);
137
+ sort(options.sort, `${mode}.sort`);
138
+ scalar(options.entryData, "function", `${mode}.entryData`);
139
+ if (mode === "build") {
140
+ scalar(options.fileHref, "function", "build.fileHref");
141
+ for (const key of [
142
+ "base",
143
+ "mode",
144
+ "urlStrategy"
145
+ ]) scalar(options[key], "string", `${mode}.${key}`);
146
+ scalar(options.mirror, "boolean", "build.mirror");
147
+ scalar(options.dryRun, "boolean", "build.dryRun");
148
+ if (options.outputPolicy !== void 0 && options.outputPolicy !== "owned" && options.outputPolicy !== "replace") throw new TypeError("build.outputPolicy must be owned or replace");
149
+ scalar(options.metadata, "function", "build.metadata");
150
+ if (options.outputName !== void 0 && typeof options.outputName !== "string" && typeof options.outputName !== "function") throw new TypeError("build.outputName must be a string or function");
151
+ if (options.symlinks !== void 0) {
152
+ const links = object(options.symlinks, "build.symlinks");
153
+ keys(links, [
154
+ "follow",
155
+ "boundary",
156
+ "onCycle"
157
+ ], "build.symlinks");
158
+ scalar(links.follow, "boolean", "build.symlinks.follow");
159
+ for (const [key, allowed] of Object.entries({
160
+ boundary: ["root", "anywhere"],
161
+ onCycle: ["error", "skip"]
162
+ })) if (links[key] !== void 0 && !allowed.includes(String(links[key]))) throw new TypeError(`build.symlinks.${key} must be one of ${allowed.join(", ")}`);
163
+ }
164
+ } else {
165
+ for (const key of ["host", "mountPath"]) scalar(options[key], "string", `server.${key}`);
166
+ for (const key of [
167
+ "port",
168
+ "pageSize",
169
+ "maxPageSize",
170
+ "blockSize"
171
+ ]) scalar(options[key], "number", `server.${key}`);
172
+ for (const key of [
173
+ "fileHref",
174
+ "headers",
175
+ "authorize"
176
+ ]) scalar(options[key], "function", `server.${key}`);
177
+ if (options.files !== void 0 && options.files !== false && ![
178
+ "media",
179
+ "attachment",
180
+ "inline"
181
+ ].includes(String(options.files))) throw new TypeError("server.files must be false, media, attachment or inline");
182
+ if (options.live !== void 0 && options.live !== false) {
183
+ const live = object(options.live, "server.live");
184
+ keys(live, ["intervalMs", "maxClients"], "server.live");
185
+ for (const key of ["intervalMs", "maxClients"]) scalar(live[key], "number", `server.live.${key}`);
186
+ }
187
+ if (options.search !== void 0 && options.search !== false) {
188
+ const search = object(options.search, "server.search");
189
+ keys(search, [
190
+ "maxEntries",
191
+ "maxDirectories",
192
+ "maxConcurrent",
193
+ "maxResults",
194
+ "timeoutMs",
195
+ "refreshIntervalMs"
196
+ ], "server.search");
197
+ if (typeof search.maxResults === "number" && search.maxResults > 1e4) throw new TypeError("server.search.maxResults must not exceed 10000");
198
+ for (const key of Object.keys(search)) {
199
+ scalar(search[key], "number", `server.search.${key}`);
200
+ if (!Number.isSafeInteger(search[key]) || Number(search[key]) < (key === "refreshIntervalMs" ? 1e3 : 1)) throw new TypeError(`server.search.${key} must be a positive safe integer`);
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+ /** Preserve literal types and validate JavaScript configuration at definition time. */
207
+ function defineConfig(config) {
208
+ validateCliConfig(config);
209
+ return config;
210
+ }
211
+ //#endregion
212
+ //#region src/index.ts
213
+ const help = `Usage:
214
+ dirwell build <source> --out <output>
215
+ dirwell server <source> [--port 3000]
216
+
217
+ Plain is included and selected by default. Install other themes separately.
218
+ Official themes: --theme plain|ledger|crosswave
219
+ Custom themes: --theme <module>#<factory>, or a module's default export.
220
+ Reads dirwell.config.mjs in the working folder; --config <file.mjs> selects another file.
221
+ Config exports theme, listing, build options, and server hooks.
222
+ Build options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>
223
+ Build safety: --dry-run, --force (-f) replaces an unowned nonempty output.
224
+ Selection: --ignore-file <path> (repeatable), --no-ignore disables ignore files.
225
+ Server options: --host <host>, --port <port>, --mount <path>, --live
226
+ Both modes: --page-size <n>, --block-size <n>
227
+ Live server reads on request. It does not build an output tree or require watch mode.
228
+ `;
229
+ const positive = (value, name) => {
230
+ if (value === void 0) return void 0;
231
+ const number = Number(value);
232
+ if (!Number.isSafeInteger(number) || number < 1) throw new TypeError(`${name} must be a positive integer`);
233
+ return number;
234
+ };
235
+ function assertTheme(candidate) {
236
+ 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)");
237
+ }
238
+ async function loadTheme(specifier, cwd) {
239
+ const official = {
240
+ plain: "@dirwell/theme-plain#createPlainTheme",
241
+ ledger: "@dirwell/theme-ledger#createLedgerTheme",
242
+ crosswave: "@dirwell/theme-crosswave#createCrosswaveTheme"
243
+ };
244
+ if (Object.hasOwn(official, specifier)) specifier = official[specifier];
245
+ const delimiter = specifier.lastIndexOf("#"), name = delimiter < 0 ? specifier : specifier.slice(0, delimiter), exported = delimiter < 0 ? "default" : specifier.slice(delimiter + 1);
246
+ if (!name || !exported) throw new TypeError("Theme needs a module and export name");
247
+ let moduleUrl;
248
+ if (name.startsWith(".") || path.isAbsolute(name)) moduleUrl = pathToFileURL(path.resolve(cwd, name)).href;
249
+ else try {
250
+ moduleUrl = resolve(name, pathToFileURL(path.join(cwd, "package.json")).href);
251
+ } catch (error) {
252
+ if (!(error instanceof Error && "code" in error && error.code === "ERR_MODULE_NOT_FOUND")) throw error;
253
+ try {
254
+ moduleUrl = resolve(name, import.meta.url);
255
+ } catch (fallbackError) {
256
+ 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}.`);
257
+ throw fallbackError;
258
+ }
259
+ }
260
+ const module = await import(moduleUrl);
261
+ const candidate = typeof module[exported] === "function" ? await module[exported]() : module[exported];
262
+ assertTheme(candidate);
263
+ return candidate;
264
+ }
265
+ /** Parse explicit commands; return server ownership to the caller for graceful shutdown. */
266
+ async function runCli(args, cwd = process.cwd(), log = console.log) {
267
+ const parsed = parseArgs({
268
+ args: [...args],
269
+ allowPositionals: true,
270
+ options: {
271
+ help: {
272
+ type: "boolean",
273
+ short: "h"
274
+ },
275
+ force: {
276
+ type: "boolean",
277
+ short: "f"
278
+ },
279
+ "dry-run": { type: "boolean" },
280
+ "ignore-file": {
281
+ type: "string",
282
+ multiple: true
283
+ },
284
+ "no-ignore": { type: "boolean" },
285
+ live: { type: "boolean" },
286
+ theme: { type: "string" },
287
+ config: { type: "string" },
288
+ out: { type: "string" },
289
+ mode: { type: "string" },
290
+ urls: { type: "string" },
291
+ base: { type: "string" },
292
+ host: { type: "string" },
293
+ port: { type: "string" },
294
+ mount: { type: "string" },
295
+ "page-size": { type: "string" },
296
+ "block-size": { type: "string" }
297
+ }
298
+ });
299
+ if (parsed.values.help) {
300
+ log(help);
301
+ return null;
302
+ }
303
+ const [command, source] = parsed.positionals;
304
+ if (!["build", "server"].includes(command ?? "") || source === void 0 || source === "" || parsed.positionals.length !== 2) throw new TypeError(help);
305
+ const otherModeOptions = command === "build" ? [
306
+ "host",
307
+ "port",
308
+ "mount",
309
+ "live"
310
+ ] : [
311
+ "out",
312
+ "mode",
313
+ "urls",
314
+ "base",
315
+ "force",
316
+ "dry-run"
317
+ ];
318
+ for (const option of otherModeOptions) if (Object.hasOwn(parsed.values, option)) throw new TypeError(`--${option} is not supported in ${command} mode`);
319
+ let config = {};
320
+ const configPath = path.resolve(cwd, parsed.values.config ?? "dirwell.config.mjs");
321
+ let hasConfig = parsed.values.config !== void 0;
322
+ if (!hasConfig) try {
323
+ await access(configPath);
324
+ hasConfig = true;
325
+ } catch (error) {
326
+ if (!(error instanceof Error && "code" in error && error.code === "ENOENT")) throw error;
327
+ }
328
+ if (!hasConfig) for (const filename of [
329
+ "dirwell.config.ts",
330
+ "dirwell.config.js",
331
+ "dirwell.config.mts",
332
+ "dirwell.config.cjs"
333
+ ]) {
334
+ try {
335
+ await access(path.resolve(cwd, filename));
336
+ } catch (error) {
337
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") continue;
338
+ throw error;
339
+ }
340
+ 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.`);
341
+ }
342
+ if (hasConfig) {
343
+ const module = await import(pathToFileURL(configPath).href);
344
+ if (typeof module.default !== "object" || module.default === null || Array.isArray(module.default)) throw new TypeError("Config must export an object");
345
+ for (const key of Object.keys(module.default)) if (![
346
+ "theme",
347
+ "listing",
348
+ "build",
349
+ "server",
350
+ "ignoreFiles"
351
+ ].includes(key)) throw new TypeError(`Unsupported config key "${key}". Use { theme, listing, build, server }; selection rules belong in build/server.`);
352
+ if (typeof module.default.then === "function") throw new TypeError("Config must export a resolved object; use top-level await for async setup");
353
+ validateCliConfig(module.default);
354
+ config = module.default;
355
+ }
356
+ const theme = parsed.values.theme !== void 0 ? await loadTheme(parsed.values.theme, cwd) : config.theme === void 0 ? createPlainTheme() : config.theme;
357
+ assertTheme(theme);
358
+ const pageSize = positive(parsed.values["page-size"], "page-size"), blockSize = positive(parsed.values["block-size"], "block-size");
359
+ const listing = pageSize !== void 0 || blockSize !== void 0 ? {
360
+ html: pageSize === void 0 ? { strategy: "all" } : {
361
+ strategy: "paged",
362
+ pageSize
363
+ },
364
+ data: blockSize === void 0 ? { strategy: "all" } : {
365
+ strategy: "blocks",
366
+ blockSize
367
+ }
368
+ } : config.listing;
369
+ const root = path.resolve(cwd, source);
370
+ if (!(await stat(root)).isDirectory()) throw new TypeError("Source must be an existing directory");
371
+ if (parsed.values["no-ignore"] && parsed.values["ignore-file"]) throw new TypeError("--no-ignore cannot be combined with --ignore-file");
372
+ const modeOptions = command === "build" ? config.build : config.server;
373
+ const ignoreFiles = resolveIgnoreFiles(parsed.values["no-ignore"] ? [] : parsed.values["ignore-file"] ?? modeOptions?.ignoreFiles ?? config.ignoreFiles ?? [{
374
+ path: ".gitignore",
375
+ optional: true
376
+ }], cwd);
377
+ if (command === "build") {
378
+ if (parsed.values.out === void 0) throw new TypeError("Build requires --out pointing to a dedicated generated output tree");
379
+ const mode = parsed.values.mode ?? config.build?.mode ?? "ssg", urlStrategy = parsed.values.urls ?? config.build?.urlStrategy ?? "relative";
380
+ if (mode !== "ssg" && mode !== "mpa") throw new TypeError("mode must be ssg or mpa");
381
+ if (![
382
+ "relative",
383
+ "base",
384
+ "html-base"
385
+ ].includes(urlStrategy)) throw new TypeError("Unknown URL strategy");
386
+ const report = await generateExplorer({
387
+ ...config.build,
388
+ sourceDir: root,
389
+ outputDir: path.resolve(cwd, parsed.values.out),
390
+ theme,
391
+ ignoreFiles,
392
+ ...parsed.values.force ? { outputPolicy: "replace" } : {},
393
+ ...parsed.values["dry-run"] ? { dryRun: true } : {},
394
+ mode,
395
+ urlStrategy,
396
+ ...parsed.values.base === void 0 ? {} : { base: parsed.values.base },
397
+ ...listing === void 0 ? {} : { listing }
398
+ });
399
+ if (report.dryRun) log(JSON.stringify(report, null, 2));
400
+ else {
401
+ log(`Built ${parsed.values.out}`);
402
+ 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.`);
403
+ 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.outputName)}`);
404
+ }
405
+ return null;
406
+ }
407
+ const port = Number(parsed.values.port ?? process.env.PORT ?? config.server?.port ?? 0);
408
+ if (!Number.isSafeInteger(port) || port < 0 || port > 65535) throw new TypeError("port must be between 0 and 65535");
409
+ const server = await startExplorerServer({
410
+ ...config.server,
411
+ ...parsed.values.live ? { live: config.server?.live || {} } : {},
412
+ root,
413
+ theme,
414
+ ignoreFiles,
415
+ port,
416
+ ...listing === void 0 ? {} : { listing },
417
+ ...pageSize === void 0 ? {} : { pageSize },
418
+ ...blockSize === void 0 ? {} : { blockSize },
419
+ ...parsed.values.host === void 0 ? {} : { host: parsed.values.host },
420
+ ...parsed.values.mount === void 0 ? {} : { mountPath: parsed.values.mount }
421
+ });
422
+ log(`Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? ""}/browse/`);
423
+ return server;
424
+ }
425
+ //#endregion
426
+ export { defineConfig as n, runCli as t };
427
+
428
+ //# sourceMappingURL=src-BadHyOPx.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-BadHyOPx.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 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\"].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 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 { 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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"}
package/package.json CHANGED
@@ -1,7 +1,11 @@
1
1
  {
2
2
  "name": "@dirwell/cli",
3
- "version": "0.0.1-alpha.1",
3
+ "version": "0.0.2",
4
4
  "description": "Minimal file explorer build and live-server commands with Plain included.",
5
+ "homepage": "https://vp-tw.github.io/dirwell/getting-started/",
6
+ "bugs": {
7
+ "url": "https://github.com/vp-tw/dirwell/issues"
8
+ },
5
9
  "license": "MIT",
6
10
  "repository": {
7
11
  "type": "git",
@@ -26,12 +30,12 @@
26
30
  "publishConfig": {
27
31
  "access": "public",
28
32
  "provenance": true,
29
- "tag": "alpha"
33
+ "tag": "latest"
30
34
  },
31
35
  "dependencies": {
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",
36
+ "@dirwell/core": "0.0.2",
37
+ "@dirwell/server": "0.0.2",
38
+ "@dirwell/theme-plain": "0.0.2",
35
39
  "import-meta-resolve": "4.2.0"
36
40
  },
37
41
  "engines": {
@@ -1,194 +0,0 @@
1
- import path from "node:path";
2
- import { access, stat } from "node:fs/promises";
3
- import { pathToFileURL } from "node:url";
4
- import { resolve } from "import-meta-resolve";
5
- import { parseArgs } from "node:util";
6
- import { generateExplorer } from "@dirwell/core";
7
- import { createPlainTheme } from "@dirwell/theme-plain";
8
- import { startExplorerServer } from "@dirwell/server/node";
9
- //#region src/index.ts
10
- const help = `Usage:
11
- dirwell build <source> --out <output>
12
- dirwell server <source> [--port 3000]
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.
19
- Build options: --mode ssg|mpa, --urls relative|base|html-base, --base <path>
20
- Server options: --host <host>, --port <port>, --mount <path>, --live
21
- Both modes: --page-size <n>, --block-size <n>
22
- Live server reads on request. It does not build an output tree or require watch mode.
23
- `;
24
- const positive = (value, name) => {
25
- if (value === void 0) return void 0;
26
- const number = Number(value);
27
- if (!Number.isSafeInteger(number) || number < 1) throw new TypeError(`${name} must be a positive integer`);
28
- return number;
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
- }
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];
40
- const delimiter = specifier.lastIndexOf("#"), name = delimiter < 0 ? specifier : specifier.slice(0, delimiter), exported = delimiter < 0 ? "default" : specifier.slice(delimiter + 1);
41
- if (!name || !exported) throw new TypeError("Theme needs a module and export name");
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);
56
- const candidate = typeof module[exported] === "function" ? await module[exported]() : module[exported];
57
- assertTheme(candidate);
58
- return candidate;
59
- }
60
- /** Parse explicit commands; return server ownership to the caller for graceful shutdown. */
61
- async function runCli(args, cwd = process.cwd(), log = console.log) {
62
- const parsed = parseArgs({
63
- args: [...args],
64
- allowPositionals: true,
65
- options: {
66
- help: {
67
- type: "boolean",
68
- short: "h"
69
- },
70
- live: { type: "boolean" },
71
- theme: { type: "string" },
72
- config: { type: "string" },
73
- out: { type: "string" },
74
- mode: { type: "string" },
75
- urls: { type: "string" },
76
- base: { type: "string" },
77
- host: { type: "string" },
78
- port: { type: "string" },
79
- mount: { type: "string" },
80
- "page-size": { type: "string" },
81
- "block-size": { type: "string" }
82
- }
83
- });
84
- if (parsed.values.help) {
85
- log(help);
86
- return null;
87
- }
88
- const [command, source] = parsed.positionals;
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`);
102
- let config = {};
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");
135
- config = module.default;
136
- }
137
- const theme = parsed.values.theme !== void 0 ? await loadTheme(parsed.values.theme, cwd) : config.theme === void 0 ? createPlainTheme() : config.theme;
138
- assertTheme(theme);
139
- const pageSize = positive(parsed.values["page-size"], "page-size"), blockSize = positive(parsed.values["block-size"], "block-size");
140
- const listing = pageSize !== void 0 || blockSize !== void 0 ? {
141
- html: pageSize === void 0 ? { strategy: "all" } : {
142
- strategy: "paged",
143
- pageSize
144
- },
145
- data: blockSize === void 0 ? { strategy: "all" } : {
146
- strategy: "blocks",
147
- blockSize
148
- }
149
- } : config.listing;
150
- const root = path.resolve(cwd, source);
151
- if (!(await stat(root)).isDirectory()) throw new TypeError("Source must be an existing directory");
152
- if (command === "build") {
153
- if (parsed.values.out === void 0) throw new TypeError("Build requires --out pointing to a dedicated generated output tree");
154
- const mode = parsed.values.mode ?? config.build?.mode ?? "ssg", urlStrategy = parsed.values.urls ?? config.build?.urlStrategy ?? "relative";
155
- if (mode !== "ssg" && mode !== "mpa") throw new TypeError("mode must be ssg or mpa");
156
- if (![
157
- "relative",
158
- "base",
159
- "html-base"
160
- ].includes(urlStrategy)) throw new TypeError("Unknown URL strategy");
161
- await generateExplorer({
162
- ...config.build,
163
- sourceDir: root,
164
- outputDir: path.resolve(cwd, parsed.values.out),
165
- theme,
166
- mode,
167
- urlStrategy,
168
- ...parsed.values.base === void 0 ? {} : { base: parsed.values.base },
169
- ...listing === void 0 ? {} : { listing }
170
- });
171
- log(`Built ${parsed.values.out}`);
172
- return null;
173
- }
174
- const port = Number(parsed.values.port ?? process.env.PORT ?? config.server?.port ?? 0);
175
- if (!Number.isSafeInteger(port) || port < 0 || port > 65535) throw new TypeError("port must be between 0 and 65535");
176
- const server = await startExplorerServer({
177
- ...config.server,
178
- ...parsed.values.live ? { live: config.server?.live || {} } : {},
179
- root,
180
- theme,
181
- port,
182
- ...listing === void 0 ? {} : { listing },
183
- ...pageSize === void 0 ? {} : { pageSize },
184
- ...blockSize === void 0 ? {} : { blockSize },
185
- ...parsed.values.host === void 0 ? {} : { host: parsed.values.host },
186
- ...parsed.values.mount === void 0 ? {} : { mountPath: parsed.values.mount }
187
- });
188
- log(`Dirwell server: ${server.url}${parsed.values.mount ?? config.server?.mountPath ?? ""}/browse/`);
189
- return server;
190
- }
191
- //#endregion
192
- export { runCli as t };
193
-
194
- //# sourceMappingURL=src-BDKeXf0W.mjs.map
@@ -1 +0,0 @@
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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"}