webanvil 0.0.12 → 0.0.15

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
@@ -25,6 +25,7 @@ Table of contents
25
25
  - [A web app](#a-web-app)
26
26
  - [Storybook](#storybook)
27
27
  - [A Node project](#a-node-project)
28
+ - [Browser tests](#browser-tests)
28
29
  - [Configuration](#configuration)
29
30
  - [Tool selection](#tool-selection)
30
31
  - [Native tool configuration](#native-tool-configuration)
@@ -48,6 +49,7 @@ wa clean # remove tracked build output
48
49
  wa check # check formatting, linting, and types
49
50
  wa check --fix # format files and apply safe lint fixes before type checking
50
51
  wa test # run tests, watch them, collect coverage, or open the UI
52
+ wa e2e # build, preview, and run browser tests
51
53
  wa lint # lint files
52
54
  wa format # format files
53
55
  wa typecheck # type-check the project
@@ -64,6 +66,7 @@ What it includes
64
66
  | Tracked output cleanup | `wa clean` | WebAnvil |
65
67
  | Static checks | `wa check` | Oxfmt, Oxlint, TypeScript Native, or svelte-check |
66
68
  | Tests | `wa test` | Vitest |
69
+ | Browser tests | `wa e2e` | Playwright Test and Chromium |
67
70
  | Linting | `wa lint` | Oxlint |
68
71
  | Formatting | `wa format` | Oxfmt |
69
72
  | Type checking | `wa typecheck` | TypeScript Native or svelte-check |
@@ -80,10 +83,10 @@ npm install --save-dev webanvil
80
83
  ```
81
84
 
82
85
  Your package manager remains responsible for dependencies, the lockfile, and
83
- the installed tree. WebAnvil never installs or updates tools. When a supported
84
- tool is declared directly by the active project, or by a workspace that contains
85
- it, WebAnvil uses that installed version. Otherwise it uses the exact version
86
- shipped with WebAnvil.
86
+ the installed tree. WebAnvil never installs or updates tools. Except for the
87
+ bundled Vitest and Playwright test toolchains, WebAnvil uses a compatible tool
88
+ declared directly by the active project or containing workspace before its
89
+ exact fallback.
87
90
 
88
91
  Add the scripts you want to `package.json`:
89
92
 
@@ -95,6 +98,7 @@ Add the scripts you want to `package.json`:
95
98
  "clean": "wa clean",
96
99
  "check": "wa check",
97
100
  "test": "wa test",
101
+ "e2e": "wa e2e",
98
102
  "lint": "wa lint",
99
103
  "format": "wa format",
100
104
  "typecheck": "wa typecheck"
@@ -114,6 +118,7 @@ npm run build
114
118
  npm run clean
115
119
  npm run check
116
120
  npm run test
121
+ npm run e2e
117
122
  npm run lint
118
123
  npm run format
119
124
  npm run typecheck
@@ -198,8 +203,8 @@ package build still owns `--out-dir`.
198
203
  Set `storybook.test: false` to exclude Storybook stories, including `play`
199
204
  functions, from `wa test`. Chromium is downloaded by
200
205
  `@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.
206
+ Storybook tests use WebAnvil's bundled Vitest and browser provider as one
207
+ version-matched toolchain.
203
208
 
204
209
  ### A Node project
205
210
 
@@ -330,21 +335,23 @@ over the matching WebAnvil block.
330
335
  ### Tool selection
331
336
 
332
337
  WebAnvil selects compatible project and workspace declarations before its own
333
- fallbacks. A transitive or merely hoisted package is not selected. Each command
338
+ fallbacks, except for its bundled Vitest and Playwright test toolchains. A
339
+ transitive or merely hoisted package is not selected. Each command
334
340
  preflights the engines it can dispatch before `webanvil.config.*` is loaded or
335
341
  its plugins are evaluated, so a declared command engine that is missing, has
336
342
  invalid package identity, or is outside the supported range fails first.
337
343
 
338
- | Tool | Supported project/workspace versions | Exact WebAnvil fallback |
339
- | -------------------------- | ------------------------------------ | ----------------------- |
340
- | Vite | `>=8.1.5 <9` | `8.1.5` |
341
- | Vitest | `>=4.1.10 <5` | `4.1.10` |
342
- | Storybook | `>=10.5.9 <11` | `10.5.9` |
343
- | Rolldown | `>=1.2.0 <2` | `1.2.0` |
344
- | Oxlint | `>=1.75.0 <2` | `1.75.0` |
345
- | Oxfmt | `>=0.60.0 <0.61` | `0.60.0` |
346
- | TypeScript (declarations) | `>=5 <7` | `6.0.3` |
347
- | TypeScript Native (`tsgo`) | `>=7.0.0-dev.20260707.2 <7.0.0` | `7.0.0-dev.20260707.2` |
344
+ | Tool | Supported project/workspace versions | Exact WebAnvil fallback |
345
+ | ---------------------------- | ------------------------------------ | ----------------------- |
346
+ | Vite | `>=8.1.5 <9` | `8.1.5` |
347
+ | Vitest | Bundled only | `4.1.11` |
348
+ | Playwright Test and Chromium | Bundled only | `1.58.2` |
349
+ | Storybook | `>=10.5.9 <11` | `10.5.9` |
350
+ | Rolldown | `>=1.2.0 <2` | `1.2.0` |
351
+ | Oxlint | `>=1.75.0 <2` | `1.75.0` |
352
+ | Oxfmt | `>=0.60.0 <0.61` | `0.60.0` |
353
+ | TypeScript (declarations) | `>=5 <7` | `6.0.3` |
354
+ | TypeScript Native (`tsgo`) | `>=7.0.0-dev.20260707.2 <7.0.0` | `7.0.0-dev.20260707.2` |
348
355
 
349
356
  When a tool is first used, the CLI reports its package, version, and source, for
350
357
  example `Using rolldown 1.2.0 (project)` or
@@ -354,6 +361,10 @@ The TypeScript compiler is selected only after configuration enables a
354
361
  declaration build, but before Rolldown starts that build. It follows the same
355
362
  direct project/workspace declaration and exact-fallback rules.
356
363
 
364
+ `wa test` and `wa e2e` always use WebAnvil's bundled Vitest and Playwright
365
+ toolchains. This keeps each runner on the same version as its `webanvil/test` or
366
+ `webanvil/e2e` API.
367
+
357
368
  ### Native tool configuration
358
369
 
359
370
  The `vite`, `test`, `rolldown`, `lint`, and `format` blocks use the owning
@@ -369,6 +380,10 @@ Precedence is:
369
380
  3. the matching native block in `webanvil.config.*`;
370
381
  4. WebAnvil defaults.
371
382
 
383
+ A `playwright.config.*` is different: it takes full control of `wa e2e`,
384
+ including the server lifecycle and browser configuration. WebAnvil still runs
385
+ its bundled Playwright Test binary.
386
+
372
387
  WebAnvil-owned `build`, `copy`, cross-engine `plugins`, and CLI behavior remain
373
388
  orchestration settings. `rolldown.input` and per-format `rolldown.output`
374
389
  options extend Node builds; WebAnvil still owns the input roots, output
@@ -494,6 +509,56 @@ These are run-specific modes; keep persistent Vitest configuration in
494
509
  `vitest.config.*`. `--ui-port` selects a strict loopback port and requires
495
510
  `--ui`.
496
511
 
512
+ Import the bundled test API from WebAnvil so test registration and execution
513
+ always use the same Vitest instance:
514
+
515
+ ```ts
516
+ import { context, describe, expect, it } from "webanvil/test"
517
+
518
+ describe("calculator", () => {
519
+ context("with two values", () => {
520
+ it("adds them", () => {
521
+ expect(1 + 1).toBe(2)
522
+ })
523
+ })
524
+ })
525
+ ```
526
+
527
+ Native Vitest configuration can import `defineConfig` from
528
+ `webanvil/test/config`.
529
+
530
+ ### Browser tests
531
+
532
+ `wa e2e` builds a web project, starts a production preview, and runs Playwright
533
+ Test files in `e2e/`. Import the test API from WebAnvil so the runner and test
534
+ code use the bundled matching version:
535
+
536
+ ```ts
537
+ import { context, describe, expect, it } from "webanvil/e2e"
538
+
539
+ describe("home page", () => {
540
+ context("when a visitor opens it", () => {
541
+ it("shows its heading", async ({ page }) => {
542
+ await page.goto("/")
543
+ await expect(page.getByRole("heading")).toBeVisible()
544
+ })
545
+ })
546
+ })
547
+ ```
548
+
549
+ The generated configuration provides a `chromium` project, so `wa e2e --project
550
+ chromium` works without a Playwright config. Use `--headed`, `--debug`, or `--ui`
551
+ for an interactive run. WebAnvil ships Playwright Test and Chromium, but the host
552
+ still needs the browser system libraries. On Linux CI, install them with:
553
+
554
+ ```sh
555
+ npx playwright install --with-deps chromium
556
+ ```
557
+
558
+ For advanced configuration, add `playwright.config.*`. WebAnvil then delegates
559
+ directly to Playwright and does not build or start a server. Configure
560
+ Playwright's `webServer` option in that file when the tests need one.
561
+
497
562
  ### Cleaning build output
498
563
 
499
564
  `wa build` records the actual emitted and copied files in
@@ -543,6 +608,7 @@ Command reference
543
608
  | `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
609
  | `wa preview` | Serves a Vite production build or configured static Storybook output. | `--out-dir`, `--host`, `--port`, `--open` |
545
610
  | `wa test [filters...]` | Runs Vitest once, in watch mode, with coverage, or UI. | `--environment`, `--watch`, `--coverage`, `--ui`, `--ui-port` |
611
+ | `wa e2e [filters...]` | Builds, previews, and runs Playwright browser tests. Native Playwright configuration takes control. | `--host`, `--port`, `--ui`, `--headed`, `--debug`, `--project` |
546
612
  | `wa lint [paths...]` | Runs Oxlint and treats warnings as failures. | `--fix` |
547
613
  | `wa format [paths...]` | Formats with Oxfmt. | `--check` |
548
614
  | `wa typecheck [paths...]` | Type-checks with TypeScript Native. | No options |
@@ -17,6 +17,7 @@ import { playwright } from "@vitest/browser-playwright";
17
17
  import { storybookTest } from "@storybook/addon-vitest/vitest-plugin";
18
18
  import { consola } from "consola";
19
19
  import { randomUUID } from "node:crypto";
20
+ import { tmpdir } from "node:os";
20
21
  const entry = defineArgument({
21
22
  name: "entry",
22
23
  description: "Web entry or Node public root; unbundled Node builds emit its reachable graph with preserveModules."
@@ -101,7 +102,14 @@ const supportedTools = {
101
102
  },
102
103
  vitest: {
103
104
  packageName: "vitest",
104
- range: ">=4.1.10 <5"
105
+ range: ">=4.1.11 <5",
106
+ allowProjectOverride: false
107
+ },
108
+ playwright: {
109
+ packageName: "@playwright/test",
110
+ range: ">=1.58.2 <2",
111
+ bin: "playwright",
112
+ allowProjectOverride: false
105
113
  },
106
114
  storybook: {
107
115
  packageName: "storybook",
@@ -343,7 +351,7 @@ var Toolchain = class {
343
351
  }
344
352
  async #resolve(name) {
345
353
  const definition = supportedTools[name];
346
- const declaration = await findDeclaration(this.cwd, definition.packageName);
354
+ const declaration = definition.allowProjectOverride === false ? void 0 : await findDeclaration(this.cwd, definition.packageName);
347
355
  if (declaration !== void 0) return loadResolvedTool(name, definition, declaration.directory, "project");
348
356
  const webanvilPackageRoot = dirname$1(await findContainingManifest(fileURLToPath(import.meta.url)));
349
357
  return loadResolvedTool(name, definition, webanvilPackageRoot, "webanvil");
@@ -1114,7 +1122,7 @@ const useToolExecutable = async (name, toolchain = new Toolchain(process.cwd()))
1114
1122
  if (tool.executable === void 0) throw new Error(`${tool.packageName} does not expose an executable`);
1115
1123
  return tool.executable;
1116
1124
  };
1117
- const storybookVitestVersion = "4.1.10";
1125
+ const storybookVitestVersion = "4.1.11";
1118
1126
  const relativeModule = (from, to) => {
1119
1127
  const path = relative(from, to).replaceAll("\\", "/");
1120
1128
  return path.startsWith(".") ? path : `./${path}`;
@@ -1255,6 +1263,11 @@ const declaration = defineOption({
1255
1263
  arity: 1,
1256
1264
  schema: z.enum(["true", "false"]).transform((value) => value === "true")
1257
1265
  });
1266
+ const debug = defineOption({
1267
+ name: "debug",
1268
+ description: "Run browser tests in Playwright debug mode.",
1269
+ arity: 0
1270
+ });
1258
1271
  const environment = defineOption({
1259
1272
  name: "environment",
1260
1273
  description: "Vitest environment to use for this run.",
@@ -1276,6 +1289,11 @@ const host = defineOption({
1276
1289
  description: "Host interface for the web server.",
1277
1290
  arity: 1
1278
1291
  });
1292
+ const headed = defineOption({
1293
+ name: "headed",
1294
+ description: "Run browser tests with a visible browser window.",
1295
+ arity: 0
1296
+ });
1279
1297
  const minify = defineOption({
1280
1298
  name: "minify",
1281
1299
  description: "Minify the build output: true or false.",
@@ -1308,6 +1326,12 @@ const platform = defineOption({
1308
1326
  "neutral"
1309
1327
  ])
1310
1328
  });
1329
+ const project = defineOption({
1330
+ name: "project",
1331
+ description: "Run one named Playwright project.",
1332
+ arity: 1,
1333
+ schema: z.string().min(1)
1334
+ });
1311
1335
  const port = defineOption({
1312
1336
  name: "port",
1313
1337
  description: "Port for the web server.",
@@ -1331,7 +1355,7 @@ const target = defineOption({
1331
1355
  });
1332
1356
  const ui = defineOption({
1333
1357
  name: "ui",
1334
- description: "Start the Vitest user interface.",
1358
+ description: "Start the test user interface.",
1335
1359
  arity: 0
1336
1360
  });
1337
1361
  const uiPort = defineOption({
@@ -1362,7 +1386,7 @@ const build = async (mode, entry, outDir, options = {}, plugins = [], viteConfig
1362
1386
  const rolldown = await useToolApi("rolldown", void 0, toolchain);
1363
1387
  await runNodeBuild(await createNodeBuildPlan(entry, outDir, options, resolveRolldownPlugins(plugins), rolldownConfig, toolchain), rolldown.rolldown);
1364
1388
  logger.success(`Built ${entry} to ${outDir}`);
1365
- return;
1389
+ return outDir;
1366
1390
  }
1367
1391
  const web = await build.webConfig(entry, outDir, options, plugins, viteConfig, toolchain, explicit);
1368
1392
  const target = web.outDir;
@@ -1379,6 +1403,7 @@ const build = async (mode, entry, outDir, options = {}, plugins = [], viteConfig
1379
1403
  ...copied
1380
1404
  ]);
1381
1405
  logger.success(`Built ${entry} to ${outDir}`);
1406
+ return target;
1382
1407
  };
1383
1408
  build.webConfig = async (entry, outDir, options, plugins, viteConfig = {}, toolchain = new Toolchain(process.cwd()), explicit = {}) => {
1384
1409
  assertSyntaxTarget(options.target);
@@ -1831,7 +1856,7 @@ var dev_default = defineCommand({
1831
1856
  return commandRun(toolchain)(arguments_, config);
1832
1857
  }
1833
1858
  });
1834
- const preview = async (outDir, host, port, useOutDir = false, waitForTermination = untilTerminated, openBrowser, viteConfig = {}) => {
1859
+ const startPreview = async (outDir, host, port, useOutDir = false, openBrowser, viteConfig = {}) => {
1835
1860
  logger.start("Starting web preview");
1836
1861
  const vite = await useToolApi("vite");
1837
1862
  const hasViteConfig = await hasToolConfig("vite");
@@ -1848,7 +1873,10 @@ const preview = async (outDir, host, port, useOutDir = false, waitForTermination
1848
1873
  ...openBrowser === void 0 ? {} : { open: openBrowser }
1849
1874
  }
1850
1875
  });
1851
- const server = await vite.preview(config);
1876
+ return vite.preview(config);
1877
+ };
1878
+ const preview = async (outDir, host, port, useOutDir = false, waitForTermination = untilTerminated, openBrowser, viteConfig = {}) => {
1879
+ const server = await startPreview(outDir, host, port, useOutDir, openBrowser, viteConfig);
1852
1880
  try {
1853
1881
  server.printUrls();
1854
1882
  await waitForTermination();
@@ -1872,6 +1900,94 @@ var preview_default = defineCommand({
1872
1900
  return preview(outDir ?? (storybook === void 0 ? config.build?.outDir ?? "dist" : storybookOutputDir(storybook)), host, port, outDir !== void 0 || storybook !== void 0, untilTerminated, open, config.vite);
1873
1901
  }
1874
1902
  });
1903
+ const localUrl = (server) => {
1904
+ const url = server.resolvedUrls?.local?.[0] ?? server.resolvedUrls?.network?.[0];
1905
+ if (url === void 0) throw new Error("WebAnvil could not determine the preview URL for browser tests");
1906
+ return url;
1907
+ };
1908
+ const argumentsFor = (filters, options, config, passWithNoTests = false) => [
1909
+ "test",
1910
+ ...config === void 0 ? [] : ["--config", config],
1911
+ ...filters,
1912
+ ...passWithNoTests ? ["--pass-with-no-tests"] : [],
1913
+ ...options.ui ? ["--ui"] : [],
1914
+ ...options.headed ? ["--headed"] : [],
1915
+ ...options.debug ? ["--debug"] : [],
1916
+ ...options.project === void 0 ? [] : ["--project", options.project]
1917
+ ];
1918
+ const defaultE2EConfig = (testDir, baseURL, outputDir) => ({
1919
+ outputDir,
1920
+ projects: [{
1921
+ name: "chromium",
1922
+ use: { browserName: "chromium" }
1923
+ }],
1924
+ testDir,
1925
+ use: { baseURL }
1926
+ });
1927
+ const defaultConfig = (testDir, baseURL, outputDir) => `${JSON.stringify(defaultE2EConfig(testDir, baseURL, outputDir), void 0, 4)}\n`;
1928
+ const runPlaywright = async (filters, options, config, baseURL, passWithNoTests = false) => {
1929
+ await execa(await useToolExecutable("playwright"), argumentsFor(filters, options, config, passWithNoTests), {
1930
+ cwd: process.cwd(),
1931
+ env: {
1932
+ ...process.env,
1933
+ ...baseURL === void 0 ? {} : { WEBANVIL_E2E_URL: baseURL },
1934
+ PLAYWRIGHT_TEST: "1"
1935
+ },
1936
+ stdio: "inherit"
1937
+ });
1938
+ };
1939
+ const e2e = async (filters, options = {}) => {
1940
+ logger.start("Running end-to-end tests");
1941
+ if (await hasToolConfig("playwright")) {
1942
+ await runPlaywright(filters, options);
1943
+ logger.success("End-to-end tests passed");
1944
+ return;
1945
+ }
1946
+ const { config } = await loadConfig();
1947
+ const effective = resolveEffectiveBuildConfig(config, {}, false);
1948
+ if (effective.mode !== "web") throw new Error("wa e2e requires build.mode: \"web\" or a native playwright.config.* file");
1949
+ const toolchain = new Toolchain(process.cwd());
1950
+ const server = await startPreview(await build(effective.mode, effective.entry, effective.outDir, effective, config.plugins ?? [], config.vite, config.rolldown, toolchain), options.host, options.port, true, false, config.vite);
1951
+ let directory;
1952
+ try {
1953
+ const baseURL = localUrl(server);
1954
+ directory = await mkdtemp(join$1(tmpdir(), "webanvil-playwright-"));
1955
+ const configPath = join$1(directory, "playwright.config.mjs");
1956
+ await writeFile(configPath, `export default ${defaultConfig(resolve$1(process.cwd(), "e2e"), baseURL, join$1(directory, "test-results"))}`);
1957
+ await runPlaywright(filters, options, configPath, baseURL, true);
1958
+ } finally {
1959
+ await Promise.all([server.close(), ...directory === void 0 ? [] : [rm(directory, {
1960
+ force: true,
1961
+ recursive: true
1962
+ })]]);
1963
+ }
1964
+ logger.success("End-to-end tests passed");
1965
+ };
1966
+ var e2e_default = defineCommand({
1967
+ name: "e2e",
1968
+ description: "Run end-to-end browser tests.",
1969
+ arguments: [filters],
1970
+ options: [
1971
+ host,
1972
+ port,
1973
+ ui,
1974
+ headed,
1975
+ debug,
1976
+ project
1977
+ ],
1978
+ run: async ({ filters, host, port, ui, headed, debug, project }) => {
1979
+ const nativePlaywrightConfig = await hasToolConfig("playwright");
1980
+ await Promise.all([useTool("playwright"), ...nativePlaywrightConfig ? [] : [useTool("vite")]]);
1981
+ return e2e(filters, {
1982
+ debug,
1983
+ headed,
1984
+ host,
1985
+ port,
1986
+ project,
1987
+ ui
1988
+ });
1989
+ }
1990
+ });
1875
1991
  const test = async (filters, config = {}, options = {}, waitForTermination = untilTerminated, storybook = {}) => {
1876
1992
  if (options.uiPort !== void 0 && options.ui !== true) throw new Error("--ui-port requires --ui");
1877
1993
  logger.start("Running tests");
@@ -1956,4 +2072,4 @@ var test_default = defineCommand({
1956
2072
  return runTest(arguments_);
1957
2073
  }
1958
2074
  });
1959
- 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 };
2075
+ export { build, build_default, bundle, check, checkProject, check_default, clean, clean_default, copy, coverage, debug, declaration, dev, dev_default, e2e, e2e_default, entry, environment, filters, fix$1 as fix, format, format_default, formats, headed, host, lint, lint_default, logger, minify, mode, open, outDir, paths, platform, port, preview, preview_default, project, sourcemap, target, test, test_default, typecheck, typecheck_default, ui, uiPort, watch };
package/dist/cli.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { build_default, check_default, clean_default, dev_default, format_default, lint_default, logger, preview_default, test_default, typecheck_default } from "./_chunks/commands.mjs";
1
+ import { build_default, check_default, clean_default, dev_default, e2e_default, format_default, lint_default, logger, preview_default, test_default, typecheck_default } from "./_chunks/commands.mjs";
2
2
  import { execute } from "cmdore";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { readPackageJSON } from "pkg-types";
@@ -9,6 +9,7 @@ const main = async (...varargs) => {
9
9
  check_default,
10
10
  clean_default,
11
11
  dev_default,
12
+ e2e_default,
12
13
  format_default,
13
14
  lint_default,
14
15
  preview_default,
package/dist/e2e.d.mts ADDED
@@ -0,0 +1,73 @@
1
+ import { defineConfig, devices, expect, test } from "@playwright/test";
2
+ declare const describe: {
3
+ (title: string, callback: () => void): void;
4
+ (callback: () => void): void;
5
+ (title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
6
+ only(title: string, callback: () => void): void;
7
+ only(callback: () => void): void;
8
+ only(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
9
+ skip(title: string, callback: () => void): void;
10
+ skip(callback: () => void): void;
11
+ skip(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
12
+ fixme(title: string, callback: () => void): void;
13
+ fixme(callback: () => void): void;
14
+ fixme(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
15
+ serial: {
16
+ (title: string, callback: () => void): void;
17
+ (callback: () => void): void;
18
+ (title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
19
+ only(title: string, callback: () => void): void;
20
+ only(callback: () => void): void;
21
+ only(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
22
+ };
23
+ parallel: {
24
+ (title: string, callback: () => void): void;
25
+ (callback: () => void): void;
26
+ (title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
27
+ only(title: string, callback: () => void): void;
28
+ only(callback: () => void): void;
29
+ only(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
30
+ };
31
+ configure: (options: {
32
+ mode?: "default" | "parallel" | "serial";
33
+ retries?: number;
34
+ timeout?: number;
35
+ }) => void;
36
+ };
37
+ declare const context: {
38
+ (title: string, callback: () => void): void;
39
+ (callback: () => void): void;
40
+ (title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
41
+ only(title: string, callback: () => void): void;
42
+ only(callback: () => void): void;
43
+ only(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
44
+ skip(title: string, callback: () => void): void;
45
+ skip(callback: () => void): void;
46
+ skip(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
47
+ fixme(title: string, callback: () => void): void;
48
+ fixme(callback: () => void): void;
49
+ fixme(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
50
+ serial: {
51
+ (title: string, callback: () => void): void;
52
+ (callback: () => void): void;
53
+ (title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
54
+ only(title: string, callback: () => void): void;
55
+ only(callback: () => void): void;
56
+ only(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
57
+ };
58
+ parallel: {
59
+ (title: string, callback: () => void): void;
60
+ (callback: () => void): void;
61
+ (title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
62
+ only(title: string, callback: () => void): void;
63
+ only(callback: () => void): void;
64
+ only(title: string, details: import("playwright/test").TestDetails, callback: () => void): void;
65
+ };
66
+ configure: (options: {
67
+ mode?: "default" | "parallel" | "serial";
68
+ retries?: number;
69
+ timeout?: number;
70
+ }) => void;
71
+ };
72
+ declare const it: import("playwright/test").TestType<import("playwright/test").PlaywrightTestArgs & import("playwright/test").PlaywrightTestOptions, import("playwright/test").PlaywrightWorkerArgs & import("playwright/test").PlaywrightWorkerOptions>;
73
+ export { context, defineConfig, describe, devices, expect, it, test };
package/dist/e2e.mjs ADDED
@@ -0,0 +1,5 @@
1
+ import { defineConfig, devices, expect, test, test as test$1 } from "@playwright/test";
2
+ const describe = test$1.describe;
3
+ const context = test$1.describe;
4
+ const it = test$1;
5
+ export { context, defineConfig, describe, devices, expect, it, test };
package/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
1
  import { rename } from "node:fs/promises";
2
2
  import { z } from "zod";
3
3
  import { TsconfigJson } from "get-tsconfig";
4
+ import { TestUserConfig } from "vitest/config";
4
5
  import { InlineConfig, PluginOption, UserConfig as UserConfig$1 } from "vite";
5
6
  import { OxfmtConfig } from "oxfmt";
6
7
  import { OxlintConfig } from "oxlint";
7
8
  import { InputOptions, OutputOptions, Plugin } from "rolldown";
8
- import { TestUserConfig } from "vitest/config";
9
9
  import { IsolatedDeclarationsOptions } from "rolldown/experimental";
10
10
  declare const entry$1: {
11
11
  readonly name: "entry";
@@ -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" | "storybook" | "typescript" | "typescript-native";
38
+ type ToolName = "vite" | "vitest" | "playwright" | "rolldown" | "oxlint" | "oxfmt" | "storybook" | "typescript" | "typescript-native";
39
39
  type OptionalToolName = "svelte-check";
40
40
  type AnyToolName = ToolName | OptionalToolName;
41
41
  type ToolSource = "project" | "webanvil";
@@ -293,7 +293,7 @@ type WebBuild = {
293
293
  vite: ViteApi;
294
294
  };
295
295
  declare const build: {
296
- (mode: "web" | "node", entry: string, outDir: string, options?: BuildOptions, plugins?: WebAnvilPlugin[], viteConfig?: UserConfig$1, rolldownConfig?: RolldownConfig, toolchain?: Toolchain, explicit?: ExplicitWebBuild): Promise<void>;
296
+ (mode: "web" | "node", entry: string, outDir: string, options?: BuildOptions, plugins?: WebAnvilPlugin[], viteConfig?: UserConfig$1, rolldownConfig?: RolldownConfig, toolchain?: Toolchain, explicit?: ExplicitWebBuild): Promise<string>;
297
297
  webConfig(entry: string, outDir: string, options: BuildOptions, plugins: WebAnvilPlugin[], viteConfig?: UserConfig$1, toolchain?: Toolchain, explicit?: ExplicitWebBuild): Promise<WebBuild>;
298
298
  publicOutputFiles({ outDir, publicDir }: WebBuild): Promise<string[]>;
299
299
  web(web: WebBuild): Promise<string[]>;
@@ -379,7 +379,7 @@ type TypecheckOptions = {
379
379
  svelteCheck: ResolvedTool | undefined;
380
380
  };
381
381
  declare const typecheck: (paths: string[], options?: TypecheckOptions) => Promise<void>;
382
- declare const _default$8: import("cmdore").Command<readonly import("cmdore").Option[], readonly [{
382
+ declare const _default$9: import("cmdore").Command<readonly import("cmdore").Option[], readonly [{
383
383
  readonly name: "paths";
384
384
  readonly description: "Files or directories to check.";
385
385
  readonly variadic: true;
@@ -484,8 +484,48 @@ declare const _default$3: import("cmdore").Command<readonly [{
484
484
  readonly name: "entry";
485
485
  readonly description: "Web entry or Node public root; unbundled Node builds emit its reachable graph with preserveModules.";
486
486
  }]>;
487
- declare const format: (paths: string[], check?: boolean, config?: FormatConfig) => Promise<void>;
487
+ type E2EOptions = {
488
+ debug?: boolean;
489
+ headed?: boolean;
490
+ host?: string;
491
+ port?: number;
492
+ project?: string;
493
+ ui?: boolean;
494
+ };
495
+ declare const e2e: (filters: string[], options?: E2EOptions) => Promise<void>;
488
496
  declare const _default$4: import("cmdore").Command<readonly [{
497
+ readonly name: "host";
498
+ readonly description: "Host interface for the web server.";
499
+ readonly arity: 1;
500
+ }, {
501
+ readonly name: "port";
502
+ readonly description: "Port for the web server.";
503
+ readonly arity: 1;
504
+ readonly schema: import("zod").ZodCoercedNumber<unknown>;
505
+ }, {
506
+ readonly name: "ui";
507
+ readonly description: "Start the test user interface.";
508
+ readonly arity: 0;
509
+ }, {
510
+ readonly name: "headed";
511
+ readonly description: "Run browser tests with a visible browser window.";
512
+ readonly arity: 0;
513
+ }, {
514
+ readonly name: "debug";
515
+ readonly description: "Run browser tests in Playwright debug mode.";
516
+ readonly arity: 0;
517
+ }, {
518
+ readonly name: "project";
519
+ readonly description: "Run one named Playwright project.";
520
+ readonly arity: 1;
521
+ readonly schema: import("zod").ZodString;
522
+ }], readonly [{
523
+ readonly name: "filters";
524
+ readonly description: "Test files or names to run.";
525
+ readonly variadic: true;
526
+ }]>;
527
+ declare const format: (paths: string[], check?: boolean, config?: FormatConfig) => Promise<void>;
528
+ declare const _default$5: import("cmdore").Command<readonly [{
489
529
  readonly name: "check";
490
530
  readonly description: "Check formatting without writing files.";
491
531
  readonly arity: 0;
@@ -495,7 +535,7 @@ declare const _default$4: import("cmdore").Command<readonly [{
495
535
  readonly variadic: true;
496
536
  }]>;
497
537
  declare const lint: (paths: string[], fix?: boolean, config?: LintConfig) => Promise<void>;
498
- declare const _default$5: import("cmdore").Command<readonly [{
538
+ declare const _default$6: import("cmdore").Command<readonly [{
499
539
  readonly name: "fix";
500
540
  readonly description: "Apply safe lint fixes.";
501
541
  readonly arity: 0;
@@ -505,7 +545,7 @@ declare const _default$5: import("cmdore").Command<readonly [{
505
545
  readonly variadic: true;
506
546
  }]>;
507
547
  declare const preview: (outDir: string, host?: string, port?: number, useOutDir?: boolean, waitForTermination?: () => Promise<void>, openBrowser?: boolean, viteConfig?: UserConfig$1) => Promise<void>;
508
- declare const _default$6: import("cmdore").Command<readonly [{
548
+ declare const _default$7: import("cmdore").Command<readonly [{
509
549
  readonly name: "out-dir";
510
550
  readonly description: "Directory where the build output is written.";
511
551
  readonly arity: 1;
@@ -530,7 +570,7 @@ declare const test: (filters: string[], config?: TestConfig, options?: {
530
570
  uiPort?: number;
531
571
  watch?: boolean;
532
572
  }, waitForTermination?: () => Promise<void>, storybook?: StorybookConfig) => Promise<void>;
533
- declare const _default$7: import("cmdore").Command<readonly [{
573
+ declare const _default$8: import("cmdore").Command<readonly [{
534
574
  readonly name: "environment";
535
575
  readonly description: "Vitest environment to use for this run.";
536
576
  readonly arity: 1;
@@ -544,7 +584,7 @@ declare const _default$7: import("cmdore").Command<readonly [{
544
584
  readonly arity: 0;
545
585
  }, {
546
586
  readonly name: "ui";
547
- readonly description: "Start the Vitest user interface.";
587
+ readonly description: "Start the test user interface.";
548
588
  readonly arity: 0;
549
589
  }, {
550
590
  readonly name: "ui-port";
@@ -588,6 +628,11 @@ declare const declaration: {
588
628
  false: "false";
589
629
  }>, z.ZodTransform<boolean, "true" | "false">>;
590
630
  };
631
+ declare const debug: {
632
+ readonly name: "debug";
633
+ readonly description: "Run browser tests in Playwright debug mode.";
634
+ readonly arity: 0;
635
+ };
591
636
  declare const environment: {
592
637
  readonly name: "environment";
593
638
  readonly description: "Vitest environment to use for this run.";
@@ -612,6 +657,11 @@ declare const host: {
612
657
  readonly description: "Host interface for the web server.";
613
658
  readonly arity: 1;
614
659
  };
660
+ declare const headed: {
661
+ readonly name: "headed";
662
+ readonly description: "Run browser tests with a visible browser window.";
663
+ readonly arity: 0;
664
+ };
615
665
  declare const minify$1: {
616
666
  readonly name: "minify";
617
667
  readonly description: "Minify the build output: true or false.";
@@ -650,6 +700,12 @@ declare const platform$1: {
650
700
  neutral: "neutral";
651
701
  }>;
652
702
  };
703
+ declare const project: {
704
+ readonly name: "project";
705
+ readonly description: "Run one named Playwright project.";
706
+ readonly arity: 1;
707
+ readonly schema: z.ZodString;
708
+ };
653
709
  declare const port: {
654
710
  readonly name: "port";
655
711
  readonly description: "Port for the web server.";
@@ -673,7 +729,7 @@ declare const target$1: {
673
729
  };
674
730
  declare const ui: {
675
731
  readonly name: "ui";
676
- readonly description: "Start the Vitest user interface.";
732
+ readonly description: "Start the test user interface.";
677
733
  readonly arity: 0;
678
734
  };
679
735
  declare const uiPort: {
@@ -689,4 +745,4 @@ declare const watch: {
689
745
  };
690
746
  declare function defineConfig(config: UserConfig): UserConfig;
691
747
  declare function defineConfig(config: UserConfigFactory): UserConfigFactory;
692
- 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 };
748
+ 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, debug, declaration, defaultConfig, defineConfig, definePlugin, dev, _default$3 as devCommand, e2e, _default$4 as e2eCommand, effectiveUserConfigSchema, entry$1 as entry, environment, filters, fix, format, _default$5 as formatCommand, formatConfigSchema, formats$1 as formats, headed, host, isUnpluginAdapter, isWebAnvilPlugin, lint, _default$6 as lintCommand, lintConfigSchema, loadConfig, minify$1 as minify, mode, open, outDir$1 as outDir, paths, platform$1 as platform, port, preview, _default$7 as previewCommand, project, resolveEffectiveBuildConfig, resolveRolldownPlugins, resolveVitePlugins, rolldownConfigSchema, sourcemap$1 as sourcemap, storybookConfigSchema, syntaxTargetSchema, target$1 as target, test, _default$8 as testCommand, testConfigSchema, typecheck, _default$9 as typecheckCommand, ui, uiPort, userConfigSchema, viteConfigSchema, watch, withConfig };
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
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";
1
+ import { build, build_default, bundle, check, checkProject, check_default, clean, clean_default, copy, coverage, debug, declaration, dev, dev_default, e2e, e2e_default, entry, environment, filters, fix, format, format_default, formats, headed, host, lint, lint_default, minify, mode, open, outDir, paths, platform, port, preview, preview_default, project, sourcemap, target, test, test_default, typecheck, typecheck_default, ui, uiPort, watch } from "./_chunks/commands.mjs";
2
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";
3
3
  function defineConfig(config) {
4
4
  return defineConfig$1(config);
5
5
  }
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 };
6
+ export { assertSyntaxTarget, build, build_default as buildCommand, buildConfigSchema, bundle, check, check_default as checkCommand, checkProject, clean, clean_default as cleanCommand, copy, copyMappingSchema, coverage, debug, declaration, defaultConfig, defineConfig, definePlugin, dev, dev_default as devCommand, e2e, e2e_default as e2eCommand, effectiveUserConfigSchema, entry, environment, filters, fix, format, format_default as formatCommand, formatConfigSchema, formats, headed, host, isUnpluginAdapter, isWebAnvilPlugin, lint, lint_default as lintCommand, lintConfigSchema, loadConfig, minify, mode, open, outDir, paths, platform, port, preview, preview_default as previewCommand, project, 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 @@
1
+ export * from "vitest/config";
@@ -0,0 +1,2 @@
1
+ export * from "vitest/config";
2
+ export {};
@@ -0,0 +1,3 @@
1
+ export * from "vitest";
2
+ declare const context: import("vitest").SuiteAPI;
3
+ export { context };
package/dist/test.mjs ADDED
@@ -0,0 +1,4 @@
1
+ import { describe } from "vitest";
2
+ export * from "vitest";
3
+ const context = describe;
4
+ export { context };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webanvil",
3
- "version": "0.0.12",
3
+ "version": "0.0.15",
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",
@@ -41,6 +41,21 @@
41
41
  "import": "./dist/index.mjs",
42
42
  "default": "./dist/index.mjs"
43
43
  },
44
+ "./e2e": {
45
+ "types": "./dist/e2e.d.mts",
46
+ "import": "./dist/e2e.mjs",
47
+ "default": "./dist/e2e.mjs"
48
+ },
49
+ "./test": {
50
+ "types": "./dist/test.d.mts",
51
+ "import": "./dist/test.mjs",
52
+ "default": "./dist/test.mjs"
53
+ },
54
+ "./test/config": {
55
+ "types": "./dist/test/config.d.mts",
56
+ "import": "./dist/test/config.mjs",
57
+ "default": "./dist/test/config.mjs"
58
+ },
44
59
  "./storybook/test/preset": {
45
60
  "types": "./dist/storybook/test/preset.d.mts",
46
61
  "import": "./dist/storybook/test/preset.mjs",
@@ -106,6 +121,7 @@
106
121
  },
107
122
  "dependencies": {
108
123
  "@playwright/browser-chromium": "1.58.2",
124
+ "@playwright/test": "1.58.2",
109
125
  "@storybook/addon-vitest": "10.5.9",
110
126
  "@storybook/react-vite": "10.5.9",
111
127
  "@storybook/svelte-vite": "10.5.9",
@@ -113,10 +129,10 @@
113
129
  "@storybook/web-components-vite": "10.5.9",
114
130
  "@types/node": "^26.1.1",
115
131
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
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",
132
+ "@vitest/browser": "4.1.11",
133
+ "@vitest/browser-playwright": "4.1.11",
134
+ "@vitest/coverage-v8": "4.1.11",
135
+ "@vitest/ui": "4.1.11",
120
136
  "c12": "^4.0.0-beta.5",
121
137
  "cmdore": "^0.4.1",
122
138
  "consola": "^3.4.2",
@@ -137,7 +153,7 @@
137
153
  "tinyglobby": "^0.2.15",
138
154
  "typescript": "6.0.3",
139
155
  "vite": "8.1.5",
140
- "vitest": "4.1.10",
156
+ "vitest": "4.1.11",
141
157
  "yaml": "^2.9.0",
142
158
  "zod": "^4.4.3"
143
159
  },