webanvil 0.0.10 → 0.0.12

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
@@ -56,17 +56,17 @@ wa typecheck # type-check the project
56
56
  What it includes
57
57
  ----------------
58
58
 
59
- | Project job | WebAnvil command | Tool |
60
- | -------------------------- | ------------------------------------------------------ | -------------------------------- |
61
- | Web builds and development | `wa build`, `wa dev`, `wa preview` | Vite |
62
- | Node builds and watch mode | `wa build`, `wa dev` | Rolldown |
63
- | Storybook development | `wa build --mode storybook`, `wa dev --mode storybook` | Storybook |
64
- | Tracked output cleanup | `wa clean` | WebAnvil |
65
- | Static checks | `wa check` | Oxfmt, Oxlint, TypeScript Native |
66
- | Tests | `wa test` | Vitest |
67
- | Linting | `wa lint` | Oxlint |
68
- | Formatting | `wa format` | Oxfmt |
69
- | Type checking | `wa typecheck` | TypeScript Native |
59
+ | Project job | WebAnvil command | Tool |
60
+ | -------------------------- | ---------------------------------- | ------------------------------------------------- |
61
+ | Web builds and development | `wa build`, `wa dev`, `wa preview` | Vite |
62
+ | Node builds and watch mode | `wa build`, `wa dev` | Rolldown |
63
+ | Design-system Storybook | `wa build`, `wa dev`, `wa preview` | Storybook |
64
+ | Tracked output cleanup | `wa clean` | WebAnvil |
65
+ | Static checks | `wa check` | Oxfmt, Oxlint, TypeScript Native, or svelte-check |
66
+ | Tests | `wa test` | Vitest |
67
+ | Linting | `wa lint` | Oxlint |
68
+ | Formatting | `wa format` | Oxfmt |
69
+ | Type checking | `wa typecheck` | TypeScript Native or svelte-check |
70
70
 
71
71
  Getting started
72
72
  ---------------
@@ -124,6 +124,12 @@ first failure. It is read-only by default. Use `wa check --fix` to format files
124
124
  and apply safe lint fixes before type checking. Tests stay separate under
125
125
  `wa test`.
126
126
 
127
+ For a Svelte project, add `svelte-check` to the package's `devDependencies`.
128
+ Then `wa typecheck` and `wa check` use it for project-wide diagnostics through
129
+ the package's `tsconfig.json`. WebAnvil otherwise uses TypeScript Native.
130
+ Explicit file paths such as `wa typecheck src/file.ts` always use TypeScript
131
+ Native because `svelte-check` checks a project rather than individual files.
132
+
127
133
  ### A web app
128
134
 
129
135
  Set the build mode to `"web"` and point it at an HTML entry point. `wa dev` starts Vite's development server, `wa build` produces a production bundle, and `wa preview` serves that bundle locally.
@@ -159,46 +165,41 @@ WebAnvil includes Storybook, the supported Vite framework adapters, Vitest's
159
165
  browser support, and Chromium. Add only a Storybook configuration and your
160
166
  project's normal framework dependencies.
161
167
 
162
- For example, a Svelte project can use this `.storybook/main.ts`:
163
-
164
- ```ts
165
- import { framework, type StorybookConfig } from "webanvil/storybook/svelte"
166
-
167
- export default {
168
- framework,
169
- stories: ["../src/**/*.stories.@(js|ts|svelte)"]
170
- } satisfies StorybookConfig
171
- ```
172
-
173
- Use `webanvil/storybook/react`, `webanvil/storybook/vue`, or
174
- `webanvil/storybook/web-components` for those frameworks. The WebAnvil wrapper
175
- uses the framework plugins already declared in `webanvil.config.*`.
176
-
177
- Set Storybook as the project's build mode when it is the primary development
178
- target:
168
+ For a Node design-system project, configure Storybook beside the package build:
179
169
 
180
170
  ```ts
171
+ import { svelte } from "@sveltejs/vite-plugin-svelte"
181
172
  import { defineConfig } from "webanvil"
182
173
 
183
174
  export default defineConfig({
184
- build: { mode: "storybook" }
175
+ build: { mode: "node", entries: { ".": "src/index.ts" }, outDir: "dist" },
176
+ storybook: { framework: "svelte", port: 6006, outDir: "storybook-static" },
177
+ vite: { plugins: [svelte()] }
185
178
  })
186
179
  ```
187
180
 
188
- Or select it for one command:
181
+ Then keep `.storybook/main.ts` focused on stories and addons:
189
182
 
190
- ```sh
191
- wa dev --mode storybook
192
- wa build --mode storybook
183
+ ```ts
184
+ import type { StorybookConfig } from "webanvil/storybook/svelte"
185
+
186
+ export default {
187
+ stories: ["../src/**/*.stories.@(js|ts|svelte)"],
188
+ addons: ["@storybook/addon-a11y"]
189
+ } satisfies StorybookConfig
193
190
  ```
194
191
 
195
- The build writes to `storybook-static` unless `storybook.outDir` in
196
- `webanvil.config.*` or `--out-dir` selects another directory. `wa test`
197
- automatically runs Storybook stories, including `play` functions, when it finds
198
- `.storybook/main.*`. Set `storybook.test: false` to exclude them. Chromium is
199
- downloaded by `@playwright/browser-chromium` when your package manager runs
200
- install scripts. If the project declares Vitest, Storybook tests require
201
- version `4.1.10` to match the bundled browser provider.
192
+ `wa dev` starts the package watcher, waits for its first successful build, then
193
+ starts Storybook. `wa build` creates the package output and static Storybook.
194
+ `wa preview` serves the static Storybook output. `wa clean` removes both sets of
195
+ tracked files. `--host` and `--port` on `wa dev` configure Storybook. The
196
+ package build still owns `--out-dir`.
197
+
198
+ Set `storybook.test: false` to exclude Storybook stories, including `play`
199
+ functions, from `wa test`. Chromium is downloaded by
200
+ `@playwright/browser-chromium` when your package manager runs install scripts.
201
+ If the project declares Vitest, Storybook tests require version `4.1.10` to
202
+ match the bundled browser provider.
202
203
 
203
204
  ### A Node project
204
205
 
@@ -534,16 +535,16 @@ That lets a project standardize on `wa` now and move settings into `webanvil.con
534
535
  Command reference
535
536
  -----------------
536
537
 
