@vp-tw/dirwell 0.0.0-stage → 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -0
  3. package/README.md +182 -2
  4. package/THEME_PACKAGE_CONTRACT.md +64 -0
  5. package/THEMING.md +88 -0
  6. package/THIRD_PARTY_NOTICES.md +14 -0
  7. package/dist/bin.d.mts +1 -0
  8. package/dist/bin.mjs +306 -0
  9. package/dist/bin.mjs.map +1 -0
  10. package/dist/bun.d.mts +7 -0
  11. package/dist/bun.mjs +7 -0
  12. package/dist/bun.mjs.map +1 -0
  13. package/dist/config-BJG2Zc3Q.d.mts +34 -0
  14. package/dist/dev-server-Q7NC1wiB.mjs +132 -0
  15. package/dist/dev-server-Q7NC1wiB.mjs.map +1 -0
  16. package/dist/esbuild.d.mts +7 -0
  17. package/dist/esbuild.mjs +7 -0
  18. package/dist/esbuild.mjs.map +1 -0
  19. package/dist/farm.d.mts +7 -0
  20. package/dist/farm.mjs +7 -0
  21. package/dist/farm.mjs.map +1 -0
  22. package/dist/generator-CtX3iHhK.mjs +1783 -0
  23. package/dist/generator-CtX3iHhK.mjs.map +1 -0
  24. package/dist/index.d.mts +47 -0
  25. package/dist/index.mjs +74 -0
  26. package/dist/index.mjs.map +1 -0
  27. package/dist/model-CWjYM17j.d.mts +100 -0
  28. package/dist/rolldown.d.mts +7 -0
  29. package/dist/rolldown.mjs +7 -0
  30. package/dist/rolldown.mjs.map +1 -0
  31. package/dist/rollup.d.mts +7 -0
  32. package/dist/rollup.mjs +7 -0
  33. package/dist/rollup.mjs.map +1 -0
  34. package/dist/rsbuild.d.mts +7 -0
  35. package/dist/rsbuild.mjs +7 -0
  36. package/dist/rsbuild.mjs.map +1 -0
  37. package/dist/rspack.d.mts +7 -0
  38. package/dist/rspack.mjs +7 -0
  39. package/dist/rspack.mjs.map +1 -0
  40. package/dist/theme-components-CXzvdQY-.mjs +8 -0
  41. package/dist/theme-components-CXzvdQY-.mjs.map +1 -0
  42. package/dist/theme-components-DW0fv9_C.d.mts +141 -0
  43. package/dist/theme-components.d.mts +2 -0
  44. package/dist/theme-components.mjs +2 -0
  45. package/dist/theme-runtime.js +1128 -0
  46. package/dist/theme-worker.js +27 -0
  47. package/dist/unplugin-CKRUCTgW.d.mts +16 -0
  48. package/dist/unplugin-p6AXhL4F.mjs +421 -0
  49. package/dist/unplugin-p6AXhL4F.mjs.map +1 -0
  50. package/dist/unplugin.d.mts +2 -0
  51. package/dist/unplugin.mjs +2 -0
  52. package/dist/vite-JBsvRvuW.mjs +282 -0
  53. package/dist/vite-JBsvRvuW.mjs.map +1 -0
  54. package/dist/vite.d.mts +12 -0
  55. package/dist/vite.mjs +2 -0
  56. package/dist/vscode-icons/NOTICE.txt +16 -0
  57. package/dist/vscode-icons/default_file.svg +1 -0
  58. package/dist/vscode-icons/default_folder.svg +1 -0
  59. package/dist/vscode-icons/file_type_css.svg +1 -0
  60. package/dist/vscode-icons/file_type_html.svg +1 -0
  61. package/dist/vscode-icons/file_type_image.svg +1 -0
  62. package/dist/vscode-icons/file_type_js.svg +1 -0
  63. package/dist/vscode-icons/file_type_json.svg +1 -0
  64. package/dist/vscode-icons/file_type_markdown.svg +1 -0
  65. package/dist/vscode-icons/file_type_pdf2.svg +1 -0
  66. package/dist/vscode-icons/file_type_shell.svg +1 -0
  67. package/dist/vscode-icons/file_type_text.svg +1 -0
  68. package/dist/vscode-icons/file_type_typescript.svg +1 -0
  69. package/dist/vscode-icons/file_type_yaml.svg +1 -0
  70. package/dist/vscode-icons/file_type_zip.svg +1 -0
  71. package/dist/webpack.d.mts +7 -0
  72. package/dist/webpack.mjs +7 -0
  73. package/dist/webpack.mjs.map +1 -0
  74. package/package.json +133 -4
