webanvil 0.0.7 → 0.0.9

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
@@ -23,6 +23,7 @@ Table of contents
23
23
  - [Install](#install)
24
24
  - [Everyday commands](#everyday-commands)
25
25
  - [A web app](#a-web-app)
26
+ - [Storybook](#storybook)
26
27
  - [A Node project](#a-node-project)
27
28
  - [Configuration](#configuration)
28
29
  - [Tool selection](#tool-selection)
@@ -55,16 +56,17 @@ wa typecheck # type-check the project
55
56
  What it includes
56
57
  ----------------
57
58
 
58
- | Project job | WebAnvil command | Tool |
59
- | -------------------------- | ---------------------------------- | -------------------------------- |
60
- | Web builds and development | `wa build`, `wa dev`, `wa preview` | Vite |
61
- | Node builds and watch mode | `wa build`, `wa dev` | Rolldown |
62
- | Tracked output cleanup | `wa clean` | WebAnvil |
63
- | Static checks | `wa check` | Oxfmt, Oxlint, TypeScript Native |
64
- | Tests | `wa test` | Vitest |
65
- | Linting | `wa lint` | Oxlint |
66
- | Formatting | `wa format` | Oxfmt |
67
- | 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
+ | 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 |
68
70
 
69
71
  Getting started
70
72
  ---------------
@@ -151,6 +153,53 @@ export default defineConfig({
151
153
  })
152
154
  ```
153
155
 
156
+ ### Storybook
157
+
158
+ WebAnvil includes Storybook, the supported Vite framework adapters, Vitest's
159
+ browser support, and Chromium. Add only a Storybook configuration and your
160
+ project's normal framework dependencies.
161
+
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:
179
+
180
+ ```ts
181
+ import { defineConfig } from "webanvil"
182
+
183
+ export default defineConfig({
184
+ build: { mode: "storybook" }
185
+ })
186
+ ```
187
+
188
+ Or select it for one command:
189
+
190
+ ```sh
191
+ wa dev --mode storybook
192
+ wa build --mode storybook
193
+ ```
194
+
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.
202
+
154
203
  ### A Node project
155
204
 
156
205
  Node mode is the default. Declare the package's public roots with `entry` or
@@ -268,6 +317,15 @@ export default defineConfig({
268
317
  })
269
318
  ```
270
319
 
320
+ For a new WebAnvil project, keep Oxfmt and Oxlint settings in the `format` and
321
+ `lint` blocks. `wa format`, `wa lint`, and `wa check` pass those options to the
322
+ matching Oxc tool, so you do not need to create `.oxfmtrc.json` or
323
+ `.oxlintrc.json`.
324
+
325
+ Keep a native Oxc config when you are adopting an existing project configuration
326
+ or need Oxc's native configuration lookup. A native Oxc config takes precedence
327
+ over the matching WebAnvil block.
328
+
271
329
  ### Tool selection
272
330
 
273
331
  WebAnvil selects compatible project and workspace declarations before its own
@@ -280,6 +338,7 @@ invalid package identity, or is outside the supported range fails first.
280
338
  | -------------------------- | ------------------------------------ | ----------------------- |
281
339
  | Vite | `>=8.1.5 <9` | `8.1.5` |
282
340
  | Vitest | `>=4.1.10 <5` | `4.1.10` |
341
+ | Storybook | `>=10.5.9 <11` | `10.5.9` |
283
342
  | Rolldown | `>=1.2.0 <2` | `1.2.0` |
284
343
  | Oxlint | `>=1.75.0 <2` | `1.75.0` |
285
344
  | Oxfmt | `>=0.60.0 <0.61` | `0.60.0` |
@@ -470,17 +529,17 @@ Your existing configuration stays in charge. A `vite.config.*` or
470
529
  `.oxfmtrc.json` and `.oxlintrc.json` do the same for Oxc. Explicit CLI values
471
530
  remain the final run-specific override.
472
531
 
473
- That lets a project standardize on `wa` now and move settings into `webanvil.config.ts` later, one part at a time. Start with a build entry when it makes sense, then bring over test, lint, or format settings as you touch them.
532
+ That lets a project standardize on `wa` now and move settings into `webanvil.config.ts` later, one part at a time. New WebAnvil projects should keep Oxfmt and Oxlint settings in the `format` and `lint` blocks; move existing native Oxc configuration there when it makes sense.
474
533
 
475
534
  Command reference
476
535
  -----------------
477
536
 
478
537
  | Command | Description | Options |
479
538
  | ------------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
480
- | `wa build [entry]` | Builds with Vite in web mode or Rolldown in Node mode. | `--mode`, `--out-dir`, `--copy`, `--bundle`, `--no-bundle`, `--formats`, `--declaration`, `--sourcemap`, `--minify`, `--platform`, `--target` |
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` |
481
540
  | `wa clean` | Removes files emitted by prior WebAnvil builds. | No options |
482
541
  | `wa check` | Checks formatting, linting, and types, stopping on the first failure. | `--fix` |
483
- | `wa dev [entry]` | Starts a Vite server or a full Node build watcher. | `--mode`, `--out-dir`, `--host`, `--port`, `--copy`, `--bundle`, `--no-bundle`, `--formats`, `--declaration`, `--sourcemap`, `--minify`, `--platform`, `--target` |
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` |
484
543
  | `wa preview` | Serves a Vite production build. | `--out-dir`, `--host`, `--port`, `--open` |
485
544
  | `wa test [filters...]` | Runs Vitest once, in watch mode, with coverage, or UI. | `--environment`, `--watch`, `--coverage`, `--ui`, `--ui-port` |
486
545
  | `wa lint [paths...]` | Runs Oxlint and treats warnings as failures. | `--fix` |
@@ -1,10 +1,9 @@
1
+ import { assertSyntaxTarget, loadConfig, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, syntaxTargetSchema, withConfig } from "./config.mjs";
1
2
  import { Module, createRequire, isBuiltin } from "node:module";
2
3
  import { defineArgument, defineCommand, defineOption } from "cmdore";
3
4
  import { dirname, isAbsolute, join, relative, resolve } from "pathe";
4
5
  import { glob } from "tinyglobby";
5
6
  import { access, copyFile, lstat, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, rmdir, writeFile } from "node:fs/promises";
6
- import { loadConfig } from "c12";
7
- import { defu } from "defu";
8
7
  import { z } from "zod";
9
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";
10
9
  import { getTsconfig, readTsconfig } from "get-tsconfig";
@@ -13,9 +12,11 @@ import { fileURLToPath, pathToFileURL } from "node:url";
13
12
  import { resolvePath } from "mlly";
14
13
  import { parse } from "yaml";
15
14
  import { resolvePackageJSON } from "pkg-types";
15
+ import { execa } from "execa";
16
+ import { playwright } from "@vitest/browser-playwright";
17
+ import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
16
18
  import { consola } from "consola";
17
19
  import { randomUUID } from "node:crypto";
18
- import { execa } from "execa";
19
20
  const entry = defineArgument({
20
21
  name: "entry",
21
22
  description: "Web entry or Node public root; unbundled Node builds emit its reachable graph with preserveModules."
@@ -43,131 +44,16 @@ const hasToolConfig = async (name, cwd = process.cwd()) => {
43
44
  ].some((extension) => files.includes(`${name}.config.${extension}`));
44
45
  };
45
46
  const hasOxcConfig = (name, cwd = process.cwd()) => hasFile(join(cwd, name === "oxfmt" ? ".oxfmtrc.json" : ".oxlintrc.json"));
46
- const NODE_PLUGIN_ERROR = "Node builds require plugins created with definePlugin()";
47
- const definePlugin = (plugin, options) => ({
48
- rolldown: () => plugin.rolldown(options),
49
- vite: () => plugin.vite(options)
50
- });
51
- const isUnpluginAdapter = (plugin) => typeof plugin === "object" && plugin !== null && "rolldown" in plugin && typeof plugin.rolldown === "function" && "vite" in plugin && typeof plugin.vite === "function";
52
- const isWebAnvilPlugin = (plugin) => Array.isArray(plugin) || typeof plugin === "function" || typeof plugin === "object" && plugin !== null && "name" in plugin || isUnpluginAdapter(plugin);
53
- const resolveRolldownPlugins = (plugins) => plugins.flatMap((plugin) => {
54
- if (!isUnpluginAdapter(plugin)) throw new Error(NODE_PLUGIN_ERROR);
55
- return plugin.rolldown();
56
- });
57
- const resolveVitePlugins = (plugins) => plugins.map((plugin) => isUnpluginAdapter(plugin) ? plugin.vite() : plugin);
58
- const copyMappingSchema = z.strictObject({
59
- from: z.string().min(1),
60
- to: z.string().min(1)
61
- });
62
- const legacyPlatformTarget = (target) => (typeof target === "string" ? [target] : target).find((value) => value === "browser" || value === "neutral");
63
- const assertSyntaxTarget = (target) => {
64
- if (target === void 0) return;
65
- const legacy = legacyPlatformTarget(target);
66
- if (legacy !== void 0) throw new Error(`build.target no longer selects a platform; use build.platform: "${legacy}" instead`);
67
- };
68
- const syntaxTargetSchema = z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).superRefine((target, context) => {
69
- const legacy = legacyPlatformTarget(target);
70
- if (legacy !== void 0) context.addIssue({
71
- code: "custom",
72
- message: `build.target no longer selects a platform; use build.platform: "${legacy}" instead`
73
- });
74
- });
75
- const nativeConfigSchema = () => z.custom((value) => typeof value === "object" && value !== null && !Array.isArray(value), "Expected a configuration object");
76
- const buildConfigSchema = z.strictObject({
77
- bundle: z.boolean().optional(),
78
- mode: z.enum(["web", "node"]).optional(),
79
- entry: z.string().min(1).optional(),
80
- entries: z.record(z.string().min(1), z.string().min(1)).optional(),
81
- outDir: z.string().min(1).optional(),
82
- declaration: z.union([z.boolean(), nativeConfigSchema()]).optional(),
83
- sourcemap: z.boolean().optional(),
84
- minify: z.boolean().optional(),
85
- copy: z.array(copyMappingSchema).optional(),
86
- formats: z.array(z.enum(["esm", "cjs"])).min(1).optional(),
87
- platform: z.enum([
88
- "node",
89
- "browser",
90
- "neutral"
91
- ]).optional(),
92
- target: syntaxTargetSchema.optional()
93
- });
94
- const formatConfigSchema = nativeConfigSchema();
95
- const lintConfigSchema = nativeConfigSchema();
96
- const rolldownConfigSchema = nativeConfigSchema();
97
- const testConfigSchema = nativeConfigSchema();
98
- const viteConfigSchema = nativeConfigSchema();
99
- const pluginSchema = z.custom(isWebAnvilPlugin, "Expected a Vite plugin or a WebAnvil plugin created with definePlugin()");
100
- const userConfigSchema = z.strictObject({
101
- build: buildConfigSchema.optional(),
102
- format: formatConfigSchema.optional(),
103
- lint: lintConfigSchema.optional(),
104
- rolldown: rolldownConfigSchema.optional(),
105
- test: testConfigSchema.optional(),
106
- vite: viteConfigSchema.optional(),
107
- plugins: z.array(pluginSchema).optional()
108
- });
109
- const effectiveUserConfigSchema = userConfigSchema.superRefine((config, context) => {
110
- const build = config.build ?? {};
111
- if (build.entries !== void 0 && build.mode !== "node") context.addIssue({
112
- code: "custom",
113
- path: ["build", "entries"],
114
- message: "build.entries is only available in Node mode"
115
- });
116
- if (build.mode === "web" && build.platform !== void 0) context.addIssue({
117
- code: "custom",
118
- path: ["build", "platform"],
119
- message: "Web builds do not accept build.platform"
120
- });
121
- if (build.mode === "node") {
122
- for (const [index, plugin] of (config.plugins ?? []).entries()) if (!isUnpluginAdapter(plugin)) context.addIssue({
123
- code: "custom",
124
- path: ["plugins", index],
125
- message: NODE_PLUGIN_ERROR
126
- });
127
- }
128
- });
129
- const defaultConfig = {
130
- build: {
131
- mode: "node",
132
- entry: "src/index.ts",
133
- outDir: "dist"
134
- },
135
- test: { environment: "node" }
136
- };
137
- const toCommandArguments = (config) => Object.fromEntries(Object.entries(config).filter(([, value]) => value !== void 0).map(([key, value]) => [key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`), value]));
138
- const defined = (arguments_) => Object.fromEntries(Object.entries(arguments_).filter(([, value]) => value !== void 0));
139
- const defineConfig = (config) => config;
140
- const loadConfig$1 = async (cwd = process.cwd()) => {
141
- const { config, configFile } = await loadConfig({
142
- name: "webanvil",
143
- cwd,
144
- configFile: "webanvil.config",
145
- packageJson: false,
146
- rcFile: false
147
- });
148
- return {
149
- config: userConfigSchema.parse(defu(config, defaultConfig)),
150
- configFile
151
- };
152
- };
153
- const resolveEffectiveBuildConfig = (config, overrides, explicitEntry) => {
154
- const build = {
155
- ...config.build,
156
- ...defined(overrides)
157
- };
158
- if (explicitEntry) delete build.entries;
159
- return effectiveUserConfigSchema.parse({
160
- ...config,
161
- build
162
- }).build ?? {};
163
- };
164
- const withConfig = (select, run) => async (arguments_) => {
165
- const { config } = await loadConfig$1();
166
- const selectedConfig = select(config) ?? {};
167
- return run({
168
- ...toCommandArguments(selectedConfig),
169
- ...defined(arguments_)
170
- }, selectedConfig, config, arguments_);
47
+ const hasStorybookConfig = async (configDir = ".storybook", cwd = process.cwd()) => {
48
+ const files = await readdir(resolve(cwd, configDir)).catch(() => []);
49
+ return [
50
+ "js",
51
+ "mjs",
52
+ "cjs",
53
+ "ts",
54
+ "mts",
55
+ "cts"
56
+ ].some((extension) => files.includes(`main.${extension}`));
171
57
  };
172
58
  const path = (cwd) => resolve(cwd, ".webanvil", "buildinfo.json");
173
59
  const relativeOutput = (file, cwd) => {
@@ -241,6 +127,11 @@ const supportedTools = {
241
127
  packageName: "vitest",
242
128
  range: ">=4.1.10 <5"
243
129
  },
130
+ storybook: {
131
+ packageName: "storybook",
132
+ range: ">=10.5.9 <11",
133
+ bin: "storybook"
134
+ },
244
135
  rolldown: {
245
136
  packageName: "rolldown",
246
137
  range: ">=1.2.0 <2"
@@ -1217,6 +1108,52 @@ const useToolExecutable = async (name, toolchain = new Toolchain(process.cwd()))
1217
1108
  if (tool.executable === void 0) throw new Error(`${tool.packageName} does not expose an executable`);
1218
1109
  return tool.executable;
1219
1110
  };
1111
+ const storybookVitestVersion = "4.1.10";
1112
+ const storybookOutputDir = (config = {}, options = {}) => options.outDir ?? config.outDir ?? "storybook-static";
1113
+ const exitWithError = (action, exitCode) => {
1114
+ throw new Error(`storybook ${action} exited with code ${exitCode ?? "unknown"}`);
1115
+ };
1116
+ const runStorybook = async (action, config = {}, options = {}, toolchain = new Toolchain(process.cwd())) => {
1117
+ const executable = await useToolExecutable("storybook", toolchain);
1118
+ const configDir = options.configDir ?? config.configDir;
1119
+ const outDir = storybookOutputDir(config, options);
1120
+ const result = await execa(executable, [
1121
+ action,
1122
+ ...configDir === void 0 ? [] : ["--config-dir", configDir],
1123
+ ...action === "dev" && options.host !== void 0 ? ["--host", options.host] : [],
1124
+ ...action === "dev" && options.port !== void 0 ? ["--port", String(options.port)] : [],
1125
+ ...action === "build" ? ["--output-dir", outDir] : []
1126
+ ], {
1127
+ reject: false,
1128
+ stdio: "inherit"
1129
+ });
1130
+ if (result.exitCode !== 0) exitWithError(action, result.exitCode);
1131
+ };
1132
+ const createStorybookTestProject = async (config = {}, vitestVersion = storybookVitestVersion) => {
1133
+ if (vitestVersion !== storybookVitestVersion) throw new Error(`Storybook tests require Vitest ${storybookVitestVersion}; Webanvil bundles @vitest/browser-playwright ${storybookVitestVersion}`);
1134
+ const previousVitest = process.env.VITEST;
1135
+ process.env.VITEST = "true";
1136
+ let plugins;
1137
+ try {
1138
+ plugins = await storybookTest({ configDir: config.configDir });
1139
+ } finally {
1140
+ if (previousVitest === void 0) delete process.env.VITEST;
1141
+ else process.env.VITEST = previousVitest;
1142
+ }
1143
+ return {
1144
+ extends: true,
1145
+ plugins,
1146
+ test: {
1147
+ name: "storybook",
1148
+ browser: {
1149
+ enabled: true,
1150
+ headless: true,
1151
+ provider: playwright(),
1152
+ instances: [{ browser: "chromium" }]
1153
+ }
1154
+ }
1155
+ };
1156
+ };
1220
1157
  const bundle = defineOption({
1221
1158
  name: "bundle",
1222
1159
  description: "Bundle the Node public roots; without it, emit their reachable graph with preserveModules.",
@@ -1280,9 +1217,13 @@ const minify = defineOption({
1280
1217
  });
1281
1218
  const mode = defineOption({
1282
1219
  name: "mode",
1283
- description: "Build mode: web uses Vite and node uses Rolldown.",
1220
+ description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.",
1284
1221
  arity: 1,
1285
- schema: z.enum(["web", "node"])
1222
+ schema: z.enum([
1223
+ "web",
1224
+ "node",
1225
+ "storybook"
1226
+ ])
1286
1227
  });
1287
1228
  const open = defineOption({
1288
1229
  name: "open",
@@ -1346,6 +1287,24 @@ const noBundle$1 = defineOption({
1346
1287
  description: "Emit the reachable Node graph with preserveModules, overriding configuration that enables bundling.",
1347
1288
  arity: 0
1348
1289
  });
1290
+ const assertStorybookArguments$1 = (explicit) => {
1291
+ const option = [
1292
+ "bundle",
1293
+ "copy",
1294
+ "declaration",
1295
+ "entry",
1296
+ "formats",
1297
+ "minify",
1298
+ "no-bundle",
1299
+ "platform",
1300
+ "sourcemap",
1301
+ "target"
1302
+ ].find((name) => {
1303
+ const value = explicit[name];
1304
+ return name === "bundle" || name === "no-bundle" ? value === true : value !== void 0;
1305
+ });
1306
+ if (option !== void 0) throw new Error(`--${option} is not available in Storybook mode`);
1307
+ };
1349
1308
  const outputFiles = (result, outDir) => {
1350
1309
  if ("on" in result) throw new Error("Web builds cannot use watch mode");
1351
1310
  return (Array.isArray(result) ? result : [result]).flatMap((output) => output.output.map((file) => resolve(outDir, file.fileName)));
@@ -1422,6 +1381,21 @@ build.publicOutputFiles = async ({ outDir, publicDir }) => publicDir ? (await gl
1422
1381
  })).map((file) => resolve(outDir, file)) : [];
1423
1382
  build.web = async (web) => [...outputFiles(await web.vite.build(web.config), web.outDir), ...await build.publicOutputFiles(web)];
1424
1383
  const commandRun$1 = (toolchain) => withConfig((config) => config.build, ({ copy, declaration, formats, minify, mode, entry, "out-dir": outDir, platform, sourcemap, target }, buildConfig, resolvedConfig, explicit) => {
1384
+ if (mode === "storybook") {
1385
+ assertStorybookArguments$1(explicit);
1386
+ const storybookOptions = { outDir: explicit["out-dir"] === void 0 ? void 0 : outDir };
1387
+ const outputDirectory = storybookOutputDir(resolvedConfig.storybook, storybookOptions);
1388
+ return (async () => {
1389
+ const existing = await removeOutputsIn(outputDirectory);
1390
+ await runStorybook("build", resolvedConfig.storybook, storybookOptions, toolchain);
1391
+ const output = await glob("**/*", {
1392
+ cwd: resolve(process.cwd(), outputDirectory),
1393
+ dot: true,
1394
+ onlyFiles: true
1395
+ });
1396
+ await writeBuildInfo([...existing.output, ...output.map((file) => resolve(outputDirectory, file))]);
1397
+ })();
1398
+ }
1425
1399
  if (explicit.bundle && explicit["no-bundle"]) throw new Error("--bundle and --no-bundle cannot be used together");
1426
1400
  const effective = resolveEffectiveBuildConfig(resolvedConfig, {
1427
1401
  bundle: explicit["no-bundle"] ? false : explicit.bundle ? true : buildConfig.bundle,
@@ -1437,7 +1411,9 @@ const commandRun$1 = (toolchain) => withConfig((config) => config.build, ({ copy
1437
1411
  sourcemap,
1438
1412
  target
1439
1413
  }, explicit.entry !== void 0);
1440
- return build(effective.mode, effective.entry, effective.outDir, effective, resolvedConfig.plugins ?? [], resolvedConfig.vite, resolvedConfig.rolldown, toolchain, {
1414
+ const executableMode = effective.mode;
1415
+ if (executableMode !== "web" && executableMode !== "node") throw new Error("Expected a web or Node build mode");
1416
+ return build(executableMode, effective.entry, effective.outDir, effective, resolvedConfig.plugins ?? [], resolvedConfig.vite, resolvedConfig.rolldown, toolchain, {
1441
1417
  ...explicit.entry === void 0 ? {} : { entry },
1442
1418
  ...explicit["out-dir"] === void 0 ? {} : { outDir },
1443
1419
  ...explicit.minify === void 0 ? {} : { minify },
@@ -1462,9 +1438,12 @@ var build_default = defineCommand({
1462
1438
  target
1463
1439
  ],
1464
1440
  run: async (arguments_) => {
1441
+ const config = arguments_.mode === void 0 ? (await loadConfig()).config : void 0;
1442
+ const selectedMode = arguments_.mode ?? config?.build?.mode;
1465
1443
  const toolchain = new Toolchain(process.cwd());
1466
- await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1467
- return commandRun$1(toolchain)(arguments_);
1444
+ if (selectedMode === "storybook") await toolchain.resolve("storybook");
1445
+ else await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1446
+ return commandRun$1(toolchain)(arguments_, config);
1468
1447
  }
1469
1448
  });
1470
1449
  const rebasePath = (path, cwd, configDirectory) => {
@@ -1621,7 +1600,7 @@ var check_default = defineCommand({
1621
1600
  useTool("oxlint"),
1622
1601
  useTool("typescript-native")
1623
1602
  ]);
1624
- const { config } = await loadConfig$1();
1603
+ const { config } = await loadConfig();
1625
1604
  await checkProject(fix, config);
1626
1605
  }
1627
1606
  });
@@ -1649,6 +1628,25 @@ const noBundle = defineOption({
1649
1628
  description: "Emit the reachable Node graph with preserveModules, overriding configuration that enables bundling.",
1650
1629
  arity: 0
1651
1630
  });
1631
+ const assertStorybookArguments = (explicit) => {
1632
+ const option = [
1633
+ "bundle",
1634
+ "copy",
1635
+ "declaration",
1636
+ "entry",
1637
+ "formats",
1638
+ "minify",
1639
+ "no-bundle",
1640
+ "out-dir",
1641
+ "platform",
1642
+ "sourcemap",
1643
+ "target"
1644
+ ].find((name) => {
1645
+ const value = explicit[name];
1646
+ return name === "bundle" || name === "no-bundle" ? value === true : value !== void 0;
1647
+ });
1648
+ if (option !== void 0) throw new Error(`--${option} is not available in Storybook mode`);
1649
+ };
1652
1650
  const dev = async (mode, entry, outDir, host, port, plugins = [], options = {}, viteConfig = {}, rolldownConfig = {}, toolchain = new Toolchain(process.cwd())) => {
1653
1651
  assertSyntaxTarget(options.target);
1654
1652
  if (mode === "web" && options.platform !== void 0) throw new Error("Web development does not accept platform; platform applies only to Node builds");
@@ -1721,6 +1719,13 @@ dev.node = async (entry, outDir, plugins = [], waitForTermination = untilTermina
1721
1719
  }
1722
1720
  };
1723
1721
  const commandRun = (toolchain) => withConfig((config) => config.build, ({ copy, declaration, formats, minify, mode, entry, "out-dir": outDir, host, platform, port, sourcemap, target }, buildConfig, resolvedConfig, explicit) => {
1722
+ if (mode === "storybook") {
1723
+ assertStorybookArguments(explicit);
1724
+ return runStorybook("dev", resolvedConfig.storybook, {
1725
+ host,
1726
+ port
1727
+ }, toolchain);
1728
+ }
1724
1729
  if (explicit.bundle && explicit["no-bundle"]) throw new Error("--bundle and --no-bundle cannot be used together");
1725
1730
  const effective = resolveEffectiveBuildConfig(resolvedConfig, {
1726
1731
  bundle: explicit["no-bundle"] ? false : explicit.bundle ? true : buildConfig.bundle,
@@ -1736,7 +1741,9 @@ const commandRun = (toolchain) => withConfig((config) => config.build, ({ copy,
1736
1741
  sourcemap,
1737
1742
  target
1738
1743
  }, explicit.entry !== void 0);
1739
- return dev(effective.mode, effective.entry, effective.outDir, host, port, resolvedConfig.plugins ?? [], effective, resolvedConfig.vite, resolvedConfig.rolldown, toolchain);
1744
+ const executableMode = effective.mode;
1745
+ if (executableMode !== "web" && executableMode !== "node") throw new Error("Expected a web or Node build mode");
1746
+ return dev(executableMode, effective.entry, effective.outDir, host, port, resolvedConfig.plugins ?? [], effective, resolvedConfig.vite, resolvedConfig.rolldown, toolchain);
1740
1747
  });
1741
1748
  var dev_default = defineCommand({
1742
1749
  name: "dev",
@@ -1757,9 +1764,12 @@ var dev_default = defineCommand({
1757
1764
  target
1758
1765
  ],
1759
1766
  run: async (arguments_) => {
1767
+ const config = arguments_.mode === void 0 ? (await loadConfig()).config : void 0;
1768
+ const selectedMode = arguments_.mode ?? config?.build?.mode;
1760
1769
  const toolchain = new Toolchain(process.cwd());
1761
- await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1762
- return commandRun(toolchain)(arguments_);
1770
+ if (selectedMode === "storybook") await toolchain.resolve("storybook");
1771
+ else await Promise.all([toolchain.resolve("vite"), toolchain.resolve("rolldown")]);
1772
+ return commandRun(toolchain)(arguments_, config);
1763
1773
  }
1764
1774
  });
1765
1775
  const preview = async (outDir, host, port, useOutDir = false, waitForTermination = untilTerminated, openBrowser, viteConfig = {}) => {
@@ -1797,20 +1807,22 @@ var preview_default = defineCommand({
1797
1807
  ],
1798
1808
  run: async ({ "out-dir": outDir, host, port, open }) => {
1799
1809
  await useTool("vite");
1800
- const { config } = await loadConfig$1();
1810
+ const { config } = await loadConfig();
1801
1811
  return preview(outDir ?? config.build?.outDir ?? "dist", host, port, outDir !== void 0, untilTerminated, open, config.vite);
1802
1812
  }
1803
1813
  });
1804
- const test = async (filters, config = {}, options = {}, waitForTermination = untilTerminated) => {
1814
+ const test = async (filters, config = {}, options = {}, waitForTermination = untilTerminated, storybook = {}) => {
1805
1815
  if (options.uiPort !== void 0 && options.ui !== true) throw new Error("--ui-port requires --ui");
1806
1816
  logger.start("Running tests");
1807
1817
  const hasVitestConfig = await hasToolConfig("vitest");
1808
1818
  const persistent = options.watch === true || options.ui === true;
1809
- const { startVitest } = await useToolApi("vitest", "node");
1819
+ const vitestTool = await useTool("vitest");
1820
+ const { startVitest } = await vitestTool.import("node");
1810
1821
  const nativeConfig = hasVitestConfig ? {} : config;
1811
1822
  const nativeCoverage = typeof nativeConfig.coverage === "object" && nativeConfig.coverage !== null ? nativeConfig.coverage : {};
1812
1823
  const nativeApi = typeof nativeConfig.api === "object" && nativeConfig.api !== null ? nativeConfig.api : {};
1813
- const vitest = await startVitest("test", filters, {
1824
+ const storybookProject = storybook.test === false || !await hasStorybookConfig(storybook.configDir) ? void 0 : await createStorybookTestProject(storybook, vitestTool.version);
1825
+ const vitestOptions = {
1814
1826
  ...nativeConfig,
1815
1827
  passWithNoTests: true,
1816
1828
  run: !persistent,
@@ -1828,27 +1840,38 @@ const test = async (filters, config = {}, options = {}, waitForTermination = unt
1828
1840
  port: options.uiPort,
1829
1841
  strictPort: true
1830
1842
  } }
1831
- });
1843
+ };
1844
+ const vitests = hasVitestConfig && storybookProject !== void 0 ? [await startVitest("test", filters, vitestOptions), await startVitest("test", filters, {
1845
+ ...vitestOptions,
1846
+ ...options.ui ? {
1847
+ api: false,
1848
+ ui: false
1849
+ } : {},
1850
+ projects: [storybookProject]
1851
+ })] : [await startVitest("test", filters, {
1852
+ ...vitestOptions,
1853
+ ...storybookProject === void 0 ? {} : { projects: [...nativeConfig.projects ?? [], storybookProject] }
1854
+ })];
1832
1855
  if (persistent) {
1833
1856
  try {
1834
1857
  await waitForTermination();
1835
1858
  } finally {
1836
- await vitest.close();
1859
+ await Promise.all(vitests.map((vitest) => vitest.close()));
1837
1860
  }
1838
1861
  return;
1839
1862
  }
1840
- const failed = vitest.state.getFiles().some((file) => file.result?.state === "fail") || vitest.state.getUnhandledErrors().length > 0;
1841
- await vitest.close();
1863
+ const failed = vitests.some((vitest) => vitest.state.getFiles().some((file) => file.result?.state === "fail") || vitest.state.getUnhandledErrors().length > 0);
1864
+ await Promise.all(vitests.map((vitest) => vitest.close()));
1842
1865
  if (failed) throw new Error("Tests failed");
1843
1866
  logger.success("Tests passed");
1844
1867
  };
1845
- const runTest = withConfig((config) => config.test, ({ filters, environment, coverage, ui, "ui-port": uiPort, watch }, config, _resolvedConfig, explicitArguments) => test(filters, config, {
1868
+ const runTest = withConfig((config) => config.test, ({ filters, environment, coverage, ui, "ui-port": uiPort, watch }, config, resolvedConfig, explicitArguments) => test(filters, config, {
1846
1869
  coverage,
1847
1870
  environment: explicitArguments.environment === void 0 ? void 0 : environment,
1848
1871
  ui,
1849
1872
  uiPort,
1850
1873
  watch
1851
- }));
1874
+ }, untilTerminated, resolvedConfig.storybook));
1852
1875
  var test_default = defineCommand({
1853
1876
  name: "test",
1854
1877
  arguments: [filters],
@@ -1864,4 +1887,4 @@ var test_default = defineCommand({
1864
1887
  return runTest(arguments_);
1865
1888
  }
1866
1889
  });
1867
- export { assertSyntaxTarget, build, buildConfigSchema, build_default, bundle, check, checkProject, check_default, clean, clean_default, copy, copyMappingSchema, coverage, declaration, defaultConfig, defineConfig, definePlugin, dev, dev_default, effectiveUserConfigSchema, entry, environment, filters, fix$1 as fix, format, formatConfigSchema, format_default, formats, host, isUnpluginAdapter, isWebAnvilPlugin, lint, lintConfigSchema, lint_default, loadConfig$1 as loadConfig, logger, minify, mode, open, outDir, paths, platform, port, preview, preview_default, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap, syntaxTargetSchema, target, test, testConfigSchema, test_default, typecheck, typecheck_default, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig };
1890
+ export { build, build_default, bundle, check, checkProject, check_default, clean, clean_default, copy, coverage, declaration, dev, dev_default, entry, environment, filters, fix$1 as fix, format, format_default, formats, host, lint, lint_default, logger, minify, mode, open, outDir, paths, platform, port, preview, preview_default, sourcemap, target, test, test_default, typecheck, typecheck_default, ui, uiPort, watch };
@@ -0,0 +1,140 @@
1
+ import { loadConfig } from "c12";
2
+ import { defu } from "defu";
3
+ import { z } from "zod";
4
+ const NODE_PLUGIN_ERROR = "Node builds require plugins created with definePlugin()";
5
+ const definePlugin = (plugin, options) => ({
6
+ rolldown: () => plugin.rolldown(options),
7
+ vite: () => plugin.vite(options)
8
+ });
9
+ const isUnpluginAdapter = (plugin) => typeof plugin === "object" && plugin !== null && "rolldown" in plugin && typeof plugin.rolldown === "function" && "vite" in plugin && typeof plugin.vite === "function";
10
+ const isWebAnvilPlugin = (plugin) => Array.isArray(plugin) || typeof plugin === "function" || typeof plugin === "object" && plugin !== null && "name" in plugin || isUnpluginAdapter(plugin);
11
+ const resolveRolldownPlugins = (plugins) => plugins.flatMap((plugin) => {
12
+ if (!isUnpluginAdapter(plugin)) throw new Error(NODE_PLUGIN_ERROR);
13
+ return plugin.rolldown();
14
+ });
15
+ const resolveVitePlugins = (plugins) => plugins.map((plugin) => isUnpluginAdapter(plugin) ? plugin.vite() : plugin);
16
+ const copyMappingSchema = z.strictObject({
17
+ from: z.string().min(1),
18
+ to: z.string().min(1)
19
+ });
20
+ const legacyPlatformTarget = (target) => (typeof target === "string" ? [target] : target).find((value) => value === "browser" || value === "neutral");
21
+ const assertSyntaxTarget = (target) => {
22
+ if (target === void 0) return;
23
+ const legacy = legacyPlatformTarget(target);
24
+ if (legacy !== void 0) throw new Error(`build.target no longer selects a platform; use build.platform: "${legacy}" instead`);
25
+ };
26
+ const syntaxTargetSchema = z.union([z.string().min(1), z.array(z.string().min(1)).min(1)]).superRefine((target, context) => {
27
+ const legacy = legacyPlatformTarget(target);
28
+ if (legacy !== void 0) context.addIssue({
29
+ code: "custom",
30
+ message: `build.target no longer selects a platform; use build.platform: "${legacy}" instead`
31
+ });
32
+ });
33
+ const nativeConfigSchema = () => z.custom((value) => typeof value === "object" && value !== null && !Array.isArray(value), "Expected a configuration object");
34
+ const buildConfigSchema = z.strictObject({
35
+ bundle: z.boolean().optional(),
36
+ mode: z.enum([
37
+ "web",
38
+ "node",
39
+ "storybook"
40
+ ]).optional(),
41
+ entry: z.string().min(1).optional(),
42
+ entries: z.record(z.string().min(1), z.string().min(1)).optional(),
43
+ outDir: z.string().min(1).optional(),
44
+ declaration: z.union([z.boolean(), nativeConfigSchema()]).optional(),
45
+ sourcemap: z.boolean().optional(),
46
+ minify: z.boolean().optional(),
47
+ copy: z.array(copyMappingSchema).optional(),
48
+ formats: z.array(z.enum(["esm", "cjs"])).min(1).optional(),
49
+ platform: z.enum([
50
+ "node",
51
+ "browser",
52
+ "neutral"
53
+ ]).optional(),
54
+ target: syntaxTargetSchema.optional()
55
+ });
56
+ const formatConfigSchema = nativeConfigSchema();
57
+ const lintConfigSchema = nativeConfigSchema();
58
+ const rolldownConfigSchema = nativeConfigSchema();
59
+ const testConfigSchema = nativeConfigSchema();
60
+ const viteConfigSchema = nativeConfigSchema();
61
+ const storybookConfigSchema = z.strictObject({
62
+ configDir: z.string().min(1).optional(),
63
+ outDir: z.string().min(1).optional(),
64
+ test: z.boolean().optional()
65
+ });
66
+ const pluginSchema = z.custom(isWebAnvilPlugin, "Expected a Vite plugin or a WebAnvil plugin created with definePlugin()");
67
+ const userConfigSchema = z.strictObject({
68
+ build: buildConfigSchema.optional(),
69
+ format: formatConfigSchema.optional(),
70
+ lint: lintConfigSchema.optional(),
71
+ rolldown: rolldownConfigSchema.optional(),
72
+ storybook: storybookConfigSchema.optional(),
73
+ test: testConfigSchema.optional(),
74
+ vite: viteConfigSchema.optional(),
75
+ plugins: z.array(pluginSchema).optional()
76
+ });
77
+ const effectiveUserConfigSchema = userConfigSchema.superRefine((config, context) => {
78
+ const build = config.build ?? {};
79
+ if (build.entries !== void 0 && build.mode !== "node") context.addIssue({
80
+ code: "custom",
81
+ path: ["build", "entries"],
82
+ message: "build.entries is only available in Node mode"
83
+ });
84
+ if (build.mode === "web" && build.platform !== void 0) context.addIssue({
85
+ code: "custom",
86
+ path: ["build", "platform"],
87
+ message: "Web builds do not accept build.platform"
88
+ });
89
+ if (build.mode === "node") {
90
+ for (const [index, plugin] of (config.plugins ?? []).entries()) if (!isUnpluginAdapter(plugin)) context.addIssue({
91
+ code: "custom",
92
+ path: ["plugins", index],
93
+ message: NODE_PLUGIN_ERROR
94
+ });
95
+ }
96
+ });
97
+ const defaultConfig = {
98
+ build: {
99
+ mode: "node",
100
+ entry: "src/index.ts",
101
+ outDir: "dist"
102
+ },
103
+ test: { environment: "node" }
104
+ };
105
+ const toCommandArguments = (config) => Object.fromEntries(Object.entries(config).filter(([, value]) => value !== void 0).map(([key, value]) => [key.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`), value]));
106
+ const defined = (arguments_) => Object.fromEntries(Object.entries(arguments_).filter(([, value]) => value !== void 0));
107
+ const defineConfig = (config) => config;
108
+ const loadConfig$1 = async (cwd = process.cwd()) => {
109
+ const { config, configFile } = await loadConfig({
110
+ name: "webanvil",
111
+ cwd,
112
+ configFile: "webanvil.config",
113
+ packageJson: false,
114
+ rcFile: false
115
+ });
116
+ return {
117
+ config: userConfigSchema.parse(defu(config, defaultConfig)),
118
+ configFile
119
+ };
120
+ };
121
+ const resolveEffectiveBuildConfig = (config, overrides, explicitEntry) => {
122
+ const build = {
123
+ ...config.build,
124
+ ...defined(overrides)
125
+ };
126
+ if (explicitEntry) delete build.entries;
127
+ return effectiveUserConfigSchema.parse({
128
+ ...config,
129
+ build
130
+ }).build ?? {};
131
+ };
132
+ const withConfig = (select, run) => async (arguments_, resolvedConfig) => {
133
+ const config = resolvedConfig ?? (await loadConfig$1()).config;
134
+ const selectedConfig = select(config) ?? {};
135
+ return run({
136
+ ...toCommandArguments(selectedConfig),
137
+ ...defined(arguments_)
138
+ }, selectedConfig, config, arguments_);
139
+ };
140
+ export { assertSyntaxTarget, buildConfigSchema, copyMappingSchema, defaultConfig, defineConfig, definePlugin, effectiveUserConfigSchema, formatConfigSchema, isUnpluginAdapter, isWebAnvilPlugin, lintConfigSchema, loadConfig$1 as loadConfig, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, storybookConfigSchema, syntaxTargetSchema, testConfigSchema, userConfigSchema, viteConfigSchema, withConfig };
@@ -0,0 +1,9 @@
1
+ import { loadConfig, resolveVitePlugins } from "./config.mjs";
2
+ const withProjectVitePlugins = async (config) => {
3
+ const { config: webanvilConfig } = await loadConfig();
4
+ return {
5
+ ...config,
6
+ plugins: [...resolveVitePlugins(webanvilConfig.plugins ?? []), ...config.plugins ?? []]
7
+ };
8
+ };
9
+ export { withProjectVitePlugins };
package/dist/index.d.mts CHANGED
@@ -35,7 +35,7 @@ declare const isUnpluginAdapter: (plugin: unknown) => plugin is UnpluginAdapter;
35
35
  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
- type ToolName = "vite" | "vitest" | "rolldown" | "oxlint" | "oxfmt" | "typescript" | "typescript-native";
38
+ type ToolName = "vite" | "vitest" | "rolldown" | "oxlint" | "oxfmt" | "storybook" | "typescript" | "typescript-native";
39
39
  type ToolSource = "project" | "webanvil";
40
40
  type ResolvedTool = {
41
41
  name: ToolName;
@@ -103,6 +103,7 @@ declare const buildConfigSchema: z.ZodObject<{
103
103
  mode: z.ZodOptional<z.ZodEnum<{
104
104
  web: "web";
105
105
  node: "node";
106
+ storybook: "storybook";
106
107
  }>>;
107
108
  entry: z.ZodOptional<z.ZodString>;
108
109
  entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -137,12 +138,18 @@ declare const lintConfigSchema: z.ZodCustom<LintConfig, LintConfig>;
137
138
  declare const rolldownConfigSchema: z.ZodCustom<RolldownConfig, RolldownConfig>;
138
139
  declare const testConfigSchema: z.ZodCustom<TestUserConfig, TestUserConfig>;
139
140
  declare const viteConfigSchema: z.ZodCustom<UserConfig$1, UserConfig$1>;
141
+ declare const storybookConfigSchema: z.ZodObject<{
142
+ configDir: z.ZodOptional<z.ZodString>;
143
+ outDir: z.ZodOptional<z.ZodString>;
144
+ test: z.ZodOptional<z.ZodBoolean>;
145
+ }, z.core.$strict>;
140
146
  declare const userConfigSchema: z.ZodObject<{
141
147
  build: z.ZodOptional<z.ZodObject<{
142
148
  bundle: z.ZodOptional<z.ZodBoolean>;
143
149
  mode: z.ZodOptional<z.ZodEnum<{
144
150
  web: "web";
145
151
  node: "node";
152
+ storybook: "storybook";
146
153
  }>>;
147
154
  entry: z.ZodOptional<z.ZodString>;
148
155
  entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -168,6 +175,11 @@ declare const userConfigSchema: z.ZodObject<{
168
175
  format: z.ZodOptional<z.ZodCustom<OxfmtConfig, OxfmtConfig>>;
169
176
  lint: z.ZodOptional<z.ZodCustom<LintConfig, LintConfig>>;
170
177
  rolldown: z.ZodOptional<z.ZodCustom<RolldownConfig, RolldownConfig>>;
178
+ storybook: z.ZodOptional<z.ZodObject<{
179
+ configDir: z.ZodOptional<z.ZodString>;
180
+ outDir: z.ZodOptional<z.ZodString>;
181
+ test: z.ZodOptional<z.ZodBoolean>;
182
+ }, z.core.$strict>>;
171
183
  test: z.ZodOptional<z.ZodCustom<TestUserConfig, TestUserConfig>>;
172
184
  vite: z.ZodOptional<z.ZodCustom<UserConfig$1, UserConfig$1>>;
173
185
  plugins: z.ZodOptional<z.ZodArray<z.ZodCustom<WebAnvilPlugin, WebAnvilPlugin>>>;
@@ -178,6 +190,7 @@ declare const effectiveUserConfigSchema: z.ZodObject<{
178
190
  mode: z.ZodOptional<z.ZodEnum<{
179
191
  web: "web";
180
192
  node: "node";
193
+ storybook: "storybook";
181
194
  }>>;
182
195
  entry: z.ZodOptional<z.ZodString>;
183
196
  entries: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
@@ -203,6 +216,11 @@ declare const effectiveUserConfigSchema: z.ZodObject<{
203
216
  format: z.ZodOptional<z.ZodCustom<OxfmtConfig, OxfmtConfig>>;
204
217
  lint: z.ZodOptional<z.ZodCustom<LintConfig, LintConfig>>;
205
218
  rolldown: z.ZodOptional<z.ZodCustom<RolldownConfig, RolldownConfig>>;
219
+ storybook: z.ZodOptional<z.ZodObject<{
220
+ configDir: z.ZodOptional<z.ZodString>;
221
+ outDir: z.ZodOptional<z.ZodString>;
222
+ test: z.ZodOptional<z.ZodBoolean>;
223
+ }, z.core.$strict>>;
206
224
  test: z.ZodOptional<z.ZodCustom<TestUserConfig, TestUserConfig>>;
207
225
  vite: z.ZodOptional<z.ZodCustom<UserConfig$1, UserConfig$1>>;
208
226
  plugins: z.ZodOptional<z.ZodArray<z.ZodCustom<WebAnvilPlugin, WebAnvilPlugin>>>;
@@ -212,6 +230,7 @@ type CopyMapping = z.infer<typeof copyMappingSchema>;
212
230
  type FormatConfig = OxfmtConfig;
213
231
  type TestConfig = TestUserConfig;
214
232
  type ViteConfig = UserConfig$1;
233
+ type StorybookConfig = z.infer<typeof storybookConfigSchema>;
215
234
  type UserConfig = z.infer<typeof userConfigSchema>;
216
235
  type UserConfigFactory = () => UserConfig | Promise<UserConfig>;
217
236
  type ConfigExport = UserConfig | UserConfigFactory;
@@ -220,7 +239,7 @@ type ResolvedConfig = {
220
239
  configFile?: string;
221
240
  };
222
241
  type CommandArguments = Record<string, unknown>;
223
- type ConfigSection = BuildConfig | FormatConfig | LintConfig | TestConfig;
242
+ type ConfigSection = BuildConfig | FormatConfig | LintConfig | StorybookConfig | TestConfig;
224
243
  type ResolvedArguments<TArguments extends CommandArguments> = { [TKey in keyof TArguments]-?: Exclude<TArguments[TKey], undefined>; };
225
244
  declare const defaultConfig: {
226
245
  build: {
@@ -234,7 +253,7 @@ declare const defaultConfig: {
234
253
  };
235
254
  declare const loadConfig: (cwd?: string) => Promise<ResolvedConfig>;
236
255
  declare const resolveEffectiveBuildConfig: (config: UserConfig, overrides: BuildConfig, explicitEntry: boolean) => BuildConfig;
237
- declare const withConfig: <TConfig extends ConfigSection, TArguments extends CommandArguments, TResult>(select: (config: UserConfig) => TConfig | undefined, run: (arguments_: ResolvedArguments<TArguments>, config: TConfig, resolvedConfig: UserConfig, explicitArguments: TArguments) => TResult | Promise<TResult>) => (arguments_: TArguments) => Promise<TResult>;
256
+ declare const withConfig: <TConfig extends ConfigSection, TArguments extends CommandArguments, TResult>(select: (config: UserConfig) => TConfig | undefined, run: (arguments_: ResolvedArguments<TArguments>, config: TConfig, resolvedConfig: UserConfig, explicitArguments: TArguments) => TResult | Promise<TResult>) => (arguments_: TArguments, resolvedConfig?: UserConfig) => Promise<TResult>;
238
257
  type NodeBuildOptions = Pick<BuildConfig, "bundle" | "copy" | "declaration" | "entries" | "formats" | "minify" | "platform" | "sourcemap" | "target">;
239
258
  type BuildOptions = NodeBuildOptions;
240
259
  type ViteApi = typeof import("vite");
@@ -257,11 +276,12 @@ declare const build: {
257
276
  };
258
277
  declare const _default: import("cmdore").Command<readonly [{
259
278
  readonly name: "mode";
260
- readonly description: "Build mode: web uses Vite and node uses Rolldown.";
279
+ readonly description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.";
261
280
  readonly arity: 1;
262
281
  readonly schema: import("zod").ZodEnum<{
263
282
  web: "web";
264
283
  node: "node";
284
+ storybook: "storybook";
265
285
  }>;
266
286
  }, {
267
287
  readonly name: "out-dir";
@@ -348,11 +368,12 @@ declare const dev: {
348
368
  };
349
369
  declare const _default$3: import("cmdore").Command<readonly [{
350
370
  readonly name: "mode";
351
- readonly description: "Build mode: web uses Vite and node uses Rolldown.";
371
+ readonly description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.";
352
372
  readonly arity: 1;
353
373
  readonly schema: import("zod").ZodEnum<{
354
374
  web: "web";
355
375
  node: "node";
376
+ storybook: "storybook";
356
377
  }>;
357
378
  }, {
358
379
  readonly name: "out-dir";
@@ -477,7 +498,7 @@ declare const test: (filters: string[], config?: TestConfig, options?: {
477
498
  ui?: boolean;
478
499
  uiPort?: number;
479
500
  watch?: boolean;
480
- }, waitForTermination?: () => Promise<void>) => Promise<void>;
501
+ }, waitForTermination?: () => Promise<void>, storybook?: StorybookConfig) => Promise<void>;
481
502
  declare const _default$7: import("cmdore").Command<readonly [{
482
503
  readonly name: "environment";
483
504
  readonly description: "Vitest environment to use for this run.";
@@ -577,11 +598,12 @@ declare const minify$1: {
577
598
  };
578
599
  declare const mode: {
579
600
  readonly name: "mode";
580
- readonly description: "Build mode: web uses Vite and node uses Rolldown.";
601
+ readonly description: "Build mode: web uses Vite, node uses Rolldown, and storybook runs Storybook.";
581
602
  readonly arity: 1;
582
603
  readonly schema: z.ZodEnum<{
583
604
  web: "web";
584
605
  node: "node";
606
+ storybook: "storybook";
585
607
  }>;
586
608
  };
587
609
  declare const open: {
@@ -643,4 +665,4 @@ declare const watch: {
643
665
  };
644
666
  declare function defineConfig(config: UserConfig): UserConfig;
645
667
  declare function defineConfig(config: UserConfigFactory): UserConfigFactory;
646
- export { BuildConfig, ConfigExport, CopyMapping, FormatConfig, LintConfig, ResolvedConfig, RolldownConfig, SyntaxTarget, TestConfig, UnpluginAdapter, UserConfig, UserConfigFactory, ViteConfig, WebAnvilPlugin, WebAnvilUnplugin, assertSyntaxTarget, build, _default as buildCommand, buildConfigSchema, bundle$1 as bundle, check, _default$1 as checkCommand, checkProject, clean, _default$2 as cleanCommand, copy, copyMappingSchema, coverage, declaration, defaultConfig, defineConfig, definePlugin, dev, _default$3 as devCommand, effectiveUserConfigSchema, entry$1 as entry, environment, filters, fix, format, _default$4 as formatCommand, formatConfigSchema, formats$1 as formats, host, isUnpluginAdapter, isWebAnvilPlugin, lint, _default$5 as lintCommand, lintConfigSchema, loadConfig, minify$1 as minify, mode, open, outDir$1 as outDir, paths, platform$1 as platform, port, preview, _default$6 as previewCommand, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap$1 as sourcemap, syntaxTargetSchema, target$1 as target, test, _default$7 as testCommand, testConfigSchema, typecheck, _default$8 as typecheckCommand, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig };
668
+ export { BuildConfig, ConfigExport, CopyMapping, FormatConfig, LintConfig, ResolvedConfig, RolldownConfig, StorybookConfig, SyntaxTarget, TestConfig, UnpluginAdapter, UserConfig, UserConfigFactory, ViteConfig, WebAnvilPlugin, WebAnvilUnplugin, assertSyntaxTarget, build, _default as buildCommand, buildConfigSchema, bundle$1 as bundle, check, _default$1 as checkCommand, checkProject, clean, _default$2 as cleanCommand, copy, copyMappingSchema, coverage, declaration, defaultConfig, defineConfig, definePlugin, dev, _default$3 as devCommand, effectiveUserConfigSchema, entry$1 as entry, environment, filters, fix, format, _default$4 as formatCommand, formatConfigSchema, formats$1 as formats, host, isUnpluginAdapter, isWebAnvilPlugin, lint, _default$5 as lintCommand, lintConfigSchema, loadConfig, minify$1 as minify, mode, open, outDir$1 as outDir, paths, platform$1 as platform, port, preview, _default$6 as previewCommand, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap$1 as sourcemap, storybookConfigSchema, syntaxTargetSchema, target$1 as target, test, _default$7 as testCommand, testConfigSchema, typecheck, _default$8 as typecheckCommand, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig };
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { assertSyntaxTarget, build, buildConfigSchema, build_default, bundle, check, checkProject, check_default, clean, clean_default, copy, copyMappingSchema, coverage, declaration, defaultConfig, defineConfig as defineConfig$1, definePlugin, dev, dev_default, effectiveUserConfigSchema, entry, environment, filters, fix, format, formatConfigSchema, format_default, formats, host, isUnpluginAdapter, isWebAnvilPlugin, lint, lintConfigSchema, lint_default, loadConfig, minify, mode, open, outDir, paths, platform, port, preview, preview_default, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap, syntaxTargetSchema, target, test, testConfigSchema, test_default, typecheck, typecheck_default, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig } from "./_chunks/commands.mjs";
1
+ import { build, build_default, bundle, check, checkProject, check_default, clean, clean_default, copy, coverage, declaration, dev, dev_default, entry, environment, filters, fix, format, format_default, formats, host, lint, lint_default, minify, mode, open, outDir, paths, platform, port, preview, preview_default, sourcemap, target, test, test_default, typecheck, typecheck_default, ui, uiPort, watch } from "./_chunks/commands.mjs";
2
+ import { assertSyntaxTarget, buildConfigSchema, copyMappingSchema, defaultConfig, defineConfig as defineConfig$1, definePlugin, effectiveUserConfigSchema, formatConfigSchema, isUnpluginAdapter, isWebAnvilPlugin, lintConfigSchema, loadConfig, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, storybookConfigSchema, syntaxTargetSchema, testConfigSchema, userConfigSchema, viteConfigSchema, withConfig } from "./_chunks/config.mjs";
2
3
  function defineConfig(config) {
3
4
  return defineConfig$1(config);
4
5
  }
5
- export { assertSyntaxTarget, build, build_default as buildCommand, buildConfigSchema, bundle, check, check_default as checkCommand, checkProject, clean, clean_default as cleanCommand, copy, copyMappingSchema, coverage, declaration, defaultConfig, defineConfig, definePlugin, dev, dev_default as devCommand, effectiveUserConfigSchema, entry, environment, filters, fix, format, format_default as formatCommand, formatConfigSchema, formats, host, isUnpluginAdapter, isWebAnvilPlugin, lint, lint_default as lintCommand, lintConfigSchema, loadConfig, minify, mode, open, outDir, paths, platform, port, preview, preview_default as previewCommand, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap, syntaxTargetSchema, target, test, test_default as testCommand, testConfigSchema, typecheck, typecheck_default as typecheckCommand, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig };
6
+ export { assertSyntaxTarget, build, build_default as buildCommand, buildConfigSchema, bundle, check, check_default as checkCommand, checkProject, clean, clean_default as cleanCommand, copy, copyMappingSchema, coverage, declaration, defaultConfig, defineConfig, definePlugin, dev, dev_default as devCommand, effectiveUserConfigSchema, entry, environment, filters, fix, format, format_default as formatCommand, formatConfigSchema, formats, host, isUnpluginAdapter, isWebAnvilPlugin, lint, lint_default as lintCommand, lintConfigSchema, loadConfig, minify, mode, open, outDir, paths, platform, port, preview, preview_default as previewCommand, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap, storybookConfigSchema, syntaxTargetSchema, target, test, test_default as testCommand, testConfigSchema, typecheck, typecheck_default as typecheckCommand, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig };
@@ -0,0 +1,3 @@
1
+ import { StorybookConfig } from "@storybook/react-vite/node";
2
+ declare const framework: string;
3
+ export { type StorybookConfig, framework };
@@ -0,0 +1,4 @@
1
+ import { dirname } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ const framework = dirname(fileURLToPath(import.meta.url));
4
+ export { framework };
@@ -0,0 +1,8 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { viteFinal as viteFinal$1 } from "@storybook/react-vite/preset";
5
+ export * from "@storybook/react-vite/preset";
6
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
7
+ const viteFinal = async (...arguments_) => viteFinal$1(await withProjectVitePlugins(arguments_[0]), arguments_[1]);
8
+ export { addons, viteFinal };
@@ -0,0 +1,5 @@
1
+ import { viteFinal as viteFinal$1 } from "@storybook/react-vite/preset";
2
+ export * from "@storybook/react-vite/preset";
3
+ declare const addons: string[];
4
+ declare const viteFinal: (...arguments_: Parameters<typeof viteFinal$1>) => Promise<import("vite").InlineConfig>;
5
+ export { addons, viteFinal };
@@ -0,0 +1,8 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { viteFinal as viteFinal$1 } from "@storybook/react-vite/preset";
5
+ export * from "@storybook/react-vite/preset";
6
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
7
+ const viteFinal = async (...arguments_) => viteFinal$1(await withProjectVitePlugins(arguments_[0]), arguments_[1]);
8
+ export { addons, viteFinal };
@@ -0,0 +1,3 @@
1
+ import { StorybookConfig } from "@storybook/svelte-vite/node";
2
+ declare const framework: string;
3
+ export { type StorybookConfig, framework };
@@ -0,0 +1,4 @@
1
+ import { dirname } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ const framework = dirname(fileURLToPath(import.meta.url));
4
+ export { framework };
@@ -0,0 +1,8 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { viteFinal as viteFinal$1 } from "@storybook/svelte-vite/preset";
5
+ export * from "@storybook/svelte-vite/preset";
6
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
7
+ const viteFinal = async (...arguments_) => viteFinal$1(await withProjectVitePlugins(arguments_[0]), arguments_[1]);
8
+ export { addons, viteFinal };
@@ -0,0 +1,5 @@
1
+ import { viteFinal as viteFinal$1 } from "@storybook/svelte-vite/preset";
2
+ export * from "@storybook/svelte-vite/preset";
3
+ declare const addons: string[];
4
+ declare const viteFinal: (...arguments_: Parameters<typeof viteFinal$1>) => Promise<import("vite").InlineConfig>;
5
+ export { addons, viteFinal };
@@ -0,0 +1,8 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { viteFinal as viteFinal$1 } from "@storybook/svelte-vite/preset";
5
+ export * from "@storybook/svelte-vite/preset";
6
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
7
+ const viteFinal = async (...arguments_) => viteFinal$1(await withProjectVitePlugins(arguments_[0]), arguments_[1]);
8
+ export { addons, viteFinal };
@@ -0,0 +1,2 @@
1
+ export * from "@storybook/addon-vitest/preset";
2
+ export {};
@@ -0,0 +1 @@
1
+ export * from "@storybook/addon-vitest/preset";
@@ -0,0 +1,2 @@
1
+ export * from "@storybook/addon-vitest/preset";
2
+ export {};
@@ -0,0 +1,3 @@
1
+ import { StorybookConfig } from "@storybook/vue3-vite/node";
2
+ declare const framework: string;
3
+ export { type StorybookConfig, framework };
@@ -0,0 +1,4 @@
1
+ import { dirname } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ const framework = dirname(fileURLToPath(import.meta.url));
4
+ export { framework };
@@ -0,0 +1,8 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { viteFinal as viteFinal$1 } from "@storybook/vue3-vite/preset";
5
+ export * from "@storybook/vue3-vite/preset";
6
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
7
+ const viteFinal = async (...arguments_) => viteFinal$1(await withProjectVitePlugins(arguments_[0]), arguments_[1]);
8
+ export { addons, viteFinal };
@@ -0,0 +1,5 @@
1
+ import { viteFinal as viteFinal$1 } from "@storybook/vue3-vite/preset";
2
+ export * from "@storybook/vue3-vite/preset";
3
+ declare const addons: string[];
4
+ declare const viteFinal: (...arguments_: Parameters<typeof viteFinal$1>) => Promise<any>;
5
+ export { addons, viteFinal };
@@ -0,0 +1,8 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { viteFinal as viteFinal$1 } from "@storybook/vue3-vite/preset";
5
+ export * from "@storybook/vue3-vite/preset";
6
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
7
+ const viteFinal = async (...arguments_) => viteFinal$1(await withProjectVitePlugins(arguments_[0]), arguments_[1]);
8
+ export { addons, viteFinal };
@@ -0,0 +1,3 @@
1
+ import { StorybookConfig } from "@storybook/web-components-vite/node";
2
+ declare const framework: string;
3
+ export { type StorybookConfig, framework };
@@ -0,0 +1,4 @@
1
+ import { dirname } from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ const framework = dirname(fileURLToPath(import.meta.url));
4
+ export { framework };
@@ -0,0 +1,7 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ export * from "@storybook/web-components-vite/preset";
5
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
6
+ const viteFinal = withProjectVitePlugins;
7
+ export { addons, viteFinal };
@@ -0,0 +1,4 @@
1
+ export * from "@storybook/web-components-vite/preset";
2
+ declare const addons: string[];
3
+ declare const viteFinal: (config: import("vite").InlineConfig) => Promise<import("vite").InlineConfig>;
4
+ export { addons, viteFinal };
@@ -0,0 +1,7 @@
1
+ import { withProjectVitePlugins } from "../../_chunks/project-vite.mjs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ export * from "@storybook/web-components-vite/preset";
5
+ const addons = [resolve(fileURLToPath(new URL("..", import.meta.url)), "test")];
6
+ const viteFinal = withProjectVitePlugins;
7
+ export { addons, viteFinal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webanvil",
3
- "version": "0.0.7",
3
+ "version": "0.0.9",
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",
@@ -40,6 +40,51 @@
40
40
  "types": "./dist/index.d.mts",
41
41
  "import": "./dist/index.mjs",
42
42
  "default": "./dist/index.mjs"
43
+ },
44
+ "./storybook/test/preset": {
45
+ "types": "./dist/storybook/test/preset.d.mts",
46
+ "import": "./dist/storybook/test/preset.mjs",
47
+ "default": "./dist/storybook/test/preset.mjs"
48
+ },
49
+ "./storybook/react": {
50
+ "types": "./dist/storybook/react/index.d.mts",
51
+ "import": "./dist/storybook/react/index.mjs",
52
+ "default": "./dist/storybook/react/index.mjs"
53
+ },
54
+ "./storybook/react/preset": {
55
+ "types": "./dist/storybook/react/preset.d.mts",
56
+ "import": "./dist/storybook/react/preset.mjs",
57
+ "default": "./dist/storybook/react/preset.mjs"
58
+ },
59
+ "./storybook/svelte": {
60
+ "types": "./dist/storybook/svelte/index.d.mts",
61
+ "import": "./dist/storybook/svelte/index.mjs",
62
+ "default": "./dist/storybook/svelte/index.mjs"
63
+ },
64
+ "./storybook/svelte/preset": {
65
+ "types": "./dist/storybook/svelte/preset.d.mts",
66
+ "import": "./dist/storybook/svelte/preset.mjs",
67
+ "default": "./dist/storybook/svelte/preset.mjs"
68
+ },
69
+ "./storybook/vue": {
70
+ "types": "./dist/storybook/vue/index.d.mts",
71
+ "import": "./dist/storybook/vue/index.mjs",
72
+ "default": "./dist/storybook/vue/index.mjs"
73
+ },
74
+ "./storybook/vue/preset": {
75
+ "types": "./dist/storybook/vue/preset.d.mts",
76
+ "import": "./dist/storybook/vue/preset.mjs",
77
+ "default": "./dist/storybook/vue/preset.mjs"
78
+ },
79
+ "./storybook/web-components": {
80
+ "types": "./dist/storybook/web-components/index.d.mts",
81
+ "import": "./dist/storybook/web-components/index.mjs",
82
+ "default": "./dist/storybook/web-components/index.mjs"
83
+ },
84
+ "./storybook/web-components/preset": {
85
+ "types": "./dist/storybook/web-components/preset.d.mts",
86
+ "import": "./dist/storybook/web-components/preset.mjs",
87
+ "default": "./dist/storybook/web-components/preset.mjs"
43
88
  }
44
89
  },
45
90
  "publishConfig": {
@@ -47,7 +92,7 @@
47
92
  "registry": "https://registry.npmjs.org/"
48
93
  },
49
94
  "scripts": {
50
- "build": "obuild",
95
+ "build": "obuild && node scripts/prepare-storybook-presets.mjs",
51
96
  "prepack": "npm run build",
52
97
  "pretest": "npm run build",
53
98
  "test": "vitest run --passWithNoTests --config vitest.repo.config.ts",
@@ -60,10 +105,18 @@
60
105
  "check": "npm run typecheck && npm run lint && npm run format:check && npm test"
61
106
  },
62
107
  "dependencies": {
108
+ "@playwright/browser-chromium": "1.58.2",
109
+ "@storybook/addon-vitest": "10.5.9",
110
+ "@storybook/react-vite": "10.5.9",
111
+ "@storybook/svelte-vite": "10.5.9",
112
+ "@storybook/vue3-vite": "10.5.9",
113
+ "@storybook/web-components-vite": "10.5.9",
63
114
  "@types/node": "^26.1.1",
64
115
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
65
- "@vitest/coverage-v8": "^4.1.10",
66
- "@vitest/ui": "^4.1.10",
116
+ "@vitest/browser": "4.1.10",
117
+ "@vitest/browser-playwright": "4.1.10",
118
+ "@vitest/coverage-v8": "4.1.10",
119
+ "@vitest/ui": "4.1.10",
67
120
  "c12": "^4.0.0-beta.5",
68
121
  "cmdore": "^0.4.1",
69
122
  "consola": "^3.4.2",
@@ -76,9 +129,11 @@
76
129
  "oxlint": "1.75.0",
77
130
  "pathe": "^2.0.3",
78
131
  "pkg-types": "^2.3.1",
132
+ "playwright": "1.58.2",
79
133
  "rolldown": "1.2.0",
80
134
  "rolldown-plugin-dts": "0.27.13",
81
135
  "semver": "7.8.5",
136
+ "storybook": "10.5.9",
82
137
  "tinyglobby": "^0.2.15",
83
138
  "typescript": "6.0.3",
84
139
  "vite": "8.1.5",