537
- | Command | Description | Options |
538
- | ------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
539
- | `wa build [entry]` | Builds with Vite in web mode, Rolldown in Node mode, or Storybook. | `--mode`, `--out-dir`, `--copy`, `--bundle`, `--no-bundle`, `--formats`, `--declaration`, `--sourcemap`, `--minify`, `--platform`, `--target` |
540
- | `wa clean` | Removes files emitted by prior WebAnvil builds. | No options |
541
- | `wa check` | Checks formatting, linting, and types, stopping on the first failure. | `--fix` |
542
- | `wa dev [entry]` | Starts Vite, a Node build watcher, or Storybook. | `--mode`, `--out-dir`, `--host`, `--port`, `--copy`, `--bundle`, `--no-bundle`, `--formats`, `--declaration`, `--sourcemap`, `--minify`, `--platform`, `--target` |
543
- | `wa preview` | Serves a Vite production build. | `--out-dir`, `--host`, `--port`, `--open` |
544
- | `wa test [filters...]` | Runs Vitest once, in watch mode, with coverage, or UI. | `--environment`, `--watch`, `--coverage`, `--ui`, `--ui-port` |
545
- | `wa lint [paths...]` | Runs Oxlint and treats warnings as failures. | `--fix` |
546
- | `wa format [paths...]` | Formats with Oxfmt. | `--check` |
547
- | `wa typecheck [paths...]` | Type-checks with TypeScript Native. | No options |
538
+ | Command | Description | Options |
539
+ | ------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
540
+ | `wa build [entry]` | Builds with Vite in web mode or Rolldown in Node mode. A configured Storybook builds with a Node project. | `--mode`, `--out-dir`, `--copy`, `--bundle`, `--no-bundle`, `--formats`, `--declaration`, `--sourcemap`, `--minify`, `--platform`, `--target` |
541
+ | `wa clean` | Removes files emitted by prior WebAnvil builds. | No options |
542
+ | `wa check` | Checks formatting, linting, and types, stopping on the first failure. | `--fix` |
543
+ | `wa dev [entry]` | Starts Vite or a Node build watcher. A configured Storybook starts with the Node watcher. | `--mode`, `--out-dir`, `--host`, `--port`, `--copy`, `--bundle`, `--no-bundle`, `--formats`, `--declaration`, `--sourcemap`, `--minify`, `--platform`, `--target` |
544
+ | `wa preview` | Serves a Vite production build or configured static Storybook output. | `--out-dir`, `--host`, `--port`, `--open` |
545
+ | `wa test [filters...]` | Runs Vitest once, in watch mode, with coverage, or UI. | `--environment`, `--watch`, `--coverage`, `--ui`, `--ui-port` |
546
+ | `wa lint [paths...]` | Runs Oxlint and treats warnings as failures. | `--fix` |
547
+ | `wa format [paths...]` | Formats with Oxfmt. | `--check` |
548
+ | `wa typecheck [paths...]` | Type-checks with TypeScript Native. | No options |
548
549
 
549
550
  Run `wa <command> --help` for the complete reference for a command.
@@ -1,9 +1,9 @@
1
- import { assertSyntaxTarget, hasOxcConfig, hasStorybookConfig, hasToolConfig, loadConfig, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, syntaxTargetSchema, withConfig } from "./config.mjs";
1
+ import { assertSyntaxTarget, effectiveUserConfigSchema, hasOxcConfig, hasStorybookConfig, hasToolConfig, loadConfig, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, syntaxTargetSchema, withConfig } from "./config.mjs";
2
2
  import { Module, createRequire, isBuiltin } from "node:module";
3
3
  import { defineArgument, defineCommand, defineOption } from "cmdore";
4
4
  import { dirname, isAbsolute, join, relative, resolve } from "pathe";
5
5
  import { glob } from "tinyglobby";
6
- import { access, copyFile, lstat, mkdir, mkdtemp, readFile, realpath, rename, rm, rmdir, writeFile } from "node:fs/promises";
6
+ import { access, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, rmdir, symlink, writeFile } from "node:fs/promises";
7
7
  import { z } from "zod";
8
8
  import { dirname as dirname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1 } from "node:path";
9
9
  import { getTsconfig, readTsconfig } from "get-tsconfig";
@@ -132,6 +132,11 @@ const supportedTools = {
132
132
  bin: "tsgo"
133
133
  }
134
134
  };