package/dist/bin.mjs ADDED
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+ import { m as resolveGenerateOptions, p as loadDirwellConfig, r as generateExplorer } from "./generator-CtX3iHhK.mjs";
3
+ import { t as createExplorerDevServer } from "./dev-server-Q7NC1wiB.mjs";
4
+ import path from "node:path";
5
+ import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
6
+ import { closeSync, openSync } from "node:fs";
7
+ import { defineCommand, runMain } from "citty";
8
+ import { spawn } from "node:child_process";
9
+ //#region src/daemon.ts
10
+ function statePaths(cwd) {
11
+ const directory = path.join(cwd, ".dirwell");
12
+ return {
13
+ directory,
14
+ log: path.join(directory, "daemon.log"),
15
+ state: path.join(directory, "daemon.json")
16
+ };
17
+ }
18
+ function isRunning(pid) {
19
+ try {
20
+ process.kill(pid, 0);
21
+ return true;
22
+ } catch {
23
+ return false;
24
+ }
25
+ }
26
+ async function readDaemonState(cwd) {
27
+ const { state } = statePaths(cwd);
28
+ try {
29
+ const value = JSON.parse(await readFile(state, "utf8"));
30
+ if (typeof value !== "object" || value === null) return null;
31
+ const candidate = value;
32
+ if (typeof candidate.pid !== "number" || typeof candidate.directory !== "string" || typeof candidate.logFile !== "string" || typeof candidate.startedAt !== "string") return null;
33
+ return candidate;
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+ async function daemonStatus(cwd) {
39
+ const state = await readDaemonState(cwd);
40
+ return {
41
+ running: state !== null && isRunning(state.pid),
42
+ state
43
+ };
44
+ }
45
+ async function startDaemon(options) {
46
+ const current = await daemonStatus(options.cwd);
47
+ if (current.running && current.state !== null) throw new Error(`Dirwell daemon is already running with PID ${current.state.pid}`);
48
+ const paths = statePaths(options.cwd);
49
+ await mkdir(paths.directory, { recursive: true });
50
+ const args = [
51
+ options.binPath,
52
+ "serve",
53
+ options.directory,
54
+ "--cwd",
55
+ options.cwd
56
+ ];
57
+ if (options.base !== void 0) args.push("--base", options.base);
58
+ if (options.host !== void 0) args.push("--host", options.host);
59
+ if (options.mode !== void 0) args.push("--mode", options.mode);
60
+ if (options.outDir !== void 0) args.push("--outDir", options.outDir);
61
+ if (options.port !== void 0) args.push("--port", options.port);
62
+ if (options.urls !== void 0) args.push("--urls", options.urls);
63
+ const logDescriptor = openSync(paths.log, "a");
64
+ const child = spawn(process.execPath, args, {
65
+ cwd: options.cwd,
66
+ detached: true,
67
+ env: {
68
+ ...process.env,
69
+ DIRWELL_DAEMON: "1"
70
+ },
71
+ stdio: [
72
+ "ignore",
73
+ logDescriptor,
74
+ logDescriptor
75
+ ]
76
+ });
77
+ closeSync(logDescriptor);
78
+ if (child.pid === void 0) throw new Error("Failed to start Dirwell daemon");
79
+ child.unref();
80
+ await new Promise((resolve) => setTimeout(resolve, 150));
81
+ if (!isRunning(child.pid)) throw new Error(`Dirwell daemon exited during startup; inspect ${paths.log}`);
82
+ const state = {
83
+ directory: path.resolve(options.cwd, options.directory),
84
+ logFile: paths.log,
85
+ pid: child.pid,
86
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
87
+ };
88
+ await writeFile(paths.state, `${JSON.stringify(state, null, 2)}\n`);
89
+ return state;
90
+ }
91
+ async function stopDaemon(cwd) {
92
+ const status = await daemonStatus(cwd);
93
+ if (status.state === null) return null;
94
+ if (status.running) process.kill(status.state.pid, "SIGTERM");
95
+ await rm(statePaths(cwd).state, { force: true });
96
+ return status.state;
97
+ }
98
+ //#endregion
99
+ //#region package.json
100
+ var version = "0.1.0-alpha.0";
101
+ //#endregion
102
+ //#region src/cli.ts
103
+ const commonArguments = {
104
+ directory: {
105
+ type: "positional",
106
+ description: "Directory to explore",
107
+ default: "."
108
+ },
109
+ base: {
110
+ type: "string",
111
+ description: "Deployment base path or URL"
112
+ },
113
+ cwd: {
114
+ type: "string",
115
+ description: "Directory used to discover dirwell.config.ts",
116
+ default: "."
117
+ },
118
+ mode: {
119
+ type: "enum",
120
+ description: "Output mode",
121
+ options: ["ssg", "mpa"]
122
+ },
123
+ urls: {
124
+ type: "enum",
125
+ description: "Generated URL strategy",
126
+ options: [
127
+ "relative",
128
+ "base",
129
+ "html-base"
130
+ ]
131
+ }
132
+ };
133
+ const serveArguments = {
134
+ ...commonArguments,
135
+ host: {
136
+ type: "string",
137
+ description: "Listening host"
138
+ },
139
+ outDir: {
140
+ type: "string",
141
+ alias: "o",
142
+ description: "Preview output directory"
143
+ },
144
+ port: {
145
+ type: "string",
146
+ alias: "p",
147
+ description: "Listening port"
148
+ }
149
+ };
150
+ async function runServe(inputs) {
151
+ const cwd = pathFromProcess(inputs.cwd);
152
+ const { config } = await loadDirwellConfig(cwd, "serve");
153
+ const outputDir = inputs.outDir ?? config.outDir ?? ".dirwell-preview";
154
+ const options = resolveGenerateOptions(cwd, config, {
155
+ ...inputs.base === void 0 ? {} : { base: inputs.base },
156
+ root: inputs.directory,
157
+ outDir: outputDir,
158
+ ...inputs.mode === void 0 ? {} : { mode: inputs.mode },
159
+ ...inputs.urls === void 0 ? {} : { urls: inputs.urls }
160
+ });
161
+ const configuredPort = inputs.port ?? process.env.PORT;
162
+ const port = configuredPort === void 0 ? config.server?.port ?? 4173 : Number(configuredPort);
163
+ if (!Number.isInteger(port) || port < 0 || port > 65535) throw new TypeError("port must be an integer between 0 and 65535");
164
+ const server = await createExplorerDevServer({
165
+ ...options,
166
+ host: inputs.host ?? process.env.HOST ?? config.server?.host ?? "127.0.0.1",
167
+ port
168
+ });
169
+ console.log(`Dirwell is watching ${options.sourceDir}`);
170
+ console.log(`Local: ${process.env.PORTLESS_URL ?? server.url}`);
171
+ }
172
+ const buildCommand = defineCommand({
173
+ meta: {
174
+ name: "build",
175
+ description: "Generate a deployable static file explorer"
176
+ },
177
+ args: {
178
+ ...commonArguments,
179
+ outDir: {
180
+ type: "string",
181
+ alias: "o",
182
+ description: "Output directory"
183
+ }
184
+ },
185
+ async run({ args }) {
186
+ const cwd = pathFromProcess(args.cwd);
187
+ const { config } = await loadDirwellConfig(cwd, "build");
188
+ const options = resolveGenerateOptions(cwd, config, {
189
+ ...args.base === void 0 ? {} : { base: args.base },
190
+ root: args.directory,
191
+ ...args.outDir === void 0 ? {} : { outDir: args.outDir },
192
+ ...args.mode === void 0 ? {} : { mode: args.mode },
193
+ ...args.urls === void 0 ? {} : { urls: args.urls }
194
+ });
195
+ await generateExplorer(options);
196
+ console.log(`Built ${options.sourceDir} → ${options.outputDir}`);
197
+ }
198
+ });
199
+ const serveCommand = defineCommand({
200
+ meta: {
201
+ name: "serve",
202
+ alias: "dev",
203
+ description: "Watch files and serve the explorer with live reload"
204
+ },
205
+ args: serveArguments,
206
+ async run({ args }) {
207
+ await runServe(args);
208
+ }
209
+ });
210
+ const daemonStartCommand = defineCommand({
211
+ meta: {
212
+ name: "start",
213
+ description: "Start a detached file explorer server"
214
+ },
215
+ args: serveArguments,
216
+ async run({ args }) {
217
+ const cwd = pathFromProcess(args.cwd);
218
+ const binPath = process.argv[1];
219
+ if (binPath === void 0) throw new Error("Cannot resolve the Dirwell executable");
220
+ const state = await startDaemon({
221
+ binPath,
222
+ cwd,
223
+ directory: args.directory,
224
+ ...args.base === void 0 ? {} : { base: args.base },
225
+ ...args.host === void 0 ? {} : { host: args.host },
226
+ ...args.mode === void 0 ? {} : { mode: args.mode },
227
+ ...args.outDir === void 0 ? {} : { outDir: args.outDir },
228
+ ...args.port === void 0 ? {} : { port: args.port },
229
+ ...args.urls === void 0 ? {} : { urls: args.urls }
230
+ });
231
+ console.log(`Started Dirwell daemon with PID ${state.pid}`);
232
+ console.log(`Log: ${state.logFile}`);
233
+ }
234
+ });
235
+ const daemonStatusCommand = defineCommand({
236
+ meta: {
237
+ name: "status",
238
+ description: "Show daemon status"
239
+ },
240
+ args: { cwd: commonArguments.cwd },
241
+ async run({ args }) {
242
+ const status = await daemonStatus(pathFromProcess(args.cwd));
243
+ if (!status.running || status.state === null) {
244
+ console.log("Dirwell daemon is not running");
245
+ return;
246
+ }
247
+ console.log(`Dirwell daemon is running with PID ${status.state.pid}`);
248
+ console.log(`Directory: ${status.state.directory}`);
249
+ console.log(`Log: ${status.state.logFile}`);
250
+ }
251
+ });
252
+ const daemonStopCommand = defineCommand({
253
+ meta: {
254
+ name: "stop",
255
+ description: "Stop the detached file explorer server"
256
+ },
257
+ args: { cwd: commonArguments.cwd },
258
+ async run({ args }) {
259
+ const state = await stopDaemon(pathFromProcess(args.cwd));
260
+ console.log(state === null ? "Dirwell daemon is not running" : `Stopped PID ${state.pid}`);
261
+ }
262
+ });
263
+ const daemonCommand = defineCommand({
264
+ meta: {
265
+ name: "daemon",
266
+ description: "Manage a detached file explorer server"
267
+ },
268
+ default: "start",
269
+ subCommands: {
270
+ start: daemonStartCommand,
271
+ status: daemonStatusCommand,
272
+ stop: daemonStopCommand
273
+ }
274
+ });
275
+ const mainCommand = defineCommand({
276
+ meta: {
277
+ name: "dirwell",
278
+ version,
279
+ description: "Build and serve a static file explorer"
280
+ },
281
+ default: "serve",
282
+ subCommands: {
283
+ build: buildCommand,
284
+ daemon: daemonCommand,
285
+ serve: serveCommand
286
+ }
287
+ });
288
+ function pathFromProcess(value) {
289
+ return path.resolve(process.cwd(), value);
290
+ }
291
+ //#endregion
292
+ //#region src/bin.ts
293
+ const rawArgs = process.argv.slice(2);
294
+ const firstArgument = rawArgs[0];
295
+ const knownCommands = /* @__PURE__ */ new Set([
296
+ "build",
297
+ "daemon",
298
+ "dev",
299
+ "serve"
300
+ ]);
301
+ const normalizedArgs = firstArgument !== void 0 && !firstArgument.startsWith("-") && !knownCommands.has(firstArgument) ? ["serve", ...rawArgs] : rawArgs;
302
+ await runMain(mainCommand, { rawArgs: normalizedArgs });
303
+ //#endregion
304
+ export {};
305
+
306
+ //# sourceMappingURL=bin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.mjs","names":["packageMetadata.version"],"sources":["../src/daemon.ts","../package.json","../src/cli.ts","../src/bin.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { closeSync, openSync } from \"node:fs\";\nimport { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport interface DaemonState {\n readonly directory: string;\n readonly logFile: string;\n readonly pid: number;\n readonly startedAt: string;\n}\n\nfunction statePaths(cwd: string): { directory: string; log: string; state: string } {\n const directory = path.join(cwd, \".dirwell\");\n return {\n directory,\n log: path.join(directory, \"daemon.log\"),\n state: path.join(directory, \"daemon.json\"),\n };\n}\n\nfunction isRunning(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function readDaemonState(cwd: string): Promise<DaemonState | null> {\n const { state } = statePaths(cwd);\n try {\n const value: unknown = JSON.parse(await readFile(state, \"utf8\"));\n if (typeof value !== \"object\" || value === null) return null;\n const candidate = value as Partial<DaemonState>;\n if (\n typeof candidate.pid !== \"number\" ||\n typeof candidate.directory !== \"string\" ||\n typeof candidate.logFile !== \"string\" ||\n typeof candidate.startedAt !== \"string\"\n ) {\n return null;\n }\n return candidate as DaemonState;\n } catch {\n return null;\n }\n}\n\nexport async function daemonStatus(cwd: string): Promise<{\n readonly running: boolean;\n readonly state: DaemonState | null;\n}> {\n const state = await readDaemonState(cwd);\n return { running: state !== null && isRunning(state.pid), state };\n}\n\nexport async function startDaemon(options: {\n readonly base?: string;\n readonly binPath: string;\n readonly cwd: string;\n readonly directory: string;\n readonly host?: string;\n readonly mode?: string;\n readonly outDir?: string;\n readonly port?: string;\n readonly urls?: string;\n}): Promise<DaemonState> {\n const current = await daemonStatus(options.cwd);\n if (current.running && current.state !== null) {\n throw new Error(`Dirwell daemon is already running with PID ${current.state.pid}`);\n }\n\n const paths = statePaths(options.cwd);\n await mkdir(paths.directory, { recursive: true });\n const args = [options.binPath, \"serve\", options.directory, \"--cwd\", options.cwd];\n if (options.base !== undefined) args.push(\"--base\", options.base);\n if (options.host !== undefined) args.push(\"--host\", options.host);\n if (options.mode !== undefined) args.push(\"--mode\", options.mode);\n if (options.outDir !== undefined) args.push(\"--outDir\", options.outDir);\n if (options.port !== undefined) args.push(\"--port\", options.port);\n if (options.urls !== undefined) args.push(\"--urls\", options.urls);\n\n const logDescriptor = openSync(paths.log, \"a\");\n const child = spawn(process.execPath, args, {\n cwd: options.cwd,\n detached: true,\n env: { ...process.env, DIRWELL_DAEMON: \"1\" },\n stdio: [\"ignore\", logDescriptor, logDescriptor],\n });\n closeSync(logDescriptor);\n if (child.pid === undefined) throw new Error(\"Failed to start Dirwell daemon\");\n child.unref();\n\n await new Promise((resolve) => setTimeout(resolve, 150));\n if (!isRunning(child.pid)) {\n throw new Error(`Dirwell daemon exited during startup; inspect ${paths.log}`);\n }\n\n const state: DaemonState = {\n directory: path.resolve(options.cwd, options.directory),\n logFile: paths.log,\n pid: child.pid,\n startedAt: new Date().toISOString(),\n };\n await writeFile(paths.state, `${JSON.stringify(state, null, 2)}\\n`);\n return state;\n}\n\nexport async function stopDaemon(cwd: string): Promise<DaemonState | null> {\n const status = await daemonStatus(cwd);\n if (status.state === null) return null;\n if (status.running) process.kill(status.state.pid, \"SIGTERM\");\n await rm(statePaths(cwd).state, { force: true });\n return status.state;\n}\n","","import { defineCommand } from \"citty\";\nimport path from \"node:path\";\nimport { generateExplorer } from \"./generator.ts\";\nimport { createExplorerDevServer } from \"./dev-server.ts\";\nimport { loadDirwellConfig, resolveGenerateOptions } from \"./config.ts\";\nimport { daemonStatus, startDaemon, stopDaemon } from \"./daemon.ts\";\nimport packageMetadata from \"../package.json\" with { type: \"json\" };\n\nconst directoryArgument = {\n type: \"positional\" as const,\n description: \"Directory to explore\",\n default: \".\",\n};\n\nconst commonArguments = {\n directory: directoryArgument,\n base: {\n type: \"string\" as const,\n description: \"Deployment base path or URL\",\n },\n cwd: {\n type: \"string\" as const,\n description: \"Directory used to discover dirwell.config.ts\",\n default: \".\",\n },\n mode: {\n type: \"enum\" as const,\n description: \"Output mode\",\n options: [\"ssg\", \"mpa\"],\n },\n urls: {\n type: \"enum\" as const,\n description: \"Generated URL strategy\",\n options: [\"relative\", \"base\", \"html-base\"],\n },\n};\n\nconst serveArguments = {\n ...commonArguments,\n host: { type: \"string\" as const, description: \"Listening host\" },\n outDir: {\n type: \"string\" as const,\n alias: \"o\",\n description: \"Preview output directory\",\n },\n port: { type: \"string\" as const, alias: \"p\", description: \"Listening port\" },\n};\n\ninterface ServeInputs {\n readonly base: string | undefined;\n readonly cwd: string;\n readonly directory: string;\n readonly host: string | undefined;\n readonly mode: string | undefined;\n readonly outDir: string | undefined;\n readonly port: string | undefined;\n readonly urls: string | undefined;\n}\n\nasync function runServe(inputs: ServeInputs): Promise<void> {\n const cwd = pathFromProcess(inputs.cwd);\n const { config } = await loadDirwellConfig(cwd, \"serve\");\n const outputDir = inputs.outDir ?? config.outDir ?? \".dirwell-preview\";\n const options = resolveGenerateOptions(cwd, config, {\n ...(inputs.base === undefined ? {} : { base: inputs.base }),\n root: inputs.directory,\n outDir: outputDir,\n ...(inputs.mode === undefined ? {} : { mode: inputs.mode as \"mpa\" | \"ssg\" }),\n ...(inputs.urls === undefined\n ? {}\n : { urls: inputs.urls as \"base\" | \"html-base\" | \"relative\" }),\n });\n const configuredPort = inputs.port ?? process.env.PORT;\n const port =\n configuredPort === undefined ? (config.server?.port ?? 4173) : Number(configuredPort);\n if (!Number.isInteger(port) || port < 0 || port > 65_535) {\n throw new TypeError(\"port must be an integer between 0 and 65535\");\n }\n const server = await createExplorerDevServer({\n ...options,\n host: inputs.host ?? process.env.HOST ?? config.server?.host ?? \"127.0.0.1\",\n port,\n });\n console.log(`Dirwell is watching ${options.sourceDir}`);\n console.log(`Local: ${process.env.PORTLESS_URL ?? server.url}`);\n}\n\nexport const buildCommand = defineCommand({\n meta: { name: \"build\", description: \"Generate a deployable static file explorer\" },\n args: {\n ...commonArguments,\n outDir: {\n type: \"string\",\n alias: \"o\",\n description: \"Output directory\",\n },\n },\n async run({ args }) {\n const cwd = pathFromProcess(args.cwd);\n const { config } = await loadDirwellConfig(cwd, \"build\");\n const options = resolveGenerateOptions(cwd, config, {\n ...(args.base === undefined ? {} : { base: args.base }),\n root: args.directory,\n ...(args.outDir === undefined ? {} : { outDir: args.outDir }),\n ...(args.mode === undefined ? {} : { mode: args.mode as \"mpa\" | \"ssg\" }),\n ...(args.urls === undefined ? {} : { urls: args.urls as \"base\" | \"html-base\" | \"relative\" }),\n });\n await generateExplorer(options);\n console.log(`Built ${options.sourceDir} → ${options.outputDir}`);\n },\n});\n\nexport const serveCommand = defineCommand({\n meta: {\n name: \"serve\",\n alias: \"dev\",\n description: \"Watch files and serve the explorer with live reload\",\n },\n args: serveArguments,\n async run({ args }) {\n await runServe(args);\n },\n});\n\nconst daemonStartCommand = defineCommand({\n meta: { name: \"start\", description: \"Start a detached file explorer server\" },\n args: serveArguments,\n async run({ args }) {\n const cwd = pathFromProcess(args.cwd);\n const binPath = process.argv[1];\n if (binPath === undefined) throw new Error(\"Cannot resolve the Dirwell executable\");\n const state = await startDaemon({\n binPath,\n cwd,\n directory: args.directory,\n ...(args.base === undefined ? {} : { base: args.base }),\n ...(args.host === undefined ? {} : { host: args.host }),\n ...(args.mode === undefined ? {} : { mode: args.mode }),\n ...(args.outDir === undefined ? {} : { outDir: args.outDir }),\n ...(args.port === undefined ? {} : { port: args.port }),\n ...(args.urls === undefined ? {} : { urls: args.urls }),\n });\n console.log(`Started Dirwell daemon with PID ${state.pid}`);\n console.log(`Log: ${state.logFile}`);\n },\n});\n\nconst daemonStatusCommand = defineCommand({\n meta: { name: \"status\", description: \"Show daemon status\" },\n args: { cwd: commonArguments.cwd },\n async run({ args }) {\n const status = await daemonStatus(pathFromProcess(args.cwd));\n if (!status.running || status.state === null) {\n console.log(\"Dirwell daemon is not running\");\n return;\n }\n console.log(`Dirwell daemon is running with PID ${status.state.pid}`);\n console.log(`Directory: ${status.state.directory}`);\n console.log(`Log: ${status.state.logFile}`);\n },\n});\n\nconst daemonStopCommand = defineCommand({\n meta: { name: \"stop\", description: \"Stop the detached file explorer server\" },\n args: { cwd: commonArguments.cwd },\n async run({ args }) {\n const state = await stopDaemon(pathFromProcess(args.cwd));\n console.log(state === null ? \"Dirwell daemon is not running\" : `Stopped PID ${state.pid}`);\n },\n});\n\nexport const daemonCommand = defineCommand({\n meta: { name: \"daemon\", description: \"Manage a detached file explorer server\" },\n default: \"start\",\n subCommands: {\n start: daemonStartCommand,\n status: daemonStatusCommand,\n stop: daemonStopCommand,\n },\n});\n\nexport const mainCommand = defineCommand({\n meta: {\n name: \"dirwell\",\n version: packageMetadata.version,\n description: \"Build and serve a static file explorer\",\n },\n default: \"serve\",\n subCommands: {\n build: buildCommand,\n daemon: daemonCommand,\n serve: serveCommand,\n },\n});\n\nfunction pathFromProcess(value: string): string {\n return path.resolve(process.cwd(), value);\n}\n","#!/usr/bin/env node\nimport { runMain } from \"citty\";\nimport { mainCommand } from \"./cli.ts\";\n\nconst rawArgs = process.argv.slice(2);\nconst firstArgument = rawArgs[0];\nconst knownCommands = new Set([\"build\", \"daemon\", \"dev\", \"serve\"]);\nconst normalizedArgs =\n firstArgument !== undefined && !firstArgument.startsWith(\"-\") && !knownCommands.has(firstArgument)\n ? [\"serve\", ...rawArgs]\n : rawArgs;\n\nawait runMain(mainCommand, { rawArgs: normalizedArgs });\n"],"mappings":"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"}
package/dist/bun.d.mts ADDED
@@ -0,0 +1,7 @@
1
+ import { n as DirwellPluginOptions, t as DirwellPluginInput } from "./unplugin-CKRUCTgW.mjs";
2
+ import { UnpluginInstance } from "unplugin";
3
+ //#region src/bun.d.ts
4
+ declare const plugin: UnpluginInstance<DirwellPluginInput, true>["bun"];
5
+ //#endregion
6
+ export { type DirwellPluginOptions, plugin as default };
7
+ //# sourceMappingURL=bun.d.mts.map
package/dist/bun.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import { t as unplugin } from "./unplugin-p6AXhL4F.mjs";
2
+ //#region src/bun.ts
3
+ const plugin = unplugin.bun;
4
+ //#endregion
5
+ export { plugin as default };
6
+
7
+ //# sourceMappingURL=bun.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun.mjs","names":[],"sources":["../src/bun.ts"],"sourcesContent":["import type { UnpluginInstance } from \"unplugin\";\nimport unplugin, { type DirwellPluginInput } from \"./unplugin.ts\";\nexport type { DirwellPluginOptions } from \"./unplugin.ts\";\nconst plugin: UnpluginInstance<DirwellPluginInput, true>[\"bun\"] = unplugin.bun;\nexport default plugin;\n"],"mappings":";;AAGA,MAAM,SAA4D,SAAS"}
@@ -0,0 +1,34 @@
1
+ import { p as SortOptions, r as ExplorerTheme, s as GenerateOptions } from "./model-CWjYM17j.mjs";
2
+ //#region src/config.d.ts
3
+ interface DirwellConfig {
4
+ readonly base?: string;
5
+ readonly include?: GenerateOptions["include"];
6
+ readonly exclude?: GenerateOptions["exclude"];
7
+ readonly extends?: string | readonly string[];
8
+ readonly mode?: "mpa" | "ssg";
9
+ readonly mirror?: boolean;
10
+ readonly outDir?: string;
11
+ readonly outputName?: GenerateOptions["outputName"];
12
+ readonly root?: string;
13
+ readonly server?: {
14
+ readonly host?: string;
15
+ readonly port?: number;
16
+ };
17
+ readonly symlinks?: GenerateOptions["symlinks"];
18
+ readonly sort?: SortOptions;
19
+ readonly theme?: ExplorerTheme;
20
+ readonly urls?: "base" | "html-base" | "relative";
21
+ }
22
+ interface DirwellConfigContext {
23
+ readonly command: "build" | "daemon" | "serve";
24
+ }
25
+ type DirwellConfigInput = DirwellConfig | ((context: DirwellConfigContext) => DirwellConfig | Promise<DirwellConfig>);
26
+ declare function defineConfig<const T extends DirwellConfigInput>(config: T): T;
27
+ declare function loadDirwellConfig(cwd: string, command: DirwellConfigContext["command"]): Promise<{
28
+ config: DirwellConfig;
29
+ configFile: string | null;
30
+ }>;
31
+ declare function resolveGenerateOptions(cwd: string, config: DirwellConfig, overrides?: Partial<Pick<DirwellConfig, "base" | "mode" | "outDir" | "root" | "urls">>): GenerateOptions;
32
+ //#endregion
33
+ export { loadDirwellConfig as a, defineConfig as i, DirwellConfigContext as n, resolveGenerateOptions as o, DirwellConfigInput as r, DirwellConfig as t };
34
+ //# sourceMappingURL=config-BJG2Zc3Q.d.mts.map
@@ -0,0 +1,132 @@
1
+ import { r as generateExplorer } from "./generator-CtX3iHhK.mjs";
2
+ import path from "node:path";
3
+ import { stat } from "node:fs/promises";
4
+ import { createReadStream, watch } from "node:fs";
5
+ import { createServer } from "node:http";
6
+ //#region src/dev-server.ts
7
+ function reloadClient(eventsPath) {
8
+ return `<script>
9
+ const events=new EventSource(${JSON.stringify(eventsPath)});
10
+ events.addEventListener('reload',()=>location.reload());
11
+ <\/script>`;
12
+ }
13
+ const contentTypes = {
14
+ ".css": "text/css; charset=utf-8",
15
+ ".html": "text/html; charset=utf-8",
16
+ ".js": "text/javascript; charset=utf-8",
17
+ ".json": "application/json; charset=utf-8",
18
+ ".svg": "image/svg+xml",
19
+ ".txt": "text/plain; charset=utf-8"
20
+ };
21
+ function send(response, status, body) {
22
+ response.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
23
+ response.end(body);
24
+ }
25
+ async function createExplorerDevServer(options) {
26
+ const outputDir = path.resolve(options.outputDir);
27
+ const mountPath = options.urlStrategy === "base" || options.urlStrategy === "html-base" ? `${new URL(options.base ?? "/", "http://dirwell.local").pathname.replace(/\/+$/, "")}/` : "/";
28
+ const eventsPath = `${mountPath}__explorer/events`;
29
+ const clients = /* @__PURE__ */ new Set();
30
+ let building = false;
31
+ let rebuildRequested = false;
32
+ let timer;
33
+ async function rebuild() {
34
+ if (building) {
35
+ rebuildRequested = true;
36
+ return;
37
+ }
38
+ building = true;
39
+ try {
40
+ await generateExplorer(options);
41
+ for (const client of clients) client.write("event: reload\ndata: updated\n\n");
42
+ } finally {
43
+ building = false;
44
+ if (rebuildRequested) {
45
+ rebuildRequested = false;
46
+ await rebuild();
47
+ }
48
+ }
49
+ }
50
+ await rebuild();
51
+ const fileWatcher = watch(options.sourceDir, { recursive: true }, () => {
52
+ clearTimeout(timer);
53
+ timer = setTimeout(() => {
54
+ rebuild().catch((error) => {
55
+ console.error("Dirwell rebuild failed; serving the last successful build", error);
56
+ });
57
+ }, 60);
58
+ });
59
+ const server = createServer(async (request, response) => {
60
+ const requestUrl = new URL(request.url ?? "/", "http://localhost");
61
+ if (requestUrl.pathname === eventsPath) {
62
+ response.writeHead(200, {
63
+ "cache-control": "no-cache",
64
+ connection: "keep-alive",
65
+ "content-type": "text/event-stream"
66
+ });
67
+ response.write(": connected\n\n");
68
+ clients.add(response);
69
+ request.on("close", () => clients.delete(response));
70
+ return;
71
+ }
72
+ if (requestUrl.pathname === mountPath.slice(0, -1)) {
73
+ response.writeHead(308, { location: mountPath });
74
+ response.end();
75
+ return;
76
+ }
77
+ if (!requestUrl.pathname.startsWith(mountPath)) {
78
+ send(response, 404, "Not found");
79
+ return;
80
+ }
81
+ let decodedPath;
82
+ try {
83
+ decodedPath = decodeURIComponent(`/${requestUrl.pathname.slice(mountPath.length)}`);
84
+ } catch {
85
+ send(response, 400, "Invalid URL encoding");
86
+ return;
87
+ }
88
+ let filePath = path.resolve(outputDir, `.${decodedPath}`);
89
+ if (!filePath.startsWith(`${outputDir}${path.sep}`) && filePath !== outputDir) {
90
+ send(response, 403, "Forbidden");
91
+ return;
92
+ }
93
+ try {
94
+ if ((await stat(filePath)).isDirectory()) filePath = path.join(filePath, "index.html");
95
+ const fileStats = await stat(filePath);
96
+ if (!fileStats.isFile()) throw new Error("Not a file");
97
+ const extension = path.extname(filePath).toLowerCase();
98
+ const headers = {
99
+ "cache-control": "no-store",
100
+ "content-type": contentTypes[extension] ?? "application/octet-stream"
101
+ };
102
+ if (extension !== ".html") headers["content-length"] = fileStats.size;
103
+ response.writeHead(200, headers);
104
+ if (extension === ".html") {
105
+ let html = "";
106
+ for await (const chunk of createReadStream(filePath, { encoding: "utf8" })) html += chunk;
107
+ response.end(html.replace("</body>", `${reloadClient(eventsPath)}</body>`));
108
+ } else createReadStream(filePath).pipe(response);
109
+ } catch {
110
+ send(response, 404, "Not found");
111
+ }
112
+ });
113
+ await new Promise((resolve, reject) => {
114
+ server.once("error", reject);
115
+ server.listen(options.port ?? 4173, options.host ?? "127.0.0.1", resolve);
116
+ });
117
+ const address = server.address();
118
+ const port = typeof address === "object" && address !== null ? address.port : options.port;
119
+ return {
120
+ url: `http://${options.host ?? "127.0.0.1"}:${port ?? 4173}${mountPath}`,
121
+ async close() {
122
+ clearTimeout(timer);
123
+ fileWatcher.close();
124
+ for (const client of clients) client.end();
125
+ await new Promise((resolve, reject) => server.close((error) => error ? reject(error) : resolve()));
126
+ }
127
+ };
128
+ }
129
+ //#endregion
130
+ export { createExplorerDevServer as t };
131
+
132
+ //# sourceMappingURL=dev-server-Q7NC1wiB.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-server-Q7NC1wiB.mjs","names":[],"sources":["../src/dev-server.ts"],"sourcesContent":["import { createReadStream, watch } from \"node:fs\";\nimport { stat } from \"node:fs/promises\";\nimport { createServer, type ServerResponse } from \"node:http\";\nimport path from \"node:path\";\nimport { generateExplorer } from \"./generator.ts\";\nimport type { GenerateOptions } from \"./model.ts\";\n\nfunction reloadClient(eventsPath: string): string {\n return `<script>\nconst events=new EventSource(${JSON.stringify(eventsPath)});\nevents.addEventListener('reload',()=>location.reload());\n</script>`;\n}\n\nconst contentTypes: Readonly<Record<string, string>> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".txt\": \"text/plain; charset=utf-8\",\n};\n\nfunction send(response: ServerResponse, status: number, body: string): void {\n response.writeHead(status, { \"content-type\": \"text/plain; charset=utf-8\" });\n response.end(body);\n}\n\nexport async function createExplorerDevServer(\n options: GenerateOptions & { readonly host?: string; readonly port?: number },\n) {\n const outputDir = path.resolve(options.outputDir);\n const mountPath =\n options.urlStrategy === \"base\" || options.urlStrategy === \"html-base\"\n ? `${new URL(options.base ?? \"/\", \"http://dirwell.local\").pathname.replace(/\\/+$/, \"\")}/`\n : \"/\";\n const eventsPath = `${mountPath}__explorer/events`;\n const clients = new Set<ServerResponse>();\n let building = false;\n let rebuildRequested = false;\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n async function rebuild(): Promise<void> {\n if (building) {\n rebuildRequested = true;\n return;\n }\n building = true;\n try {\n await generateExplorer(options);\n for (const client of clients) client.write(\"event: reload\\ndata: updated\\n\\n\");\n } finally {\n building = false;\n if (rebuildRequested) {\n rebuildRequested = false;\n await rebuild();\n }\n }\n }\n\n await rebuild();\n\n const fileWatcher = watch(options.sourceDir, { recursive: true }, () => {\n clearTimeout(timer);\n timer = setTimeout(() => {\n void rebuild().catch((error: unknown) => {\n console.error(\"Dirwell rebuild failed; serving the last successful build\", error);\n });\n }, 60);\n });\n\n const server = createServer(async (request, response) => {\n const requestUrl = new URL(request.url ?? \"/\", \"http://localhost\");\n if (requestUrl.pathname === eventsPath) {\n response.writeHead(200, {\n \"cache-control\": \"no-cache\",\n connection: \"keep-alive\",\n \"content-type\": \"text/event-stream\",\n });\n response.write(\": connected\\n\\n\");\n clients.add(response);\n request.on(\"close\", () => clients.delete(response));\n return;\n }\n\n if (requestUrl.pathname === mountPath.slice(0, -1)) {\n response.writeHead(308, { location: mountPath });\n response.end();\n return;\n }\n if (!requestUrl.pathname.startsWith(mountPath)) {\n send(response, 404, \"Not found\");\n return;\n }\n let decodedPath: string;\n try {\n decodedPath = decodeURIComponent(`/${requestUrl.pathname.slice(mountPath.length)}`);\n } catch {\n send(response, 400, \"Invalid URL encoding\");\n return;\n }\n let filePath = path.resolve(outputDir, `.${decodedPath}`);\n if (!filePath.startsWith(`${outputDir}${path.sep}`) && filePath !== outputDir) {\n send(response, 403, \"Forbidden\");\n return;\n }\n try {\n if ((await stat(filePath)).isDirectory()) filePath = path.join(filePath, \"index.html\");\n const fileStats = await stat(filePath);\n if (!fileStats.isFile()) throw new Error(\"Not a file\");\n const extension = path.extname(filePath).toLowerCase();\n const headers: Record<string, string | number> = {\n \"cache-control\": \"no-store\",\n \"content-type\": contentTypes[extension] ?? \"application/octet-stream\",\n };\n if (extension !== \".html\") headers[\"content-length\"] = fileStats.size;\n response.writeHead(200, headers);\n if (extension === \".html\") {\n let html = \"\";\n for await (const chunk of createReadStream(filePath, { encoding: \"utf8\" })) html += chunk;\n response.end(html.replace(\"</body>\", `${reloadClient(eventsPath)}</body>`));\n } else {\n createReadStream(filePath).pipe(response);\n }\n } catch {\n send(response, 404, \"Not found\");\n }\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(options.port ?? 4173, options.host ?? \"127.0.0.1\", resolve);\n });\n const address = server.address();\n const port = typeof address === \"object\" && address !== null ? address.port : options.port;\n\n return {\n url: `http://${options.host ?? \"127.0.0.1\"}:${port ?? 4173}${mountPath}`,\n async close(): Promise<void> {\n clearTimeout(timer);\n fileWatcher.close();\n for (const client of clients) client.end();\n await new Promise<void>((resolve, reject) =>\n server.close((error) => (error ? reject(error) : resolve())),\n );\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { n as DirwellPluginOptions, t as DirwellPluginInput } from "./unplugin-CKRUCTgW.mjs";
2
+ import { UnpluginInstance } from "unplugin";
3
+ //#region src/esbuild.d.ts
4
+ declare const plugin: UnpluginInstance<DirwellPluginInput, true>["esbuild"];
5
+ //#endregion
6
+ export { type DirwellPluginOptions, plugin as default };
7
+ //# sourceMappingURL=esbuild.d.mts.map
@@ -0,0 +1,7 @@
1
+ import { t as unplugin } from "./unplugin-p6AXhL4F.mjs";
2
+ //#region src/esbuild.ts
3
+ const plugin = unplugin.esbuild;
4
+ //#endregion
5
+ export { plugin as default };
6
+
7
+ //# sourceMappingURL=esbuild.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"esbuild.mjs","names":[],"sources":["../src/esbuild.ts"],"sourcesContent":["import type { UnpluginInstance } from \"unplugin\";\nimport unplugin, { type DirwellPluginInput } from \"./unplugin.ts\";\nexport type { DirwellPluginOptions } from \"./unplugin.ts\";\nconst plugin: UnpluginInstance<DirwellPluginInput, true>[\"esbuild\"] = unplugin.esbuild;\nexport default plugin;\n"],"mappings":";;AAGA,MAAM,SAAgE,SAAS"}
@@ -0,0 +1,7 @@
1
+ import { n as DirwellPluginOptions, t as DirwellPluginInput } from "./unplugin-CKRUCTgW.mjs";
2
+ import { UnpluginInstance } from "unplugin";
3
+ //#region src/farm.d.ts
4
+ declare const plugin: UnpluginInstance<DirwellPluginInput, true>["farm"];
5
+ //#endregion
6
+ export { type DirwellPluginOptions, plugin as default };
7
+ //# sourceMappingURL=farm.d.mts.map
package/dist/farm.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import { t as unplugin } from "./unplugin-p6AXhL4F.mjs";
2
+ //#region src/farm.ts
3
+ const plugin = unplugin.farm;
4
+ //#endregion
5
+ export { plugin as default };
6
+
7
+ //# sourceMappingURL=farm.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"farm.mjs","names":[],"sources":["../src/farm.ts"],"sourcesContent":["import type { UnpluginInstance } from \"unplugin\";\nimport unplugin, { type DirwellPluginInput } from \"./unplugin.ts\";\nexport type { DirwellPluginOptions } from \"./unplugin.ts\";\nconst plugin: UnpluginInstance<DirwellPluginInput, true>[\"farm\"] = unplugin.farm;\nexport default plugin;\n"],"mappings":";;AAGA,MAAM,SAA6D,SAAS"}