vantris 0.1.0 → 0.2.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.
@@ -1,308 +0,0 @@
1
- import { pathToFileURL } from 'url';
2
- import { stat, readFile, mkdir } from 'fs/promises';
3
- import { isAbsolute, resolve, relative } from 'path';
4
-
5
- // src/shared/constants.ts
6
- var APP_NAME = "vantris";
7
- var VERSION = "0.1.0";
8
- var HTML_ENTRY_FILENAME = "index.html";
9
- var DEFAULTS = {
10
- root: ".",
11
- rootDir: "./src",
12
- publicDir: "./public",
13
- outDir: "./dist"
14
- };
15
- var CONFIG_FILENAMES = [
16
- "vantris.config.ts",
17
- "vantris.config.js",
18
- "vantris.config.mjs"
19
- ];
20
-
21
- // src/shared/errors.ts
22
- var VantrisError = class extends Error {
23
- name = "VantrisError";
24
- constructor(message, options) {
25
- super(message, options);
26
- }
27
- };
28
- var ConfigError = class extends VantrisError {
29
- name = "ConfigError";
30
- };
31
- var HtmlEntryError = class extends VantrisError {
32
- name = "HtmlEntryError";
33
- };
34
- var NotImplementedError = class extends VantrisError {
35
- name = "NotImplementedError";
36
- };
37
- function isVantrisError(error) {
38
- return error instanceof VantrisError;
39
- }
40
- async function isFile(path) {
41
- try {
42
- return (await stat(path)).isFile();
43
- } catch {
44
- return false;
45
- }
46
- }
47
- function readTextFile(path) {
48
- return readFile(path, "utf8");
49
- }
50
- async function ensureDir(path) {
51
- await mkdir(path, { recursive: true });
52
- }
53
- function resolveFrom(base, target) {
54
- return isAbsolute(target) ? target : resolve(base, target);
55
- }
56
-
57
- // src/config/load.ts
58
- async function loadConfig(options) {
59
- const { cwd, logger } = options;
60
- const file = options.configFile ? resolveFrom(cwd, options.configFile) : await findConfigFile(cwd);
61
- if (!file) {
62
- logger.debug("No config file found; using defaults.");
63
- return { config: {}, file: null };
64
- }
65
- logger.debug(`Loading config from ${file}`);
66
- const config = await importConfig(file);
67
- return { config, file };
68
- }
69
- async function findConfigFile(cwd) {
70
- for (const name of CONFIG_FILENAMES) {
71
- const candidate = resolveFrom(cwd, name);
72
- if (await isFile(candidate)) return candidate;
73
- }
74
- return null;
75
- }
76
- async function importConfig(file) {
77
- let mod;
78
- try {
79
- const url = `${pathToFileURL(file).href}?t=${Date.now()}`;
80
- mod = await import(url);
81
- } catch (cause) {
82
- throw new ConfigError(`Failed to load config file: ${file}`, { cause });
83
- }
84
- const exported = mod.default;
85
- if (exported === void 0) {
86
- throw new ConfigError(
87
- `Config file "${file}" has no default export. Export your config with \`export default defineConfig({ ... })\`.`
88
- );
89
- }
90
- return normalise(exported, file);
91
- }
92
- async function normalise(input, file) {
93
- const value = typeof input === "function" ? await input() : input;
94
- if (value === null || typeof value !== "object") {
95
- throw new ConfigError(
96
- `Config file "${file}" must export an object (or a function returning one).`
97
- );
98
- }
99
- return value;
100
- }
101
-
102
- // src/config/resolve.ts
103
- function resolveConfig(raw, cwd, configFile = null) {
104
- const root = resolveFrom(cwd, raw.root ?? DEFAULTS.root);
105
- const paths = {
106
- root,
107
- rootDir: resolveFrom(root, raw.rootDir ?? DEFAULTS.rootDir),
108
- publicDir: resolveFrom(root, raw.publicDir ?? DEFAULTS.publicDir),
109
- outDir: resolveFrom(root, raw.outDir ?? DEFAULTS.outDir)
110
- };
111
- return { raw, paths, configFile };
112
- }
113
-
114
- // src/html/parse.ts
115
- function parseHtml(file, html) {
116
- return { file, html };
117
- }
118
-
119
- // src/html/detect.ts
120
- async function detectHtmlEntry(root) {
121
- const file = resolveFrom(root, HTML_ENTRY_FILENAME);
122
- if (!await isFile(file)) return null;
123
- const html = await readTextFile(file);
124
- return parseHtml(file, html);
125
- }
126
-
127
- // src/shared/logger.ts
128
- var prefix = `[${APP_NAME}]`;
129
- function createLogger(options = {}) {
130
- const { verbose = false, sink = console } = options;
131
- return {
132
- info(message) {
133
- sink.log(`${prefix} ${message}`);
134
- },
135
- warn(message) {
136
- sink.warn(`${prefix} ${message}`);
137
- },
138
- error(message) {
139
- sink.error(`${prefix} ${message}`);
140
- },
141
- debug(message) {
142
- if (verbose) sink.log(`${prefix} ${message}`);
143
- }
144
- };
145
- }
146
-
147
- // src/shared/context.ts
148
- async function createContext(options) {
149
- const { cwd, logger } = options;
150
- const loaded = await loadConfig({
151
- cwd,
152
- logger,
153
- ...options.configFile !== void 0 ? { configFile: options.configFile } : {}
154
- });
155
- const config = resolveConfig(loaded.config, cwd, loaded.file);
156
- return { cwd, config, logger };
157
- }
158
-
159
- // src/server/index.ts
160
- async function startDevServer(options) {
161
- const { ctx } = options;
162
- ctx.logger.info(
163
- "dev server is not implemented in v0.1.0 (planned: H3 + HMR)."
164
- );
165
- }
166
- function rel(ctx, target) {
167
- return relative(ctx.config.paths.root, target) || ".";
168
- }
169
- async function inspectProject(ctx) {
170
- const { paths, configFile } = ctx.config;
171
- ctx.logger.info(
172
- `config: ${configFile ? rel(ctx, configFile) : "defaults (no config file)"}`
173
- );
174
- ctx.logger.info(`rootDir: ${rel(ctx, paths.rootDir)}`);
175
- ctx.logger.info(`publicDir: ${rel(ctx, paths.publicDir)}`);
176
- ctx.logger.info(`outDir: ${rel(ctx, paths.outDir)}`);
177
- const entry = await detectHtmlEntry(paths.root);
178
- if (entry) {
179
- ctx.logger.info(`html entry: ${rel(ctx, entry.file)}`);
180
- } else {
181
- ctx.logger.warn(
182
- "no index.html found at the project root; nothing to serve yet."
183
- );
184
- }
185
- return entry;
186
- }
187
- async function prepareDirectories(ctx, dirs) {
188
- for (const dir of dirs) {
189
- await ensureDir(dir);
190
- ctx.logger.debug(`prepared directory: ${dir}`);
191
- }
192
- }
193
-
194
- // src/commands/dev.ts
195
- var dev = {
196
- name: "dev",
197
- description: "Start the development server",
198
- async run(ctx) {
199
- const { rootDir, publicDir } = ctx.config.paths;
200
- await prepareDirectories(ctx, [rootDir, publicDir]);
201
- await inspectProject(ctx);
202
- await startDevServer({ ctx});
203
- }
204
- };
205
-
206
- // src/build/index.ts
207
- async function runBuild(options) {
208
- const { ctx } = options;
209
- ctx.logger.info(
210
- "build is not implemented in v0.1.0 (planned: Rolldown + esbuild)."
211
- );
212
- }
213
-
214
- // src/commands/build.ts
215
- var build = {
216
- name: "build",
217
- description: "Build the project for production",
218
- async run(ctx) {
219
- const { rootDir, publicDir, outDir } = ctx.config.paths;
220
- await prepareDirectories(ctx, [rootDir, publicDir, outDir]);
221
- await inspectProject(ctx);
222
- await runBuild({ ctx});
223
- }
224
- };
225
-
226
- // src/preview/index.ts
227
- async function runPreview(options) {
228
- const { ctx } = options;
229
- ctx.logger.info(
230
- "preview is not implemented in v0.1.0 (planned: static server over outDir)."
231
- );
232
- }
233
-
234
- // src/commands/preview.ts
235
- var preview = {
236
- name: "preview",
237
- description: "Locally preview a production build",
238
- async run(ctx) {
239
- await prepareDirectories(ctx, [ctx.config.paths.outDir]);
240
- await inspectProject(ctx);
241
- await runPreview({ ctx });
242
- }
243
- };
244
-
245
- // src/commands/index.ts
246
- var commands = {
247
- [dev.name]: dev,
248
- [build.name]: build,
249
- [preview.name]: preview
250
- };
251
-
252
- // src/cli/help.ts
253
- function helpText() {
254
- const lines = [
255
- `${APP_NAME} v${VERSION} \u2014 a modern bundler for JavaScript/TypeScript`,
256
- "",
257
- "Usage:",
258
- ` ${APP_NAME} <command> [options]`,
259
- "",
260
- "Commands:"
261
- ];
262
- const width = Math.max(...Object.keys(commands).map((n) => n.length));
263
- for (const command of Object.values(commands)) {
264
- lines.push(` ${command.name.padEnd(width)} ${command.description}`);
265
- }
266
- lines.push(
267
- "",
268
- "Options:",
269
- " -h, --help Show this help",
270
- " -v, --version Show the version number"
271
- );
272
- return lines.join("\n");
273
- }
274
- function versionText() {
275
- return `${APP_NAME} v${VERSION}`;
276
- }
277
-
278
- // src/cli/run.ts
279
- async function run(argv, options = {}) {
280
- const verbose = argv.includes("--verbose") || argv.includes("--debug");
281
- const logger = options.logger ?? createLogger({ verbose });
282
- const cwd = options.cwd ?? process.cwd();
283
- const [first, ...rest] = argv.filter((arg) => !isGlobalFlag(arg));
284
- if (!first || first === "--help" || first === "-h" || first === "help") {
285
- logger.info(helpText());
286
- return 0 /* Ok */;
287
- }
288
- if (first === "--version" || first === "-v") {
289
- logger.info(versionText());
290
- return 0 /* Ok */;
291
- }
292
- const command = commands[first];
293
- if (!command) {
294
- logger.error(`Unknown command: "${first}"`);
295
- logger.info(helpText());
296
- return 1 /* Error */;
297
- }
298
- const ctx = await createContext({ cwd, logger });
299
- await command.run(ctx, rest);
300
- return 0 /* Ok */;
301
- }
302
- function isGlobalFlag(arg) {
303
- return arg === "--verbose" || arg === "--debug";
304
- }
305
-
306
- export { ConfigError, HtmlEntryError, NotImplementedError, VERSION, VantrisError, commands, createContext, createLogger, detectHtmlEntry, isVantrisError, loadConfig, parseHtml, resolveConfig, run };
307
- //# sourceMappingURL=chunk-PSXNP5S5.js.map
308
- //# sourceMappingURL=chunk-PSXNP5S5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/shared/constants.ts","../src/shared/errors.ts","../src/utils/fs.ts","../src/utils/paths.ts","../src/config/load.ts","../src/config/resolve.ts","../src/html/parse.ts","../src/html/detect.ts","../src/shared/logger.ts","../src/shared/context.ts","../src/server/index.ts","../src/commands/support.ts","../src/commands/dev.ts","../src/build/index.ts","../src/commands/build.ts","../src/preview/index.ts","../src/commands/preview.ts","../src/commands/index.ts","../src/cli/help.ts","../src/cli/run.ts"],"names":[],"mappings":"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","file":"chunk-PSXNP5S5.js","sourcesContent":["/** Product name, used in CLI banners and logs. */\nexport const APP_NAME = \"vantris\";\n\n/** Current Vantris version. Kept in sync with package.json at release time. */\nexport const VERSION = \"0.1.0\";\n\n/** The HTML entry filename Vantris looks for at the project root. */\nexport const HTML_ENTRY_FILENAME = \"index.html\";\n\n/** Default directory values, relative to the project root. */\nexport const DEFAULTS = {\n root: \".\",\n rootDir: \"./src\",\n publicDir: \"./public\",\n outDir: \"./dist\",\n} as const;\n\n/** Config filenames Vantris will look for, in priority order. */\nexport const CONFIG_FILENAMES = [\n \"vantris.config.ts\",\n \"vantris.config.js\",\n \"vantris.config.mjs\",\n] as const;\n","/**\n * Base class for all errors thrown intentionally by Vantris. The CLI layer can\n * recognise these and render them cleanly (without a stack trace), while\n * unexpected errors keep their full trace.\n */\nexport class VantrisError extends Error {\n override readonly name: string = \"VantrisError\";\n\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n }\n}\n\n/** Thrown when configuration is missing, malformed, or fails to load. */\nexport class ConfigError extends VantrisError {\n override readonly name = \"ConfigError\";\n}\n\n/** Thrown when the project's HTML entry cannot be found. */\nexport class HtmlEntryError extends VantrisError {\n override readonly name = \"HtmlEntryError\";\n}\n\n/** Thrown for functionality declared but not yet implemented in this version. */\nexport class NotImplementedError extends VantrisError {\n override readonly name = \"NotImplementedError\";\n}\n\n/** Narrowing helper. */\nexport function isVantrisError(error: unknown): error is VantrisError {\n return error instanceof VantrisError;\n}\n","import { mkdir, readFile, stat } from \"node:fs/promises\";\n\n/** Returns `true` if `path` exists and is a regular file. */\nexport async function isFile(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isFile();\n } catch {\n return false;\n }\n}\n\n/** Reads a UTF-8 text file. */\nexport function readTextFile(path: string): Promise<string> {\n return readFile(path, \"utf8\");\n}\n\n/** Creates a directory (and any missing parents) if it does not exist. */\nexport async function ensureDir(path: string): Promise<void> {\n await mkdir(path, { recursive: true });\n}\n","import { isAbsolute, resolve } from \"node:path\";\n\n/**\n * Resolves `target` against `base`, returning an absolute path.\n * Absolute targets are returned untouched.\n */\nexport function resolveFrom(base: string, target: string): string {\n return isAbsolute(target) ? target : resolve(base, target);\n}\n","import { pathToFileURL } from \"node:url\";\nimport type { Config, ConfigInput } from \"../types/config.js\";\nimport type { Logger } from \"../types/logger.js\";\nimport { CONFIG_FILENAMES } from \"../shared/constants.js\";\nimport { ConfigError } from \"../shared/errors.js\";\nimport { isFile } from \"../utils/fs.js\";\nimport { resolveFrom } from \"../utils/paths.js\";\n\nexport interface LoadConfigOptions {\n /** Directory to search for a config file. */\n cwd: string;\n /** Logger for diagnostics. */\n logger: Logger;\n /** Explicit config file path; bypasses filename discovery. */\n configFile?: string;\n}\n\nexport interface LoadedConfig {\n /** Parsed configuration. Empty object when no config file is present. */\n config: Config;\n /** Absolute path of the file that was loaded, or `null` when none. */\n file: string | null;\n}\n\n/**\n * Locates and loads a `vantris.config.*` file from `cwd`.\n *\n * The actual module evaluation is isolated here so the loading strategy\n * (native TS stripping today; a bundled loader such as esbuild/jiti later)\n * can change without affecting any caller. A missing config is not an error —\n * defaults take over downstream.\n */\nexport async function loadConfig(\n options: LoadConfigOptions,\n): Promise<LoadedConfig> {\n const { cwd, logger } = options;\n\n const file = options.configFile\n ? resolveFrom(cwd, options.configFile)\n : await findConfigFile(cwd);\n\n if (!file) {\n logger.debug(\"No config file found; using defaults.\");\n return { config: {}, file: null };\n }\n\n logger.debug(`Loading config from ${file}`);\n const config = await importConfig(file);\n return { config, file };\n}\n\n/** Returns the first existing config file in {@link CONFIG_FILENAMES}. */\nasync function findConfigFile(cwd: string): Promise<string | null> {\n for (const name of CONFIG_FILENAMES) {\n const candidate = resolveFrom(cwd, name);\n if (await isFile(candidate)) return candidate;\n }\n return null;\n}\n\n/**\n * Imports a config module and normalises its export to a {@link Config}.\n * Relies on the Node runtime's native TypeScript support for `.ts` files.\n */\nasync function importConfig(file: string): Promise<Config> {\n let mod: { default?: unknown };\n try {\n // Cache-bust so repeated loads (e.g. a future config-watch mode) re-read.\n const url = `${pathToFileURL(file).href}?t=${Date.now()}`;\n mod = (await import(url)) as { default?: unknown };\n } catch (cause) {\n throw new ConfigError(`Failed to load config file: ${file}`, { cause });\n }\n\n const exported = mod.default;\n if (exported === undefined) {\n throw new ConfigError(\n `Config file \"${file}\" has no default export. ` +\n `Export your config with \\`export default defineConfig({ ... })\\`.`,\n );\n }\n\n return normalise(exported as ConfigInput, file);\n}\n\n/** Resolves a {@link ConfigInput} (object or factory) into a {@link Config}. */\nasync function normalise(\n input: ConfigInput,\n file: string,\n): Promise<Config> {\n const value = typeof input === \"function\" ? await input() : input;\n\n if (value === null || typeof value !== \"object\") {\n throw new ConfigError(\n `Config file \"${file}\" must export an object (or a function returning one).`,\n );\n }\n\n return value;\n}\n","import type { Config } from \"../types/config.js\";\nimport type { ResolvedConfig } from \"../types/config-resolved.js\";\nimport type { ResolvedPaths } from \"../types/paths.js\";\nimport { DEFAULTS } from \"../shared/constants.js\";\nimport { resolveFrom } from \"../utils/paths.js\";\n\n/**\n * Applies defaults to a raw {@link Config} and resolves every directory to an\n * absolute path. This is the single source of truth for default values and\n * path resolution — no other module should re-derive these.\n *\n * @param raw User configuration (after loading; may be empty).\n * @param cwd Working directory the invocation started from.\n * @param configFile Absolute path of the loaded config file, or `null`.\n */\nexport function resolveConfig(\n raw: Config,\n cwd: string,\n configFile: string | null = null,\n): ResolvedConfig {\n const root = resolveFrom(cwd, raw.root ?? DEFAULTS.root);\n\n const paths: ResolvedPaths = {\n root,\n rootDir: resolveFrom(root, raw.rootDir ?? DEFAULTS.rootDir),\n publicDir: resolveFrom(root, raw.publicDir ?? DEFAULTS.publicDir),\n outDir: resolveFrom(root, raw.outDir ?? DEFAULTS.outDir),\n };\n\n return { raw, paths, configFile };\n}\n","import type { HtmlEntry } from \"../types/html.js\";\n\n/**\n * Parses raw HTML into an {@link HtmlEntry}.\n *\n * v0.1.0 keeps this intentionally minimal: it only wraps the file path and\n * contents. The function exists as the single, isolated home for HTML\n * analysis so later versions can grow it (extracting `<script type=\"module\">`\n * entries, asset references, injection points for HMR) without scattering\n * parsing logic across the codebase.\n */\nexport function parseHtml(file: string, html: string): HtmlEntry {\n return { file, html };\n}\n","import type { HtmlEntry } from \"../types/html.js\";\nimport { HTML_ENTRY_FILENAME } from \"../shared/constants.js\";\nimport { isFile, readTextFile } from \"../utils/fs.js\";\nimport { resolveFrom } from \"../utils/paths.js\";\nimport { parseHtml } from \"./parse.js\";\n\n/**\n * Locates the project's `index.html` entry at `root`.\n *\n * @returns The parsed {@link HtmlEntry}, or `null` if no entry exists.\n */\nexport async function detectHtmlEntry(root: string): Promise<HtmlEntry | null> {\n const file = resolveFrom(root, HTML_ENTRY_FILENAME);\n if (!(await isFile(file))) return null;\n\n const html = await readTextFile(file);\n return parseHtml(file, html);\n}\n","import type { Logger } from \"../types/logger.js\";\nimport { APP_NAME } from \"./constants.js\";\n\nexport interface LoggerOptions {\n /** When `false`, `debug` calls are dropped. @default false */\n verbose?: boolean;\n /** Sink for output. Defaults to the console; overridable for tests. */\n sink?: Pick<Console, \"log\" | \"warn\" | \"error\">;\n}\n\nconst prefix = `[${APP_NAME}]`;\n\n/**\n * Creates the default console-backed {@link Logger}.\n *\n * The sink is injectable so tests can capture output, and verbosity is a\n * construction-time concern rather than a global flag.\n */\nexport function createLogger(options: LoggerOptions = {}): Logger {\n const { verbose = false, sink = console } = options;\n\n return {\n info(message) {\n sink.log(`${prefix} ${message}`);\n },\n warn(message) {\n sink.warn(`${prefix} ${message}`);\n },\n error(message) {\n sink.error(`${prefix} ${message}`);\n },\n debug(message) {\n if (verbose) sink.log(`${prefix} ${message}`);\n },\n };\n}\n","import type { Context } from \"../types/context.js\";\nimport type { Logger } from \"../types/logger.js\";\nimport { loadConfig } from \"../config/load.js\";\nimport { resolveConfig } from \"../config/resolve.js\";\n\nexport interface CreateContextOptions {\n /** Working directory the command was invoked from. */\n cwd: string;\n /** Injected logger. */\n logger: Logger;\n /** Optional explicit config file path. */\n configFile?: string;\n}\n\n/**\n * Builds the {@link Context} shared by every command: it loads the user config,\n * applies defaults, resolves paths, and bundles the injected services.\n *\n * Centralising construction here means commands never reach for global state —\n * everything they need arrives through the returned context.\n */\nexport async function createContext(\n options: CreateContextOptions,\n): Promise<Context> {\n const { cwd, logger } = options;\n\n const loaded = await loadConfig({\n cwd,\n logger,\n ...(options.configFile !== undefined\n ? { configFile: options.configFile }\n : {}),\n });\n\n const config = resolveConfig(loaded.config, cwd, loaded.file);\n\n return { cwd, config, logger };\n}\n","import type { Context } from \"../types/context.js\";\nimport type { HtmlEntry } from \"../types/html.js\";\n\n/**\n * Options handed to the dev server. Kept here so the contract is stable before\n * the implementation lands.\n */\nexport interface DevServerOptions {\n ctx: Context;\n /** The detected HTML entry, when present. */\n entry: HtmlEntry | null;\n}\n\n/**\n * Starts the development server.\n *\n * Reserved for a future version (planned: an H3-based server with HMR). The\n * seam exists now so `commands/dev` can already delegate here; only this file\n * changes when the engine is implemented.\n */\nexport async function startDevServer(options: DevServerOptions): Promise<void> {\n const { ctx } = options;\n ctx.logger.info(\n \"dev server is not implemented in v0.1.0 (planned: H3 + HMR).\",\n );\n}\n","import { relative } from \"node:path\";\nimport type { Context } from \"../types/context.js\";\nimport type { HtmlEntry } from \"../types/html.js\";\nimport { detectHtmlEntry } from \"../html/index.js\";\nimport { ensureDir } from \"../utils/fs.js\";\n\n/** Renders a path relative to the project root for tidy log output. */\nfunction rel(ctx: Context, target: string): string {\n return relative(ctx.config.paths.root, target) || \".\";\n}\n\n/**\n * Logs the resolved configuration and detects the HTML entry — the shared\n * preamble every command runs before delegating to its (future) engine.\n *\n * @returns The detected HTML entry, or `null` when none exists.\n */\nexport async function inspectProject(ctx: Context): Promise<HtmlEntry | null> {\n const { paths, configFile } = ctx.config;\n\n ctx.logger.info(\n `config: ${configFile ? rel(ctx, configFile) : \"defaults (no config file)\"}`,\n );\n ctx.logger.info(`rootDir: ${rel(ctx, paths.rootDir)}`);\n ctx.logger.info(`publicDir: ${rel(ctx, paths.publicDir)}`);\n ctx.logger.info(`outDir: ${rel(ctx, paths.outDir)}`);\n\n const entry = await detectHtmlEntry(paths.root);\n if (entry) {\n ctx.logger.info(`html entry: ${rel(ctx, entry.file)}`);\n } else {\n ctx.logger.warn(\n \"no index.html found at the project root; nothing to serve yet.\",\n );\n }\n\n return entry;\n}\n\n/** Ensures the given directories exist, creating them as needed. */\nexport async function prepareDirectories(\n ctx: Context,\n dirs: readonly string[],\n): Promise<void> {\n for (const dir of dirs) {\n await ensureDir(dir);\n ctx.logger.debug(`prepared directory: ${dir}`);\n }\n}\n","import type { Command } from \"../types/command.js\";\nimport { startDevServer } from \"../server/index.js\";\nimport { inspectProject, prepareDirectories } from \"./support.js\";\n\n/** `vantris dev` — start the development server. */\nexport const dev: Command = {\n name: \"dev\",\n description: \"Start the development server\",\n async run(ctx) {\n const { rootDir, publicDir } = ctx.config.paths;\n await prepareDirectories(ctx, [rootDir, publicDir]);\n\n const entry = await inspectProject(ctx);\n await startDevServer({ ctx, entry });\n },\n};\n","import type { Context } from \"../types/context.js\";\nimport type { HtmlEntry } from \"../types/html.js\";\n\n/** Options handed to the build pipeline. */\nexport interface BuildOptions {\n ctx: Context;\n /** The detected HTML entry, when present. */\n entry: HtmlEntry | null;\n}\n\n/**\n * Produces a production build into `outDir`.\n *\n * Reserved for a future version (planned: Rolldown for bundling, esbuild for\n * transforms). The seam exists now so `commands/build` can already delegate\n * here; only this file changes when the engine is implemented.\n */\nexport async function runBuild(options: BuildOptions): Promise<void> {\n const { ctx } = options;\n ctx.logger.info(\n \"build is not implemented in v0.1.0 (planned: Rolldown + esbuild).\",\n );\n}\n","import type { Command } from \"../types/command.js\";\nimport { runBuild } from \"../build/index.js\";\nimport { inspectProject, prepareDirectories } from \"./support.js\";\n\n/** `vantris build` — produce a production build. */\nexport const build: Command = {\n name: \"build\",\n description: \"Build the project for production\",\n async run(ctx) {\n const { rootDir, publicDir, outDir } = ctx.config.paths;\n await prepareDirectories(ctx, [rootDir, publicDir, outDir]);\n\n const entry = await inspectProject(ctx);\n await runBuild({ ctx, entry });\n },\n};\n","import type { Context } from \"../types/context.js\";\n\n/** Options handed to the preview server. */\nexport interface PreviewOptions {\n ctx: Context;\n}\n\n/**\n * Serves a previously produced production build from `outDir`.\n *\n * Reserved for a future version (planned: a static server over the build\n * output). The seam exists now so `commands/preview` can already delegate\n * here; only this file changes when the engine is implemented.\n */\nexport async function runPreview(options: PreviewOptions): Promise<void> {\n const { ctx } = options;\n ctx.logger.info(\n \"preview is not implemented in v0.1.0 (planned: static server over outDir).\",\n );\n}\n","import type { Command } from \"../types/command.js\";\nimport { runPreview } from \"../preview/index.js\";\nimport { inspectProject, prepareDirectories } from \"./support.js\";\n\n/** `vantris preview` — serve a production build locally. */\nexport const preview: Command = {\n name: \"preview\",\n description: \"Locally preview a production build\",\n async run(ctx) {\n await prepareDirectories(ctx, [ctx.config.paths.outDir]);\n\n await inspectProject(ctx);\n await runPreview({ ctx });\n },\n};\n","import type { Command } from \"../types/command.js\";\nimport { dev } from \"./dev.js\";\nimport { build } from \"./build.js\";\nimport { preview } from \"./preview.js\";\n\n/**\n * The command registry. The CLI routes purely against this map, so adding a\n * command is a one-line registration here plus its module — no CLI changes.\n */\nexport const commands: Readonly<Record<string, Command>> = {\n [dev.name]: dev,\n [build.name]: build,\n [preview.name]: preview,\n};\n\nexport type CommandName = keyof typeof commands;\n\nexport { dev, build, preview };\n","import { commands } from \"../commands/index.js\";\nimport { APP_NAME, VERSION } from \"../shared/constants.js\";\n\n/** Builds the help text listing every registered command. */\nexport function helpText(): string {\n const lines = [\n `${APP_NAME} v${VERSION} — a modern bundler for JavaScript/TypeScript`,\n \"\",\n \"Usage:\",\n ` ${APP_NAME} <command> [options]`,\n \"\",\n \"Commands:\",\n ];\n\n const width = Math.max(...Object.keys(commands).map((n) => n.length));\n for (const command of Object.values(commands)) {\n lines.push(` ${command.name.padEnd(width)} ${command.description}`);\n }\n\n lines.push(\n \"\",\n \"Options:\",\n \" -h, --help Show this help\",\n \" -v, --version Show the version number\",\n );\n\n return lines.join(\"\\n\");\n}\n\n/** Builds the version line. */\nexport function versionText(): string {\n return `${APP_NAME} v${VERSION}`;\n}\n","import type { Logger } from \"../types/logger.js\";\nimport { commands } from \"../commands/index.js\";\nimport { createContext } from \"../shared/context.js\";\nimport { createLogger } from \"../shared/logger.js\";\nimport { helpText, versionText } from \"./help.js\";\n\nexport interface RunOptions {\n /** Working directory; defaults to `process.cwd()`. */\n cwd?: string;\n /** Logger override, primarily for tests. */\n logger?: Logger;\n}\n\n/** Process exit codes used by the CLI. */\nexport const enum ExitCode {\n Ok = 0,\n Error = 1,\n}\n\n/**\n * Parses arguments and routes to a command. This is the entire CLI surface:\n * it owns argument handling and dispatch only — never command behaviour, which\n * lives behind the {@link commands} registry.\n *\n * @returns A process exit code.\n */\nexport async function run(\n argv: readonly string[],\n options: RunOptions = {},\n): Promise<ExitCode> {\n const verbose = argv.includes(\"--verbose\") || argv.includes(\"--debug\");\n const logger = options.logger ?? createLogger({ verbose });\n const cwd = options.cwd ?? process.cwd();\n\n const [first, ...rest] = argv.filter((arg) => !isGlobalFlag(arg));\n\n if (!first || first === \"--help\" || first === \"-h\" || first === \"help\") {\n logger.info(helpText());\n return ExitCode.Ok;\n }\n\n if (first === \"--version\" || first === \"-v\") {\n logger.info(versionText());\n return ExitCode.Ok;\n }\n\n const command = commands[first];\n if (!command) {\n logger.error(`Unknown command: \"${first}\"`);\n logger.info(helpText());\n return ExitCode.Error;\n }\n\n const ctx = await createContext({ cwd, logger });\n await command.run(ctx, rest);\n return ExitCode.Ok;\n}\n\n/** Flags handled by the runner itself rather than passed to commands. */\nfunction isGlobalFlag(arg: string): boolean {\n return arg === \"--verbose\" || arg === \"--debug\";\n}\n"]}