135
+ const optionalTools = { "svelte-check": {
136
+ packageName: "svelte-check",
137
+ range: ">=4 <5",
138
+ bin: "svelte-check"
139
+ } };
135
140
  const dependencyFields = [
136
141
  "dependencies",
137
142
  "devDependencies",
@@ -318,6 +323,7 @@ const loadResolvedTool = async (name, definition, anchor, source) => {
318
323
  var Toolchain = class {
319
324
  cwd;
320
325
  #tools = /* @__PURE__ */ new Map();
326
+ #optionalTools = /* @__PURE__ */ new Map();
321
327
  constructor(cwd = process.cwd()) {
322
328
  this.cwd = resolve$1(cwd);
323
329
  }
@@ -328,6 +334,13 @@ var Toolchain = class {
328
334
  this.#tools.set(name, selected);
329
335
  return selected;
330
336
  }
337
+ resolveOptional(name) {
338
+ const existing = this.#optionalTools.get(name);
339
+ if (existing !== void 0) return existing;
340
+ const selected = this.#resolveOptional(name);
341
+ this.#optionalTools.set(name, selected);
342
+ return selected;
343
+ }
331
344
  async #resolve(name) {
332
345
  const definition = supportedTools[name];
333
346
  const declaration = await findDeclaration(this.cwd, definition.packageName);
@@ -335,6 +348,12 @@ var Toolchain = class {
335
348
  const webanvilPackageRoot = dirname$1(await findContainingManifest(fileURLToPath(import.meta.url)));
336
349
  return loadResolvedTool(name, definition, webanvilPackageRoot, "webanvil");
337
350
  }
351
+ async #resolveOptional(name) {
352
+ const definition = optionalTools[name];
353
+ const declaration = await findDeclaration(this.cwd, definition.packageName);
354
+ if (declaration === void 0) return;
355
+ return loadResolvedTool(name, definition, declaration.directory, "project");
356
+ }
338
357
  };
339
358
  const formatResolvedTool = (tool) => `${tool.packageName} ${tool.version} (${tool.source})`;
340
359
  const declarationDefaults = {
@@ -1078,22 +1097,86 @@ const useTool = async (name, toolchain = new Toolchain(process.cwd())) => {
1078
1097
  }
1079
1098
  return tool;
1080
1099
  };
1100
+ const useOptionalTool = async (name, toolchain = new Toolchain(process.cwd())) => {
1101
+ const tool = await toolchain.resolveOptional(name);
1102
+ if (tool === void 0) return;
1103
+ const identity = `${tool.packageRoot}:${tool.version}`;
1104
+ if (!announced.has(identity)) {
1105
+ announced.add(identity);
1106
+ logger.info(`Using ${formatResolvedTool(tool)}`);
1107
+ }
1108
+ return tool;
1109
+ };
1081
1110
  const useToolApi = async (name, subpath, toolchain = new Toolchain(process.cwd())) => (await useTool(name, toolchain)).import(subpath);
1082
1111
  const useToolExecutable = async (name, toolchain = new Toolchain(process.cwd())) => {
1083
- const tool = await useTool(name, toolchain);
1112
+ const tool = name === "svelte-check" ? await useOptionalTool(name, toolchain) : await useTool(name, toolchain);
1113
+ if (tool === void 0) throw new Error(`${name} is not declared by the active project or workspace`);
1084
1114
  if (tool.executable === void 0) throw new Error(`${tool.packageName} does not expose an executable`);
1085
1115
  return tool.executable;
1086
1116
  };
1087
1117
  const storybookVitestVersion = "4.1.10";
1118
+ const relativeModule = (from, to) => {
1119
+ const path = relative(from, to).replaceAll("\\", "/");
1120
+ return path.startsWith(".") ? path : `./${path}`;
1121
+ };
1122
+ const storybookMain = async (configDirectory) => {
1123
+ const files = await readdir(configDirectory).catch(() => []);
1124
+ const file = [
1125
+ "js",
1126
+ "mjs",
1127
+ "cjs",
1128
+ "ts",
1129
+ "mts",
1130
+ "cts"
1131
+ ].map((extension) => `main.${extension}`).find((name) => files.includes(name));
1132
+ if (file === void 0) throw new Error(`Could not find a Storybook main configuration in ${configDirectory}`);
1133
+ return resolve(configDirectory, file);
1134
+ };
1135
+ const frameworkDirectory = (framework) => fileURLToPath(new URL(`../storybook/${framework}/`, import.meta.url));
1136
+ const prepareStorybookConfig = async (config = {}) => {
1137
+ if (config.framework === void 0) return void 0;
1138
+ const sourceDirectory = resolve(process.cwd(), config.configDir ?? ".storybook");
1139
+ const main = await storybookMain(sourceDirectory);
1140
+ const directory = await mkdtemp(resolve(dirname(sourceDirectory), ".webanvil-storybook-"));
1141
+ try {
1142
+ for (const entry of await readdir(sourceDirectory, { withFileTypes: true })) {
1143
+ if (resolve(sourceDirectory, entry.name) === main) continue;
1144
+ await symlink(resolve(sourceDirectory, entry.name), resolve(directory, entry.name), entry.isDirectory() ? "junction" : "file");
1145
+ }
1146
+ await mkdir(directory, { recursive: true });
1147
+ await writeFile(resolve(directory, "main.ts"), `import config from ${JSON.stringify(relativeModule(directory, main))}\n\nexport default { ...config, framework: ${JSON.stringify(frameworkDirectory(config.framework))} }\n`);
1148
+ return {
1149
+ config: {
1150
+ ...config,
1151
+ configDir: directory
1152
+ },
1153
+ cleanup: () => rm(directory, {
1154
+ force: true,
1155
+ recursive: true
1156
+ })
1157
+ };
1158
+ } catch (error) {
1159
+ await rm(directory, {
1160
+ force: true,
1161
+ recursive: true
1162
+ });
1163
+ throw error;
1164
+ }
1165
+ };
1088
1166
  const storybookOutputDir = (config = {}, options = {}) => options.outDir ?? config.outDir ?? "storybook-static";
1089
1167
  const exitWithError = (action, exitCode) => {
1090
1168
  throw new Error(`storybook ${action} exited with code ${exitCode ?? "unknown"}`);
1091
1169
  };
1092
- const runStorybook = async (action, config = {}, options = {}, toolchain = new Toolchain(process.cwd())) => {
1170
+ const startStorybook = async (action, config = {}, options = {}, toolchain = new Toolchain(process.cwd())) => {
1093
1171
  const executable = await useToolExecutable("storybook", toolchain);
1094
- const configDir = options.configDir ?? config.configDir;
1095
- const outDir = storybookOutputDir(config, options);
1096
- const result = await execa(executable, [
1172
+ const prepared = await prepareStorybookConfig({
1173
+ ...config,
1174
+ configDir: options.configDir ?? config.configDir
1175
+ });
1176
+ const effective = prepared?.config ?? config;
1177
+ const configDir = effective.configDir;
1178
+ const outDir = storybookOutputDir(effective, options);
1179
+ const child = execa(executable, [
1097
1180
  action,
1098
1181
  ...configDir === void 0 ? [] : ["--config-dir", configDir],
1099
1182
  ...action === "dev" && options.host !== void 0 ? ["--host", options.host] : [],
@@ -1103,8 +1186,16 @@ const runStorybook = async (action, config = {}, options = {}, toolchain = new T
1103
1186
  reject: false,
1104
1187
  stdio: "inherit"
1105
1188
  });
1106
- if (result.exitCode !== 0) exitWithError(action, result.exitCode);
1189
+ return {
1190
+ completed: child.then((result) => {
1191
+ if (result.exitCode !== 0) exitWithError(action, result.exitCode);
1192
+ }, (error) => {
1193
+ throw error;
1194
+ }).finally(() => prepared?.cleanup()),
1195
+ stop: () => child.kill("SIGTERM")
1196
+ };
1107
1197
  };
1198
+ const runStorybook = async (action, config = {}, options = {}, toolchain = new Toolchain(process.cwd())) => (await startStorybook(action, config, options, toolchain)).completed;
1108
1199
  const createStorybookTestProject = async (config = {}, vitestVersion = storybookVitestVersion) => {
1109
1200
  if (vitestVersion !== storybookVitestVersion) throw new Error(`Storybook tests require Vitest ${storybookVitestVersion}; Webanvil bundles @vitest/browser-playwright ${storybookVitestVersion}`);
1110
1201
  const previousVitest = process.env.VITEST;
@@ -1193,13 +1284,9 @@ const minify = defineOption({
1193
1284
  });
1194
1285
  const mode = defineOption({
1195
1286
  name: "mode",
1196
- description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.",
1287
+ description: "Build mode: web uses Vite and node uses Rolldown.",
1197
1288
  arity: 1,
1198
- schema: z.enum([
1199
- "web",
1200
- "node",
1201
- "storybook"
1202
- ])
1289
+ schema: z.enum(["web", "node"])
1203
1290
  });
1204
1291
  const open = defineOption({
1205
1292
  name: "open",
@@ -1263,24 +1350,6 @@ const noBundle$1 = defineOption({
1263
1350
  description: "Emit the reachable Node graph with preserveModules, overriding configuration that enables bundling.",
1264
1351
  arity: 0
1265
1352
  });
1266
- const assertStorybookArguments$1 = (explicit) => {
1267
- const option = [
1268
- "bundle",
1269
- "copy",
1270
- "declaration",
1271
- "entry",
1272
- "formats",
1273
- "minify",
1274
- "no-bundle",
1275
- "platform",
1276
- "sourcemap",
1277
- "target"
1278
- ].find((name) => {
1279
- const value = explicit[name];
1280
- return name === "bundle" || name === "no-bundle" ? value === true : value !== void 0;
1281
- });
1282
- if (option !== void 0) throw new Error(`--${option} is not available in Storybook mode`);
1283
- };
1284
1353
  const outputFiles = (result, outDir) => {
1285
1354
  if ("on" in result) throw new Error("Web builds cannot use watch mode");
1286
1355
  return (Array.isArray(result) ? result : [result]).flatMap((output) => output.output.map((file) => resolve(outDir, file.fileName)));
@@ -1356,22 +1425,27 @@ build.publicOutputFiles = async ({ outDir, publicDir }) => publicDir ? (await gl
1356
1425
  dot: true
1357
1426
  })).map((file) => resolve(outDir, file)) : [];
1358
1427
  build.web = async (web) => [...outputFiles(await web.vite.build(web.config), web.outDir), ...await build.publicOutputFiles(web)];
1428
+ const outputContains = (directory, path) => {
1429
+ const location = relative(directory, path);
1430
+ return location === "" || location !== ".." && !location.startsWith("../");
1431
+ };
1432
+ const assertSeparateStorybookOutput = (outDir, storybook) => {
1433
+ const output = resolve(process.cwd(), outDir);
1434
+ const storybookOutput = resolve(process.cwd(), storybookOutputDir(storybook));
1435
+ if (outputContains(output, storybookOutput) || outputContains(storybookOutput, output)) throw new Error("storybook.outDir must not overlap build.outDir");
1436
+ };
1437
+ const buildStorybook = async (storybook, toolchain) => {
1438
+ const outputDirectory = storybookOutputDir(storybook);
1439
+ const existing = await removeOutputsIn(outputDirectory);
1440
+ await runStorybook("build", storybook, {}, toolchain);
1441
+ const output = await glob("**/*", {
1442
+ cwd: resolve(process.cwd(), outputDirectory),
1443
+ dot: true,
1444
+ onlyFiles: true
1445
+ });
1446
+ await writeBuildInfo([...existing.output, ...output.map((file) => resolve(outputDirectory, file))]);
1447
+ };
1359
1448
  const commandRun$1 = (toolchain) => withConfig((config) => config.build, ({ copy, declaration, formats, minify, mode, entry, "out-dir": outDir, platform, sourcemap, target }, buildConfig, resolvedConfig, explicit) => {
1360
- if (mode === "storybook") {
1361
- assertStorybookArguments$1(explicit);
1362
- const storybookOptions = { outDir: explicit["out-dir"] === void 0 ? void 0 : outDir };
1363
- const outputDirectory = storybookOutputDir(resolvedConfig.storybook, storybookOptions);
1364
- return (async () => {
1365
- const existing = await removeOutputsIn(outputDirectory);
1366
- await runStorybook("build", resolvedConfig.storybook, storybookOptions, toolchain);
1367
- const output = await glob("**/*", {
1368
- cwd: resolve(process.cwd(), outputDirectory),
1369
- dot: true,
1370
- onlyFiles: true
1371
- });
1372
- await writeBuildInfo([...existing.output, ...output.map((file) => resolve(outputDirectory, file))]);
1373
- })();
1374
- }
1375
1449
  if (explicit.bundle && explicit["no-bundle"]) throw new Error("--bundle and --no-bundle cannot be used together");
1376
1450
  const effective = resolveEffectiveBuildConfig(resolvedConfig, {
1377
1451
  bundle: explicit["no-bundle"] ? false : explicit.bundle ? true : buildConfig.bundle,
@@ -1389,13 +1463,17 @@ const commandRun$1 = (toolchain) => withConfig((config) => config.build, ({ copy
1389
1463
  }, explicit.entry !== void 0);
1390
1464
  const executableMode = effective.mode;
1391
1465
  if (executableMode !== "web" && executableMode !== "node") throw new Error("Expected a web or Node build mode");
1392
- return build(executableMode, effective.entry, effective.outDir, effective, resolvedConfig.plugins ?? [], resolvedConfig.vite, resolvedConfig.rolldown, toolchain, {
1393
- ...explicit.entry === void 0 ? {} : { entry },
1394
- ...explicit["out-dir"] === void 0 ? {} : { outDir },
1395
- ...explicit.minify === void 0 ? {} : { minify },
1396
- ...explicit.sourcemap === void 0 ? {} : { sourcemap },
1397
- ...explicit.target === void 0 ? {} : { target }
1398
- });
1466
+ return (async () => {
1467
+ if (resolvedConfig.storybook !== void 0) assertSeparateStorybookOutput(effective.outDir, resolvedConfig.storybook);
1468
+ await build(executableMode, effective.entry, effective.outDir, effective, resolvedConfig.plugins ?? [], resolvedConfig.vite, resolvedConfig.rolldown, toolchain, {
1469
+ ...explicit.entry === void 0 ? {} : { entry },
1470
+ ...explicit["out-dir"] === void 0 ? {} : { outDir },
1471
+ ...explicit.minify === void 0 ? {} : { minify },
1472
+ ...explicit.sourcemap === void 0 ? {} : { sourcemap },
1473
+ ...explicit.target === void 0 ? {} : { target }
1474
+ });
1475
+ if (resolvedConfig.storybook !== void 0) await buildStorybook(resolvedConfig.storybook, toolchain);
1476
+ })();
1399
1477
  });
1400
1478
  var build_default = defineCommand({
1401
1479
  name: "build",
@@ -1414,11 +1492,11 @@ var build_default = defineCommand({
1414
1492
  target
1415
1493
  ],
1416
1494
  run: async (arguments_) => {
1417
- const config = arguments_.mode === void 0 ? (await loadConfig()).config : void 0;
1418
- const selectedMode = arguments_.mode ?? config?.build?.mode;
1495
+ const configured = arguments_.mode === void 0 ? (await loadConfig()).config : void 0;
1419
1496
  const toolchain = new Toolchain(process.cwd());
1420
- if (selectedMode === "storybook") await toolchain.resolve("storybook");
1421
- else await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1497
+ await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1498
+ const config = configured ?? (await loadConfig()).config;
1499
+ if (config.storybook !== void 0) await toolchain.resolve("storybook");
1422
1500
  return commandRun$1(toolchain)(arguments_, config);
1423
1501
  }
1424
1502
  });
@@ -1473,7 +1551,7 @@ const rebaseOxlintConfig = (config, cwd, configDirectory) => ({
1473
1551
  }) } : {}
1474
1552
  });
1475
1553
  const runTool = async (name, arguments_, config) => {
1476
- if (name !== "tsgo" && await hasOxcConfig(name)) config = void 0;
1554
+ if ((name === "oxfmt" || name === "oxlint") && await hasOxcConfig(name)) config = void 0;
1477
1555
  const executable = await useToolExecutable(name === "tsgo" ? "typescript-native" : name);
1478
1556
  const cwd = process.cwd();
1479
1557
  const configDirectory = join(cwd, ".webanvil");
@@ -1484,7 +1562,7 @@ const runTool = async (name, arguments_, config) => {
1484
1562
  const generatedConfig = configWithoutIgnores === void 0 || configPath === void 0 ? configWithoutIgnores : name === "oxfmt" ? rebaseOxfmtConfig(configWithoutIgnores, cwd, configDirectory) : rebaseOxlintConfig(configWithoutIgnores, cwd, configDirectory);
1485
1563
  const ignoreArguments = name === "oxfmt" ? ignorePatterns.map((pattern) => pattern.startsWith("!") ? pattern.slice(1) : `!${pattern}`) : ignorePatterns.flatMap((pattern) => ["--ignore-pattern", pattern]);
1486
1564
  const internalIgnoreArguments = name === "oxfmt" ? ["!**/.webanvil/**"] : ["--ignore-pattern", ".webanvil/**"];
1487
- const toolArguments = name === "tsgo" ? arguments_ : [
1565
+ const toolArguments = name === "tsgo" || name === "svelte-check" ? arguments_ : [
1488
1566
  ...configPath === void 0 ? [] : ["--config", configPath],
1489
1567
  ...ignoreArguments,
1490
1568
  ...internalIgnoreArguments,
@@ -1546,9 +1624,10 @@ const typecheckArguments = async (paths) => {
1546
1624
  ];
1547
1625
  return getTsconfig(process.cwd(), { typescriptVersion: false })?.config.references?.length ? ["-b", "--noEmit"] : ["--noEmit"];
1548
1626
  };
1549
- const typecheck = async (paths) => {
1550
- logger.start("Type checking");
1551
- await runTool("tsgo", await typecheckArguments(paths));
1627
+ const typecheck = async (paths, options) => {
1628
+ const svelteCheck = paths.length === 0 ? options === void 0 ? await useOptionalTool("svelte-check") : options.svelteCheck : void 0;
1629
+ logger.start(svelteCheck === void 0 ? "Type checking" : "Checking Svelte");
1630
+ await runTool(svelteCheck === void 0 ? "tsgo" : "svelte-check", svelteCheck === void 0 ? await typecheckArguments(paths) : []);
1552
1631
  logger.success("Type check passed");
1553
1632
  };
1554
1633
  var typecheck_default = defineCommand({
@@ -1561,23 +1640,25 @@ const fix = defineOption({
1561
1640
  description: "Format files and apply safe lint fixes.",
1562
1641
  arity: 0
1563
1642
  });
1564
- const checkProject = async (fixFiles = false, config = {}) => {
1643
+ const checkProject = async (fixFiles = false, config = {}, typecheckOptions) => {
1565
1644
  await format([], !fixFiles, config.format);
1566
1645
  await lint([], fixFiles, config.lint);
1567
- await typecheck([]);
1646
+ if (typecheckOptions === void 0) await typecheck([]);
1647
+ else await typecheck([], typecheckOptions);
1568
1648
  };
1569
1649
  var check_default = defineCommand({
1570
1650
  name: "check",
1571
1651
  description: "Check formatting, linting, and types, stopping at the first failure.",
1572
1652
  options: [fix],
1573
1653
  run: async ({ fix }) => {
1574
- await Promise.all([
1654
+ const [svelteCheck] = await Promise.all([
1655
+ useOptionalTool("svelte-check"),
1575
1656
  useTool("oxfmt"),
1576
- useTool("oxlint"),
1577
- useTool("typescript-native")
1657
+ useTool("oxlint")
1578
1658
  ]);
1659
+ if (svelteCheck === void 0) await useTool("typescript-native");
1579
1660
  const { config } = await loadConfig();
1580
- await checkProject(fix, config);
1661
+ await checkProject(fix, config, { svelteCheck });
1581
1662
  }
1582
1663
  });
1583
1664
  const clean = async () => {
@@ -1604,25 +1685,6 @@ const noBundle = defineOption({
1604
1685
  description: "Emit the reachable Node graph with preserveModules, overriding configuration that enables bundling.",
1605
1686
  arity: 0
1606
1687
  });
1607
- const assertStorybookArguments = (explicit) => {
1608
- const option = [
1609
- "bundle",
1610
- "copy",
1611
- "declaration",
1612
- "entry",
1613
- "formats",
1614
- "minify",
1615
- "no-bundle",
1616
- "out-dir",
1617
- "platform",
1618
- "sourcemap",
1619
- "target"
1620
- ].find((name) => {
1621
- const value = explicit[name];
1622
- return name === "bundle" || name === "no-bundle" ? value === true : value !== void 0;
1623
- });
1624
- if (option !== void 0) throw new Error(`--${option} is not available in Storybook mode`);
1625
- };
1626
1688
  const dev = async (mode, entry, outDir, host, port, plugins = [], options = {}, viteConfig = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd())) => {
1627
1689
  assertSyntaxTarget(options.target);
1628
1690
  if (mode === "web" && options.platform !== void 0) throw new Error("Web development does not accept platform; platform applies only to Node builds");
@@ -1654,7 +1716,7 @@ dev.web = async (host, port, plugins = [], waitForTermination = untilTerminated,
1654
1716
  await server.close();
1655
1717
  }
1656
1718
  };
1657
- dev.node = async (entry, outDir, plugins = [], waitForTermination = untilTerminated, options = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd())) => {
1719
+ dev.node = async (entry, outDir, plugins = [], waitForTermination = untilTerminated, options = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd()), onBuild) => {
1658
1720
  assertSyntaxTarget(options.target);
1659
1721
  const rolldown = await useToolApi("rolldown", void 0, toolchain);
1660
1722
  const plan = await createNodeBuildPlan(entry, outDir, options, resolveRolldownPlugins(plugins), rolldownConfig, toolchain);
@@ -1676,7 +1738,10 @@ dev.node = async (entry, outDir, plugins = [], waitForTermination = untilTermina
1676
1738
  }
1677
1739
  if (event.code === "BUNDLE_END") await event.result.close();
1678
1740
  if (event.code === "END" && !failed) try {
1679
- if (await lifecycle.complete() !== void 0) logger.success(`Built ${entry} to ${outDir}`);
1741
+ if (await lifecycle.complete() !== void 0) {
1742
+ logger.success(`Built ${entry} to ${outDir}`);
1743
+ await onBuild?.();
1744
+ }
1680
1745
  } catch (error) {
1681
1746
  logger.error(error);
1682
1747
  }
@@ -1694,14 +1759,31 @@ dev.node = async (entry, outDir, plugins = [], waitForTermination = untilTermina
1694
1759
  await watcher.close();
1695
1760
  }
1696
1761
  };
1697
- const commandRun = (toolchain) => withConfig((config) => config.build, ({ copy, declaration, formats, minify, mode, entry, "out-dir": outDir, host, platform, port, sourcemap, target }, buildConfig, resolvedConfig, explicit) => {
1698
- if (mode === "storybook") {
1699
- assertStorybookArguments(explicit);
1700
- return runStorybook("dev", resolvedConfig.storybook, {
1701
- host,
1702
- port
1762
+ const devWithStorybook = async (entry, outDir, storybook, host, port, plugins = [], options = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd()), waitForTermination = untilTerminated) => {
1763
+ let stopNodeWatcher = () => {};
1764
+ const nodeStopped = new Promise((resolve) => {
1765
+ stopNodeWatcher = resolve;
1766
+ });
1767
+ let initialBuild = () => {};
1768
+ const built = new Promise((resolve) => {
1769
+ initialBuild = resolve;
1770
+ });
1771
+ const node = dev.node(entry, outDir, plugins, () => nodeStopped, options, rolldownConfig, toolchain, initialBuild);
1772
+ let storybookProcess;
1773
+ try {
1774
+ await Promise.race([built, node]);
1775
+ storybookProcess = await startStorybook("dev", storybook, {
1776
+ host: host ?? storybook.host,
1777
+ port: port ?? storybook.port
1703
1778
  }, toolchain);
1779
+ if (await Promise.race([waitForTermination().then(() => "terminated"), storybookProcess.completed.then(() => "storybook")]) === "storybook") throw new Error("Storybook development stopped");
1780
+ } finally {
1781
+ stopNodeWatcher();
1782
+ storybookProcess?.stop();
1783
+ await Promise.allSettled([node, ...storybookProcess === void 0 ? [] : [storybookProcess.completed]]);
1704
1784
  }
1785
+ };
1786
+ const commandRun = (toolchain) => withConfig((config) => config.build, ({ copy, declaration, formats, minify, mode, entry, "out-dir": outDir, host, platform, port, sourcemap, target }, buildConfig, resolvedConfig, explicit) => {
1705
1787
  if (explicit.bundle && explicit["no-bundle"]) throw new Error("--bundle and --no-bundle cannot be used together");
1706
1788
  const effective = resolveEffectiveBuildConfig(resolvedConfig, {
1707
1789
  bundle: explicit["no-bundle"] ? false : explicit.bundle ? true : buildConfig.bundle,
@@ -1719,6 +1801,7 @@ const commandRun = (toolchain) => withConfig((config) => config.build, ({ copy,
1719
1801
  }, explicit.entry !== void 0);
1720
1802
  const executableMode = effective.mode;
1721
1803
  if (executableMode !== "web" && executableMode !== "node") throw new Error("Expected a web or Node build mode");
1804
+ if (resolvedConfig.storybook !== void 0) return devWithStorybook(effective.entry, effective.outDir, resolvedConfig.storybook, explicit.host === void 0 ? void 0 : host, explicit.port === void 0 ? void 0 : port, resolvedConfig.plugins ?? [], effective, resolvedConfig.rolldown, toolchain);
1722
1805
  return dev(executableMode, effective.entry, effective.outDir, host, port, resolvedConfig.plugins ?? [], effective, resolvedConfig.vite, resolvedConfig.rolldown, toolchain);
1723
1806
  });
1724
1807
  var dev_default = defineCommand({
@@ -1740,11 +1823,11 @@ var dev_default = defineCommand({
1740
1823
  target
1741
1824
  ],
1742
1825
  run: async (arguments_) => {
1743
- const config = arguments_.mode === void 0 ? (await loadConfig()).config : void 0;
1744
- const selectedMode = arguments_.mode ?? config?.build?.mode;
1826
+ const configured = arguments_.mode === void 0 ? (await loadConfig()).config : void 0;
1745
1827
  const toolchain = new Toolchain(process.cwd());
1746
- if (selectedMode === "storybook") await toolchain.resolve("storybook");
1747
- else await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1828
+ await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1829
+ const config = configured ?? (await loadConfig()).config;
1830
+ if (config.storybook !== void 0) await toolchain.resolve("storybook");
1748
1831
  return commandRun(toolchain)(arguments_, config);
1749
1832
  }
1750
1833
  });
@@ -1784,7 +1867,9 @@ var preview_default = defineCommand({
1784
1867
  run: async ({ "out-dir": outDir, host, port, open }) => {
1785
1868
  await useTool("vite");
1786
1869
  const { config } = await loadConfig();
1787
- return preview(outDir ?? config.build?.outDir ?? "dist", host, port, outDir !== void 0, untilTerminated, open, config.vite);
1870
+ resolveEffectiveBuildConfig(config, {}, false);
1871
+ const storybook = config.storybook;
1872
+ return preview(outDir ?? (storybook === void 0 ? config.build?.outDir ?? "dist" : storybookOutputDir(storybook)), host, port, outDir !== void 0 || storybook !== void 0, untilTerminated, open, config.vite);
1788
1873
  }
1789
1874
  });
1790
1875
  const test = async (filters, config = {}, options = {}, waitForTermination = untilTerminated, storybook = {}) => {
@@ -1797,57 +1882,65 @@ const test = async (filters, config = {}, options = {}, waitForTermination = unt
1797
1882
  const nativeConfig = hasVitestConfig ? {} : config;
1798
1883
  const nativeCoverage = typeof nativeConfig.coverage === "object" && nativeConfig.coverage !== null ? nativeConfig.coverage : {};
1799
1884
  const nativeApi = typeof nativeConfig.api === "object" && nativeConfig.api !== null ? nativeConfig.api : {};
1800
- const storybookProject = storybook.test === false || !await hasStorybookConfig(storybook.configDir) ? void 0 : await createStorybookTestProject(storybook, vitestTool.version);
1801
- const vitestOptions = {
1802
- ...nativeConfig,
1803
- passWithNoTests: true,
1804
- run: !persistent,
1805
- watch: persistent,
1806
- ...options.environment === void 0 ? {} : { environment: options.environment },
1807
- ...options.coverage ? { coverage: {
1808
- ...nativeCoverage,
1809
- enabled: true,
1810
- provider: "v8"
1811
- } } : {},
1812
- ...options.ui ? { ui: true } : {},
1813
- ...options.uiPort === void 0 ? {} : { api: {
1814
- ...nativeApi,
1815
- host: "127.0.0.1",
1816
- port: options.uiPort,
1817
- strictPort: true
1818
- } }
1819
- };
1820
- const vitests = hasVitestConfig && storybookProject !== void 0 ? [await startVitest("test", filters, vitestOptions), await startVitest("test", filters, {
1821
- ...vitestOptions,
1822
- ...options.ui ? {
1823
- api: false,
1824
- ui: false
1825
- } : {},
1826
- projects: [storybookProject]
1827
- })] : [await startVitest("test", filters, {
1828
- ...vitestOptions,
1829
- ...storybookProject === void 0 ? {} : { projects: [...nativeConfig.projects ?? [], storybookProject] }
1830
- })];
1831
- if (persistent) {
1832
- try {
1833
- await waitForTermination();
1834
- } finally {
1835
- await Promise.all(vitests.map((vitest) => vitest.close()));
1885
+ const preparedStorybook = storybook.framework === void 0 || storybook.test === false || !await hasStorybookConfig(storybook.configDir) ? void 0 : await prepareStorybookConfig(storybook);
1886
+ try {
1887
+ const storybookProject = preparedStorybook === void 0 ? void 0 : await createStorybookTestProject(preparedStorybook.config, vitestTool.version);
1888
+ const vitestOptions = {
1889
+ ...nativeConfig,
1890
+ passWithNoTests: true,
1891
+ run: !persistent,
1892
+ watch: persistent,
1893
+ ...options.environment === void 0 ? {} : { environment: options.environment },
1894
+ ...options.coverage ? { coverage: {
1895
+ ...nativeCoverage,
1896
+ enabled: true,
1897
+ provider: "v8"
1898
+ } } : {},
1899
+ ...options.ui ? { ui: true } : {},
1900
+ ...options.uiPort === void 0 ? {} : { api: {
1901
+ ...nativeApi,
1902
+ host: "127.0.0.1",
1903
+ port: options.uiPort,
1904
+ strictPort: true
1905
+ } }
1906
+ };
1907
+ const vitests = hasVitestConfig && storybookProject !== void 0 ? [await startVitest("test", filters, vitestOptions), await startVitest("test", filters, {
1908
+ ...vitestOptions,
1909
+ ...options.ui ? {
1910
+ api: false,
1911
+ ui: false
1912
+ } : {},
1913
+ projects: [storybookProject]
1914
+ })] : [await startVitest("test", filters, {
1915
+ ...vitestOptions,
1916
+ ...storybookProject === void 0 ? {} : { projects: [...nativeConfig.projects ?? [], storybookProject] }
1917
+ })];
1918
+ if (persistent) {
1919
+ try {
1920
+ await waitForTermination();
1921
+ } finally {
1922
+ await Promise.all(vitests.map((vitest) => vitest.close()));
1923
+ }
1924
+ return;
1836
1925
  }
1837
- return;
1926
+ const failed = vitests.some((vitest) => vitest.state.getFiles().some((file) => file.result?.state === "fail") || vitest.state.getUnhandledErrors().length > 0);
1927
+ await Promise.all(vitests.map((vitest) => vitest.close()));
1928
+ if (failed) throw new Error("Tests failed");
1929
+ logger.success("Tests passed");
1930
+ } finally {
1931
+ await preparedStorybook?.cleanup();
1838
1932
  }
1839
- const failed = vitests.some((vitest) => vitest.state.getFiles().some((file) => file.result?.state === "fail") || vitest.state.getUnhandledErrors().length > 0);
1840
- await Promise.all(vitests.map((vitest) => vitest.close()));
1841
- if (failed) throw new Error("Tests failed");
1842
- logger.success("Tests passed");
1843
- };
1844
- const runTest = withConfig((config) => config.test, ({ filters, environment, coverage, ui, "ui-port": uiPort, watch }, config, resolvedConfig, explicitArguments) => test(filters, config, {
1845
- coverage,
1846
- environment: explicitArguments.environment === void 0 ? void 0 : environment,
1847
- ui,
1848
- uiPort,
1849
- watch
1850
- }, untilTerminated, resolvedConfig.storybook));
1933
+ };
1934
+ const runTest = withConfig((config) => config.test, ({ filters, environment, coverage, ui, "ui-port": uiPort, watch }, config, resolvedConfig, explicitArguments) => {
1935
+ effectiveUserConfigSchema.parse(resolvedConfig);
1936
+ return test(filters, config, {
1937
+ coverage,
1938
+ environment: explicitArguments.environment === void 0 ? void 0 : environment,
1939
+ ui,
1940
+ uiPort,
1941
+ watch
1942
+ }, untilTerminated, resolvedConfig.storybook);
1943
+ });
1851
1944
  var test_default = defineCommand({
1852
1945
  name: "test",
1853
1946
  arguments: [filters],
@@ -59,11 +59,7 @@ const syntaxTargetSchema = z.union([z.string().min(1), z.array(z.string().min(1)
59
59
  const nativeConfigSchema = () => z.custom((value) => typeof value === "object" && value !== null && !Array.isArray(value), "Expected a configuration object");
60
60
  const buildConfigSchema = z.strictObject({
61
61
  bundle: z.boolean().optional(),
62
- mode: z.enum([
63
- "web",
64
- "node",
65
- "storybook"
66
- ]).optional(),
62
+ mode: z.enum(["web", "node"]).optional(),
67
63
  entry: z.string().min(1).optional(),
68
64
  entries: z.record(z.string().min(1), z.string().min(1)).optional(),
69
65
  outDir: z.string().min(1).optional(),
@@ -86,7 +82,15 @@ const testConfigSchema = nativeConfigSchema();
86
82
  const viteConfigSchema = nativeConfigSchema();
87
83
  const storybookConfigSchema = z.strictObject({
88
84
  configDir: z.string().min(1).optional(),
85
+ framework: z.enum([
86
+ "react",
87
+ "svelte",
88
+ "vue",
89
+ "web-components"
90
+ ]).optional(),
91
+ host: z.string().min(1).optional(),
89
92
  outDir: z.string().min(1).optional(),
93
+ port: z.number().int().min(1).max(65535).optional(),
90
94
  test: z.boolean().optional()
91
95
  });
92
96
  const pluginSchema = z.custom(isWebAnvilPlugin, "Expected a Vite plugin or a WebAnvil plugin created with definePlugin()");
@@ -119,6 +123,18 @@ const effectiveUserConfigSchema = userConfigSchema.superRefine((config, context)
119
123
  message: NODE_PLUGIN_ERROR
120
124
  });
121
125
  }
126
+ if (config.storybook !== void 0) {
127
+ if (build.mode !== "node") context.addIssue({
128
+ code: "custom",
129
+ path: ["storybook"],
130
+ message: "storybook is available for Node projects; set build.mode to \"node\""
131
+ });
132
+ if (config.storybook.framework === void 0) context.addIssue({
133
+ code: "custom",
134
+ path: ["storybook", "framework"],
135
+ message: "storybook.framework selects the Storybook framework adapter"
136
+ });
137
+ }
122
138
  });
123
139
  const defaultConfig = {
124
140
  build: {
package/dist/index.d.mts CHANGED
@@ -36,9 +36,11 @@ declare const isWebAnvilPlugin: (plugin: unknown) => plugin is WebAnvilPlugin;
36
36
  declare const resolveRolldownPlugins: (plugins: WebAnvilPlugin[]) => Plugin[];
37
37
  declare const resolveVitePlugins: (plugins: WebAnvilPlugin[]) => PluginOption[];
38
38
  type ToolName = "vite" | "vitest" | "rolldown" | "oxlint" | "oxfmt" | "storybook" | "typescript" | "typescript-native";
39
+ type OptionalToolName = "svelte-check";
40
+ type AnyToolName = ToolName | OptionalToolName;
39
41
  type ToolSource = "project" | "webanvil";
40
42
  type ResolvedTool = {
41
- name: ToolName;
43
+ name: AnyToolName;
42
44
  packageName: string;
43
45
  version: string;
44
46
  source: ToolSource;
@@ -51,6 +53,7 @@ declare class Toolchain {
51
53
  readonly cwd: string;
52
54
  constructor(cwd?: string);
53
55
  resolve(name: ToolName): Promise<ResolvedTool>;
56
+ resolveOptional(name: OptionalToolName): Promise<ResolvedTool | undefined>;
54
57
  }
55
58
  type DeclarationLogger = {
56
59
  info: (...arguments_: unknown[]) => void;
@@ -103,7 +106,6 @@ declare const buildConfigSchema: z.ZodObject<{
103
106
  mode: z.ZodOptional<z.ZodEnum<{
104
107
  web: "web";
105
108
  node: "node";
106
- storybook: "storybook";
107
109
  }>>;
108
110
  entry: z.ZodOptional<z.ZodString>;
109
111
  entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -140,7 +142,15 @@ declare const testConfigSchema: z.ZodCustom<TestUserConfig, TestUserConfig>;
140
142
  declare const viteConfigSchema: z.ZodCustom<UserConfig$1, UserConfig$1>;
141
143
  declare const storybookConfigSchema: z.ZodObject<{
142
144
  configDir: z.ZodOptional<z.ZodString>;
145
+ framework: z.ZodOptional<z.ZodEnum<{
146
+ react: "react";
147
+ svelte: "svelte";
148
+ vue: "vue";
149
+ "web-components": "web-components";
150
+ }>>;
151
+ host: z.ZodOptional<z.ZodString>;
143
152
  outDir: z.ZodOptional<z.ZodString>;
153
+ port: z.ZodOptional<z.ZodNumber>;
144
154
  test: z.ZodOptional<z.ZodBoolean>;
145
155
  }, z.core.$strict>;
146
156
  declare const userConfigSchema: z.ZodObject<{
@@ -149,7 +159,6 @@ declare const userConfigSchema: z.ZodObject<{
149
159
  mode: z.ZodOptional<z.ZodEnum<{
150
160
  web: "web";
151
161
  node: "node";
152
- storybook: "storybook";
153
162
  }>>;
154
163
  entry: z.ZodOptional<z.ZodString>;
155
164
  entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -177,7 +186,15 @@ declare const userConfigSchema: z.ZodObject<{
177
186
  rolldown: z.ZodOptional<z.ZodCustom<RolldownConfig, RolldownConfig>>;
178
187
  storybook: z.ZodOptional<z.ZodObject<{
179
188
  configDir: z.ZodOptional<z.ZodString>;
189
+ framework: z.ZodOptional<z.ZodEnum<{
190
+ react: "react";
191
+ svelte: "svelte";
192
+ vue: "vue";
193
+ "web-components": "web-components";
194
+ }>>;
195
+ host: z.ZodOptional<z.ZodString>;
180
196
  outDir: z.ZodOptional<z.ZodString>;
197
+ port: z.ZodOptional<z.ZodNumber>;
181
198
  test: z.ZodOptional<z.ZodBoolean>;
182
199
  }, z.core.$strict>>;
183
200
  test: z.ZodOptional<z.ZodCustom<TestUserConfig, TestUserConfig>>;
@@ -190,7 +207,6 @@ declare const effectiveUserConfigSchema: z.ZodObject<{
190
207
  mode: z.ZodOptional<z.ZodEnum<{
191
208
  web: "web";
192
209
  node: "node";
193
- storybook: "storybook";
194
210
  }>>;
195
211
  entry: z.ZodOptional<z.ZodString>;
196
212
  entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -218,7 +234,15 @@ declare const effectiveUserConfigSchema: z.ZodObject<{
218
234
  rolldown: z.ZodOptional<z.ZodCustom<RolldownConfig, RolldownConfig>>;
219
235
  storybook: z.ZodOptional<z.ZodObject<{
220
236
  configDir: z.ZodOptional<z.ZodString>;
237
+ framework: z.ZodOptional<z.ZodEnum<{
238
+ react: "react";
239
+ svelte: "svelte";
240
+ vue: "vue";
241
+ "web-components": "web-components";
242
+ }>>;
243
+ host: z.ZodOptional<z.ZodString>;
221
244
  outDir: z.ZodOptional<z.ZodString>;
245
+ port: z.ZodOptional<z.ZodNumber>;
222
246
  test: z.ZodOptional<z.ZodBoolean>;
223
247
  }, z.core.$strict>>;
224
248
  test: z.ZodOptional<z.ZodCustom<TestUserConfig, TestUserConfig>>;
@@ -276,12 +300,11 @@ declare const build: {
276
300
  };
277
301
  declare const _default: import("cmdore").Command<readonly [{
278
302
  readonly name: "mode";
279
- readonly description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.";
303
+ readonly description: "Build mode: web uses Vite and node uses Rolldown.";
280
304
  readonly arity: 1;
281
305
  readonly schema: import("zod").ZodEnum<{
282
306
  web: "web";
283
307
  node: "node";
284
- storybook: "storybook";
285
308
  }>;
286
309
  }, {
287
310
  readonly name: "out-dir";
@@ -352,8 +375,17 @@ declare const _default: import("cmdore").Command<readonly [{
352
375
  readonly name: "entry";
353
376
  readonly description: "Web entry or Node public root; unbundled Node builds emit its reachable graph with preserveModules.";
354
377
  }]>;
378
+ type TypecheckOptions = {
379
+ svelteCheck: ResolvedTool | undefined;
380
+ };
381
+ declare const typecheck: (paths: string[], options?: TypecheckOptions) => Promise<void>;
382
+ declare const _default$8: import("cmdore").Command<readonly import("cmdore").Option[], readonly [{
383
+ readonly name: "paths";
384
+ readonly description: "Files or directories to check.";
385
+ readonly variadic: true;
386
+ }]>;
355
387
  type CheckConfig = Pick<UserConfig, "format" | "lint">;
356
- declare const checkProject: (fixFiles?: boolean, config?: CheckConfig) => Promise<void>;
388
+ declare const checkProject: (fixFiles?: boolean, config?: CheckConfig, typecheckOptions?: TypecheckOptions) => Promise<void>;
357
389
  declare const _default$1: import("cmdore").Command<readonly [{
358
390
  readonly name: "fix";
359
391
  readonly description: "Format files and apply safe lint fixes.";
@@ -364,16 +396,15 @@ declare const _default$2: import("cmdore").Command<readonly import("cmdore").Opt
364
396
  declare const dev: {
365
397
  (mode: "web" | "node", entry: string, outDir: string, host?: string, port?: number, plugins?: WebAnvilPlugin[], options?: NodeBuildOptions, viteConfig?: UserConfig$1, rolldownConfig?: RolldownConfig, toolchain?: Toolchain): Promise<void>;
366
398
  web(host?: string, port?: number, plugins?: WebAnvilPlugin[], waitForTermination?: () => Promise<void>, viteConfig?: UserConfig$1, toolchain?: Toolchain): Promise<void>;
367
- node(entry: string, outDir: string, plugins?: WebAnvilPlugin[], waitForTermination?: () => Promise<void>, options?: NodeBuildOptions, rolldownConfig?: RolldownConfig, toolchain?: Toolchain): Promise<void>;
399
+ node(entry: string, outDir: string, plugins?: WebAnvilPlugin[], waitForTermination?: () => Promise<void>, options?: NodeBuildOptions, rolldownConfig?: RolldownConfig, toolchain?: Toolchain, onBuild?: () => void | Promise<void>): Promise<void>;
368
400
  };
369
401
  declare const _default$3: import("cmdore").Command<readonly [{
370
402
  readonly name: "mode";
371
- readonly description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.";
403
+ readonly description: "Build mode: web uses Vite and node uses Rolldown.";
372
404
  readonly arity: 1;
373
405
  readonly schema: import("zod").ZodEnum<{
374
406
  web: "web";
375
407
  node: "node";
376
- storybook: "storybook";
377
408
  }>;
378
409
  }, {
379
410
  readonly name: "out-dir";
@@ -525,12 +556,6 @@ declare const _default$7: import("cmdore").Command<readonly [{
525
556
  readonly description: "Test files or names to run.";
526
557
  readonly variadic: true;
527
558
  }]>;
528
- declare const typecheck: (paths: string[]) => Promise<void>;
529
- declare const _default$8: import("cmdore").Command<readonly import("cmdore").Option[], readonly [{
530
- readonly name: "paths";
531
- readonly description: "Files or directories to check.";
532
- readonly variadic: true;
533
- }]>;
534
559
  declare const bundle$1: {
535
560
  readonly name: "bundle";
536
561
  readonly description: "Bundle the Node public roots; without it, emit their reachable graph with preserveModules.";
@@ -598,12 +623,11 @@ declare const minify$1: {
598
623
  };
599
624
  declare const mode: {
600
625
  readonly name: "mode";
601
- readonly description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.";
626
+ readonly description: "Build mode: web uses Vite and node uses Rolldown.";
602
627
  readonly arity: 1;
603
628
  readonly schema: z.ZodEnum<{
604
629
  web: "web";
605
630
  node: "node";
606
- storybook: "storybook";
607
631
  }>;
608
632
  };
609
633
  declare const open: {
@@ -1,3 +1,6 @@
1
- import { StorybookConfig } from "@storybook/react-vite/node";
1
+ import { StorybookConfig as StorybookConfig$1 } from "@storybook/react-vite/node";
2
+ type StorybookConfig = Omit<StorybookConfig$1, "framework"> & {
3
+ framework?: StorybookConfig$1["framework"];
4
+ };
2
5
  declare const framework: string;
3
- export { type StorybookConfig, framework };
6
+ export { StorybookConfig, framework };
@@ -1,3 +1,6 @@
1
- import { StorybookConfig } from "@storybook/svelte-vite/node";
1
+ import { StorybookConfig as StorybookConfig$1 } from "@storybook/svelte-vite/node";
2
+ type StorybookConfig = Omit<StorybookConfig$1, "framework"> & {
3
+ framework?: StorybookConfig$1["framework"];
4
+ };
2
5
  declare const framework: string;
3
- export { type StorybookConfig, framework };
6
+ export { StorybookConfig, framework };
@@ -1,3 +1,6 @@
1
- import { StorybookConfig } from "@storybook/vue3-vite/node";
1
+ import { StorybookConfig as StorybookConfig$1 } from "@storybook/vue3-vite/node";
2
+ type StorybookConfig = Omit<StorybookConfig$1, "framework"> & {
3
+ framework?: StorybookConfig$1["framework"];
4
+ };
2
5
  declare const framework: string;
3
- export { type StorybookConfig, framework };
6
+ export { StorybookConfig, framework };
@@ -1,3 +1,6 @@
1
- import { StorybookConfig } from "@storybook/web-components-vite/node";
1
+ import { StorybookConfig as StorybookConfig$1 } from "@storybook/web-components-vite/node";
2
+ type StorybookConfig = Omit<StorybookConfig$1, "framework"> & {
3
+ framework?: StorybookConfig$1["framework"];
4
+ };
2
5
  declare const framework: string;
3
- export { type StorybookConfig, framework };
6
+ export { StorybookConfig, framework };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webanvil",
3
- "version": "0.0.10",
3
+ "version": "0.0.12",
4
4
  "description": "A unified CLI for building, testing, linting, formatting, and type-checking JavaScript and TypeScript projects.",
5
5
  "keywords": [
6
6
  "build-tool",