@coryrylan/tools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +193 -0
  3. package/dist/eslint/configs/browser.d.ts +12 -0
  4. package/dist/eslint/configs/browser.js +40 -0
  5. package/dist/eslint/configs/html.d.ts +10 -0
  6. package/dist/eslint/configs/html.js +38 -0
  7. package/dist/eslint/configs/json.d.ts +13 -0
  8. package/dist/eslint/configs/json.js +39 -0
  9. package/dist/eslint/configs/shared.d.ts +11 -0
  10. package/dist/eslint/configs/shared.js +19 -0
  11. package/dist/eslint/configs/tests.d.ts +8 -0
  12. package/dist/eslint/configs/tests.js +20 -0
  13. package/dist/eslint/configs/typescript.d.ts +18 -0
  14. package/dist/eslint/configs/typescript.js +104 -0
  15. package/dist/eslint/index.d.ts +27 -0
  16. package/dist/eslint/index.js +19 -0
  17. package/dist/eslint/plugin.d.ts +8 -0
  18. package/dist/eslint/plugin.js +36 -0
  19. package/dist/eslint/rules/consistent-error-messages.d.ts +10 -0
  20. package/dist/eslint/rules/consistent-error-messages.js +127 -0
  21. package/dist/eslint/rules/no-dead-code.d.ts +16 -0
  22. package/dist/eslint/rules/no-dead-code.js +110 -0
  23. package/dist/eslint/rules/no-deep-class-inheritance.d.ts +19 -0
  24. package/dist/eslint/rules/no-deep-class-inheritance.js +132 -0
  25. package/dist/eslint/rules/no-reexport-barrels.d.ts +10 -0
  26. package/dist/eslint/rules/no-reexport-barrels.js +55 -0
  27. package/dist/eslint/rules/no-single-consumer-abstraction.d.ts +3 -0
  28. package/dist/eslint/rules/no-single-consumer-abstraction.js +364 -0
  29. package/dist/eslint/rules/no-unjustified-disable.d.ts +11 -0
  30. package/dist/eslint/rules/no-unjustified-disable.js +75 -0
  31. package/dist/eslint/rules/no-unpinned-dependency-ranges.d.ts +46 -0
  32. package/dist/eslint/rules/no-unpinned-dependency-ranges.js +79 -0
  33. package/dist/eslint/rules/require-listener-cleanup.d.ts +8 -0
  34. package/dist/eslint/rules/require-listener-cleanup.js +202 -0
  35. package/dist/eslint/rules/require-observer-cleanup.d.ts +8 -0
  36. package/dist/eslint/rules/require-observer-cleanup.js +69 -0
  37. package/dist/eslint/rules/require-timer-cleanup.d.ts +10 -0
  38. package/dist/eslint/rules/require-timer-cleanup.js +142 -0
  39. package/dist/eslint/rules/utils.d.ts +45 -0
  40. package/dist/eslint/rules/utils.js +109 -0
  41. package/dist/prettier/index.d.ts +15 -0
  42. package/dist/prettier/index.js +26 -0
  43. package/dist/stylelint/index.d.ts +10 -0
  44. package/dist/stylelint/index.js +50 -0
  45. package/dist/vale/styles/config/vocabularies/Tools/accept.txt +80 -0
  46. package/dist/vale/styles/config/vocabularies/Tools/reject.txt +5 -0
  47. package/dist/vale/vale.ini +27 -0
  48. package/dist/vite/index.d.ts +37 -0
  49. package/dist/vite/index.js +61 -0
  50. package/dist/vite/plugins/dts.d.ts +26 -0
  51. package/dist/vite/plugins/dts.js +106 -0
  52. package/dist/vite/plugins/write-if-changed.d.ts +20 -0
  53. package/dist/vite/plugins/write-if-changed.js +33 -0
  54. package/dist/vitest/browser.d.ts +31 -0
  55. package/dist/vitest/browser.js +105 -0
  56. package/dist/vitest/index.d.ts +28 -0
  57. package/dist/vitest/index.js +34 -0
  58. package/package.json +200 -0
@@ -0,0 +1,50 @@
1
+ //#region src/stylelint/index.ts
2
+ /**
3
+ * Personal Stylelint preferences layered on `stylelint-config-standard`.
4
+ * Point a `stylelint.config.js` at it with
5
+ * `export { default } from '@coryrylan/tools/stylelint';`, or import the
6
+ * default export and add `rules`/`overrides` on top for project-specific
7
+ * exceptions.
8
+ */
9
+ var config = {
10
+ extends: ["stylelint-config-standard"],
11
+ ignoreFiles: [
12
+ "**/node_modules/**",
13
+ "**/dist/**",
14
+ "**/.wireit/**",
15
+ "**/.eslintcache/**",
16
+ "**/coverage/**"
17
+ ],
18
+ rules: {
19
+ "alpha-value-notation": "number",
20
+ "hue-degree-notation": "number",
21
+ "property-no-vendor-prefix": null,
22
+ "declaration-empty-line-before": null,
23
+ "custom-property-empty-line-before": null,
24
+ "import-notation": null,
25
+ "custom-property-pattern": "^_?[a-z][a-z0-9]*(-[a-z0-9]+)*$",
26
+ "value-keyword-case": ["lower", { ignoreKeywords: ["currentColor"] }],
27
+ "declaration-block-no-redundant-longhand-properties": [true, { ignoreShorthands: ["grid-template"] }],
28
+ "no-duplicate-selectors": null,
29
+ "no-descending-specificity": null,
30
+ "comment-empty-line-before": null,
31
+ "property-disallowed-list": [
32
+ "margin-left",
33
+ "margin-right",
34
+ "margin-top",
35
+ "margin-bottom",
36
+ "padding-left",
37
+ "padding-right",
38
+ "padding-top",
39
+ "padding-bottom"
40
+ ],
41
+ "declaration-property-value-disallowed-list": [{
42
+ "/^(margin|padding)(-(top|right|bottom|left|inline|block|inline-start|inline-end|block-start|block-end))?$/": ["/(?<!\\w)\\d+px/"],
43
+ "/^(gap|row-gap|column-gap)$/": ["/(?<!\\w)\\d+px/"],
44
+ "/^(top|right|bottom|left)$/": ["/(?<!\\w)\\d+px/"],
45
+ "/^inset(-(inline|block)(-(start|end))?)?$/": ["/(?<!\\w)\\d+px/"]
46
+ }, { message: "Use a CSS custom property (design token) instead of a hardcoded pixel value." }]
47
+ }
48
+ };
49
+ //#endregion
50
+ export { config as default };
@@ -0,0 +1,80 @@
1
+ (H|h)ardcoded
2
+ a11y
3
+ allowlist(s)?
4
+ API(s)?
5
+ ARIA
6
+ blocklist(s)?
7
+ boolean(s)?
8
+ bundler(s)?
9
+ changelog
10
+ CLI
11
+ codebase(s)?
12
+ config(s)?
13
+ CSS
14
+ declaratively
15
+ dedupe(d|s)?
16
+ deduplicate(d|s)?
17
+ deduplication
18
+ dev
19
+ devDependenc(y|ies)
20
+ DOM
21
+ downlevel
22
+ Eleventy
23
+ ESLint
24
+ ESM
25
+ extensionless
26
+ flexbox
27
+ frontmatter
28
+ GitHub
29
+ globals
30
+ greppable
31
+ HTML
32
+ i18n
33
+ IDE
34
+ ini
35
+ inlined
36
+ istanbul
37
+ JSDoc
38
+ JSON(C)?
39
+ linter(s)?
40
+ linting
41
+ lockfile
42
+ longhand(s)?
43
+ lookup(s)?
44
+ MCP
45
+ middleware
46
+ mise
47
+ mixin(s)?
48
+ monorepo(s)?
49
+ multiline
50
+ namespace(d|s)?
51
+ npm
52
+ NVIDIA
53
+ parens
54
+ Playwright
55
+ pluggable
56
+ pnpm
57
+ polyfill(s)?
58
+ pre-release
59
+ Prettier
60
+ repo(s)?
61
+ Rolldown
62
+ Rollup('s)?
63
+ Rylan
64
+ semver
65
+ SSR
66
+ Stylelint
67
+ stylesheet(s)?
68
+ subclass(ers|es|ing)?
69
+ superclass('s|es)?
70
+ teardown(s)?
71
+ toolchain(s)?
72
+ tsconfig
73
+ TypeScript
74
+ typescript-eslint
75
+ uncomment(ed)?
76
+ Vale
77
+ Vite
78
+ Vitest
79
+ wireit
80
+ write-good
@@ -0,0 +1,5 @@
1
+ blacklist
2
+ e-mail
3
+ repo(sitory)? admin
4
+ web site
5
+ whitelist
@@ -0,0 +1,27 @@
1
+ # StylesPath assumes a consumer's .vale.ini lives at the project root, so
2
+ # node_modules/@coryrylan/tools resolves relative to it - adjust the path if
3
+ # this template is copied elsewhere. Run `vale sync` once after installing to
4
+ # download the Google and write-good packages into StylesPath.
5
+ StylesPath = node_modules/@coryrylan/tools/dist/vale/styles
6
+ MinAlertLevel = warning
7
+ Vocab = Tools
8
+ Packages = Google, write-good
9
+
10
+ [*.md]
11
+ BasedOnStyles = Vale, Google, write-good
12
+ TokenIgnores = (\{%[\s\S]*?%\}), (\{\{[^}]*\}\})
13
+ Vale.Terms = NO
14
+ Google.Headings = NO
15
+ Google.WordList = NO
16
+ ; Spaced em dashes, `e.g.`/`i.e.`, and logical quote punctuation
17
+ ; (periods outside quotation marks) are the house style.
18
+ Google.EmDash = NO
19
+ Google.Latin = NO
20
+ Google.Quotes = NO
21
+
22
+ [*.ts]
23
+ BasedOnStyles = Vale, write-good
24
+ write-good.TooWordy = error
25
+ write-good.Passive = error
26
+ Vale.Spelling = NO
27
+ Vale.Terms = NO
@@ -0,0 +1,37 @@
1
+ import type { LibraryOptions, UserConfig } from 'vite';
2
+ /** Options for {@link createNodeLibraryBuildConfig}. */
3
+ export interface NodeLibraryBuildConfigOptions {
4
+ readonly entry: LibraryOptions['entry'];
5
+ }
6
+ /** Options for {@link createBrowserLibraryBuildConfig}. */
7
+ export interface BrowserLibraryBuildConfigOptions {
8
+ readonly entry: LibraryOptions['entry'];
9
+ }
10
+ /**
11
+ * Vite build config for a Node.js library entry point: ESM output only,
12
+ * targets `node22`, and never bundles a dependency - every bare specifier
13
+ * (`lit`, `node:path`, `@scope/pkg`) is externalized. `preserveModules`
14
+ * keeps one output file per source module instead of one monolithic
15
+ * bundle, so `package.json#exports` can expose deep-import paths.
16
+ *
17
+ * Extend it with `mergeConfig` rather than hand-rolling the shape again:
18
+ *
19
+ * ```ts
20
+ * import { mergeConfig } from 'vite';
21
+ * import { createNodeLibraryBuildConfig } from '@coryrylan/tools/vite';
22
+ *
23
+ * export default mergeConfig(
24
+ * createNodeLibraryBuildConfig({ entry: { index: 'src/index.ts' } }),
25
+ * { plugins: [] },
26
+ * );
27
+ * ```
28
+ */
29
+ export declare function createNodeLibraryBuildConfig(options: NodeLibraryBuildConfigOptions): UserConfig;
30
+ /**
31
+ * Vite build config for a browser library entry point. Same shape as
32
+ * {@link createNodeLibraryBuildConfig} - ESM-only, everything external,
33
+ * `preserveModules` - except it targets `esnext` instead of a Node.js
34
+ * runtime, since the consuming browser (or a downstream bundler) declares
35
+ * its own compilation baseline.
36
+ */
37
+ export declare function createBrowserLibraryBuildConfig(options: BrowserLibraryBuildConfigOptions): UserConfig;
@@ -0,0 +1,61 @@
1
+ //#region src/vite/index.ts
2
+ /**
3
+ * The build shape both factories below share: ESM-only, every bare
4
+ * specifier externalized (a dependency stays a real `package.json`
5
+ * dependency instead of getting inlined, so tree-shaking stays viable
6
+ * downstream), and `preserveModules` so `dist` mirrors `src` 1:1 - one
7
+ * output file per source module, which is what lets `package.json#exports`
8
+ * map deep-import paths straight to build output.
9
+ */
10
+ function createLibraryBuildConfig(target, entry) {
11
+ return { build: {
12
+ target,
13
+ minify: false,
14
+ lib: {
15
+ entry,
16
+ formats: ["es"]
17
+ },
18
+ rollupOptions: {
19
+ external: (id) => !id.startsWith(".") && !id.startsWith("/"),
20
+ output: {
21
+ preserveModules: true,
22
+ preserveModulesRoot: "src",
23
+ entryFileNames: "[name].js"
24
+ }
25
+ }
26
+ } };
27
+ }
28
+ /**
29
+ * Vite build config for a Node.js library entry point: ESM output only,
30
+ * targets `node22`, and never bundles a dependency - every bare specifier
31
+ * (`lit`, `node:path`, `@scope/pkg`) is externalized. `preserveModules`
32
+ * keeps one output file per source module instead of one monolithic
33
+ * bundle, so `package.json#exports` can expose deep-import paths.
34
+ *
35
+ * Extend it with `mergeConfig` rather than hand-rolling the shape again:
36
+ *
37
+ * ```ts
38
+ * import { mergeConfig } from 'vite';
39
+ * import { createNodeLibraryBuildConfig } from '@coryrylan/tools/vite';
40
+ *
41
+ * export default mergeConfig(
42
+ * createNodeLibraryBuildConfig({ entry: { index: 'src/index.ts' } }),
43
+ * { plugins: [] },
44
+ * );
45
+ * ```
46
+ */
47
+ function createNodeLibraryBuildConfig(options) {
48
+ return createLibraryBuildConfig("node22", options.entry);
49
+ }
50
+ /**
51
+ * Vite build config for a browser library entry point. Same shape as
52
+ * {@link createNodeLibraryBuildConfig} - ESM-only, everything external,
53
+ * `preserveModules` - except it targets `esnext` instead of a Node.js
54
+ * runtime, since the consuming browser (or a downstream bundler) declares
55
+ * its own compilation baseline.
56
+ */
57
+ function createBrowserLibraryBuildConfig(options) {
58
+ return createLibraryBuildConfig("esnext", options.entry);
59
+ }
60
+ //#endregion
61
+ export { createBrowserLibraryBuildConfig, createNodeLibraryBuildConfig };
@@ -0,0 +1,26 @@
1
+ import type { Plugin } from 'vite';
2
+ /** Options for {@link dts}. */
3
+ export interface DtsPluginOptions {
4
+ /** Path to the `tsconfig` used for declaration emit, resolved against `process.cwd()`. @default 'tsconfig.build.json' */
5
+ readonly project?: string;
6
+ /** Directory declarations are emitted into, resolved against `process.cwd()`. @default 'dist' */
7
+ readonly outDir?: string;
8
+ /** Package providing the `bin/tsc` entry point to shell out to - swap in `'typescript-go'` for the native compiler alias. @default 'typescript' */
9
+ readonly compilerPackage?: string;
10
+ }
11
+ /**
12
+ * Generates the type declaration files that accompany a library's published
13
+ * JS build. Shells out to the consumer's own TypeScript compiler (or the
14
+ * `typescript-go` native-compiler alias, via `compilerPackage`) at build
15
+ * start so declarations are always in sync with the code that just built,
16
+ * then copies over any hand-written `.d.ts` files `tsc` wouldn't otherwise
17
+ * emit - for example, ambient module declarations. The compiler runs
18
+ * asynchronously; watch builds additionally pass `--incremental` with build
19
+ * info stored at `<outDir>/.tsbuildinfo` so a rebuild reuses tsc's prior
20
+ * state instead of retypechecking from scratch.
21
+ *
22
+ * If `compilerPackage` can't be resolved from the consumer's dependency
23
+ * tree, this warns and no-ops rather than throwing - library code never
24
+ * crashes a consumer's build over a misconfigured option.
25
+ */
26
+ export declare function dts(options?: DtsPluginOptions): Plugin;
@@ -0,0 +1,106 @@
1
+ import { copyFileSync, existsSync, mkdirSync, readdirSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { spawn } from "node:child_process";
5
+ import { createRequire } from "node:module";
6
+ //#region src/vite/plugins/dts.ts
7
+ var DEFAULT_PROJECT = "tsconfig.build.json";
8
+ var DEFAULT_OUT_DIR = "dist";
9
+ var DEFAULT_COMPILER_PACKAGE = "typescript";
10
+ /**
11
+ * Resolves `<compilerPackage>/bin/tsc` from the *consuming* project's own
12
+ * dependency tree, not this package's - a monorepo consumer commonly pins a
13
+ * different TypeScript (or the `typescript-go` native-compiler alias) than
14
+ * whatever this package itself was built with.
15
+ */
16
+ function resolveTscBinaryPath(compilerPackage) {
17
+ try {
18
+ return resolve(dirname(createRequire(pathToFileURL(join(process.cwd(), "package.json"))).resolve(`${compilerPackage}/package.json`)), "bin/tsc");
19
+ } catch {
20
+ return;
21
+ }
22
+ }
23
+ /**
24
+ * Copies every hand-written `src/**\/*.d.ts` (excluding `*.test.d.ts`) into
25
+ * `outDir`, preserving its path relative to `srcDir` - `tsc` only emits
26
+ * declarations for `.ts` sources, so ambient/hand-authored `.d.ts` files
27
+ * never make it into the build output on their own.
28
+ */
29
+ function copyHandWrittenDeclarationFiles(srcDir, outDir) {
30
+ if (!existsSync(srcDir)) return;
31
+ const declarationFilePaths = readdirSync(srcDir, { recursive: true }).filter((entry) => typeof entry === "string").filter((entry) => entry.endsWith(".d.ts") && !entry.endsWith(".test.d.ts"));
32
+ for (const relativePath of declarationFilePaths) {
33
+ const destination = resolve(outDir, relativePath);
34
+ mkdirSync(dirname(destination), { recursive: true });
35
+ copyFileSync(resolve(srcDir, relativePath), destination);
36
+ }
37
+ }
38
+ /**
39
+ * Runs the resolved tsc binary without blocking the event loop. Rejects on a
40
+ * nonzero exit so type errors still fail the build - the one place this
41
+ * package's warn-and-no-op rule doesn't apply, because a silently skipped
42
+ * typecheck would publish broken declarations.
43
+ */
44
+ function runTsc(args) {
45
+ return new Promise((resolvePromise, rejectPromise) => {
46
+ const child = spawn(process.execPath, args, { stdio: "inherit" });
47
+ child.on("error", rejectPromise);
48
+ child.on("close", (code) => {
49
+ if (code === 0) resolvePromise();
50
+ else rejectPromise(/* @__PURE__ */ new Error(`tsc exited with code ${String(code)}`));
51
+ });
52
+ });
53
+ }
54
+ /**
55
+ * Generates the type declaration files that accompany a library's published
56
+ * JS build. Shells out to the consumer's own TypeScript compiler (or the
57
+ * `typescript-go` native-compiler alias, via `compilerPackage`) at build
58
+ * start so declarations are always in sync with the code that just built,
59
+ * then copies over any hand-written `.d.ts` files `tsc` wouldn't otherwise
60
+ * emit - for example, ambient module declarations. The compiler runs
61
+ * asynchronously; watch builds additionally pass `--incremental` with build
62
+ * info stored at `<outDir>/.tsbuildinfo` so a rebuild reuses tsc's prior
63
+ * state instead of retypechecking from scratch.
64
+ *
65
+ * If `compilerPackage` can't be resolved from the consumer's dependency
66
+ * tree, this warns and no-ops rather than throwing - library code never
67
+ * crashes a consumer's build over a misconfigured option.
68
+ */
69
+ function dts(options = {}) {
70
+ const project = options.project ?? DEFAULT_PROJECT;
71
+ const outDir = options.outDir ?? DEFAULT_OUT_DIR;
72
+ const compilerPackage = options.compilerPackage ?? DEFAULT_COMPILER_PACKAGE;
73
+ let isWatchBuild = false;
74
+ return {
75
+ name: "dts",
76
+ apply: "build",
77
+ configResolved(config) {
78
+ isWatchBuild = config.build.watch !== null;
79
+ },
80
+ async buildStart() {
81
+ const cwd = process.cwd();
82
+ const tscPath = resolveTscBinaryPath(compilerPackage);
83
+ if (tscPath === void 0) {
84
+ console.warn(`[@coryrylan/tools/vite] Could not resolve compiler package "${compilerPackage}" from ${cwd} - skipping declaration generation.`);
85
+ return;
86
+ }
87
+ const projectPath = resolve(cwd, project);
88
+ const outDirPath = resolve(cwd, outDir);
89
+ await runTsc([
90
+ tscPath,
91
+ "--project",
92
+ projectPath,
93
+ "--outDir",
94
+ outDirPath,
95
+ ...isWatchBuild ? [
96
+ "--incremental",
97
+ "--tsBuildInfoFile",
98
+ join(outDirPath, ".tsbuildinfo")
99
+ ] : []
100
+ ]);
101
+ copyHandWrittenDeclarationFiles(resolve(cwd, "src"), outDirPath);
102
+ }
103
+ };
104
+ }
105
+ //#endregion
106
+ export { dts };
@@ -0,0 +1,20 @@
1
+ import type { Plugin } from 'vite';
2
+ /** Options for {@link writeIfChanged}. */
3
+ export interface WriteIfChangedPluginOptions {
4
+ /**
5
+ * Output directory to resolve emitted chunk file names against. Only
6
+ * consulted when Rollup's own `output.dir` isn't set on the build.
7
+ */
8
+ readonly outDir?: string;
9
+ }
10
+ /**
11
+ * Vite plugin that only writes bundle files to disk if their content has
12
+ * changed. Prevents unnecessary file system writes - and the downstream
13
+ * rebuilds/HMR churn a file watcher would otherwise trigger - when a
14
+ * rebuild produces byte-identical output.
15
+ *
16
+ * Uses `writeBundle` instead of `generateBundle` for Rolldown compatibility:
17
+ * Rolldown does not support mutating the bundle object, so the write has to
18
+ * happen after Rollup/Rolldown's own bundle-writing step, not in place of it.
19
+ */
20
+ export declare function writeIfChanged(options?: WriteIfChangedPluginOptions): Plugin;
@@ -0,0 +1,33 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { extname, resolve } from "node:path";
3
+ //#region src/vite/plugins/write-if-changed.ts
4
+ var writtenFileContentsByPath = /* @__PURE__ */ new Map();
5
+ /**
6
+ * Vite plugin that only writes bundle files to disk if their content has
7
+ * changed. Prevents unnecessary file system writes - and the downstream
8
+ * rebuilds/HMR churn a file watcher would otherwise trigger - when a
9
+ * rebuild produces byte-identical output.
10
+ *
11
+ * Uses `writeBundle` instead of `generateBundle` for Rolldown compatibility:
12
+ * Rolldown does not support mutating the bundle object, so the write has to
13
+ * happen after Rollup/Rolldown's own bundle-writing step, not in place of it.
14
+ */
15
+ function writeIfChanged(options = {}) {
16
+ return {
17
+ name: "write-if-changed",
18
+ writeBundle(outputOptions, bundle) {
19
+ const dir = outputOptions.dir ?? options.outDir;
20
+ if (dir === void 0) return;
21
+ for (const bundleFile of Object.values(bundle)) {
22
+ if (bundleFile.type !== "chunk" || extname(bundleFile.fileName) !== ".js") continue;
23
+ const filePath = resolve(dir, bundleFile.fileName);
24
+ if (writtenFileContentsByPath.get(filePath) === bundleFile.code) continue;
25
+ writtenFileContentsByPath.set(filePath, bundleFile.code);
26
+ mkdirSync(resolve(filePath, ".."), { recursive: true });
27
+ writeFileSync(filePath, bundleFile.code);
28
+ }
29
+ }
30
+ };
31
+ }
32
+ //#endregion
33
+ export { writeIfChanged };
@@ -0,0 +1,31 @@
1
+ import type { ViteUserConfig } from 'vitest/config';
2
+ /**
3
+ * Preset Vitest config for browser-mode component tests against a real
4
+ * Chromium instance via the Playwright provider. Separate entry point from
5
+ * `./index.js` so a consumer without `@vitest/browser-playwright` installed
6
+ * never loads this module's import of it.
7
+ *
8
+ * Extend it with `mergeConfig`:
9
+ *
10
+ * ```ts
11
+ * // vitest.config.ts
12
+ * import { mergeConfig, defineConfig } from 'vitest/config';
13
+ * import { browserTestConfig } from '@coryrylan/tools/vitest/browser';
14
+ *
15
+ * export default mergeConfig(
16
+ * browserTestConfig,
17
+ * defineConfig({
18
+ * test: { setupFiles: ['./test-setup.ts'] },
19
+ * }),
20
+ * );
21
+ * ```
22
+ *
23
+ * Requires the optional peers `@vitest/browser-playwright`,
24
+ * `@vitest/coverage-istanbul`, and `playwright`, plus a browser installed
25
+ * via `pnpm exec playwright install chromium`.
26
+ *
27
+ * `CI`/`--watch` detection is resolved once at import time (see `isCi` and
28
+ * `isWatchMode` above) - changing either after this module loads has no
29
+ * effect on an already-built config.
30
+ */
31
+ export declare const browserTestConfig: ViteUserConfig;
@@ -0,0 +1,105 @@
1
+ import process from "node:process";
2
+ import { playwright } from "@vitest/browser-playwright";
3
+ //#region src/vitest/browser.ts
4
+ var isCi = process.env["CI"] !== void 0;
5
+ var isWatchMode = process.argv.includes("--watch");
6
+ /**
7
+ * Preset Vitest config for browser-mode component tests against a real
8
+ * Chromium instance via the Playwright provider. Separate entry point from
9
+ * `./index.js` so a consumer without `@vitest/browser-playwright` installed
10
+ * never loads this module's import of it.
11
+ *
12
+ * Extend it with `mergeConfig`:
13
+ *
14
+ * ```ts
15
+ * // vitest.config.ts
16
+ * import { mergeConfig, defineConfig } from 'vitest/config';
17
+ * import { browserTestConfig } from '@coryrylan/tools/vitest/browser';
18
+ *
19
+ * export default mergeConfig(
20
+ * browserTestConfig,
21
+ * defineConfig({
22
+ * test: { setupFiles: ['./test-setup.ts'] },
23
+ * }),
24
+ * );
25
+ * ```
26
+ *
27
+ * Requires the optional peers `@vitest/browser-playwright`,
28
+ * `@vitest/coverage-istanbul`, and `playwright`, plus a browser installed
29
+ * via `pnpm exec playwright install chromium`.
30
+ *
31
+ * `CI`/`--watch` detection is resolved once at import time (see `isCi` and
32
+ * `isWatchMode` above) - changing either after this module loads has no
33
+ * effect on an already-built config.
34
+ */
35
+ var browserTestConfig = { test: {
36
+ globals: true,
37
+ include: ["src/**/*.test.ts"],
38
+ retry: 2,
39
+ ...isCi ? {
40
+ maxWorkers: 1,
41
+ maxConcurrency: 1
42
+ } : {},
43
+ testTimeout: 6e4,
44
+ hookTimeout: 3e4,
45
+ browser: {
46
+ fileParallelism: !isCi,
47
+ enabled: true,
48
+ provider: playwright({ launchOptions: {
49
+ args: [
50
+ "--disable-dev-shm-usage",
51
+ "--disable-setuid-sandbox",
52
+ "--disable-software-rasterizer",
53
+ "--no-sandbox",
54
+ "--disable-gpu",
55
+ "--disable-extensions",
56
+ "--disable-background-networking",
57
+ "--disable-default-apps",
58
+ "--disable-sync",
59
+ "--disable-translate",
60
+ "--metrics-recording-only",
61
+ "--mute-audio",
62
+ "--no-first-run",
63
+ "--safebrowsing-disable-auto-update",
64
+ "--disable-features=TranslateUI",
65
+ "--disable-features=BlinkGenPropertyTrees",
66
+ "--disable-ipc-flooding-protection"
67
+ ],
68
+ timeout: 12e4
69
+ } }),
70
+ headless: !isWatchMode,
71
+ instances: [{ browser: "chromium" }]
72
+ },
73
+ coverage: {
74
+ provider: "istanbul",
75
+ reportsDirectory: "./coverage/unit",
76
+ reporter: [
77
+ ["lcov", { file: "coverage.dat" }],
78
+ "html",
79
+ "json-summary"
80
+ ],
81
+ thresholds: {
82
+ lines: 90,
83
+ branches: 90,
84
+ functions: 90,
85
+ statements: 90
86
+ },
87
+ watermarks: {
88
+ statements: [80, 90],
89
+ functions: [80, 90],
90
+ branches: [80, 90],
91
+ lines: [80, 90]
92
+ },
93
+ include: ["src/**/*.ts"],
94
+ exclude: [
95
+ "**/dist/**",
96
+ "**/*.test.ts",
97
+ "**/*.d.ts",
98
+ "**/*.examples.ts",
99
+ "vite.*.ts",
100
+ "vitest.*.ts"
101
+ ]
102
+ }
103
+ } };
104
+ //#endregion
105
+ export { browserTestConfig };
@@ -0,0 +1,28 @@
1
+ import type { ViteUserConfig } from 'vitest/config';
2
+ /**
3
+ * Preset Vitest config for node-environment unit tests of libraries and
4
+ * tooling: Node globals, no DOM, `test.globals` enabled so specs can call
5
+ * `describe`/`it`/`expect` without importing them.
6
+ *
7
+ * Extend it with `mergeConfig` rather than spreading manually, so array
8
+ * fields (e.g. `test.include`) merge instead of getting clobbered:
9
+ *
10
+ * ```ts
11
+ * // vitest.config.ts
12
+ * import { mergeConfig, defineConfig } from 'vitest/config';
13
+ * import { nodeTestConfig } from '@coryrylan/tools/vitest';
14
+ *
15
+ * export default mergeConfig(
16
+ * nodeTestConfig,
17
+ * defineConfig({
18
+ * test: { coverage: { provider: 'istanbul' } },
19
+ * }),
20
+ * );
21
+ * ```
22
+ *
23
+ * This module has zero runtime dependency on `@vitest/browser-playwright` -
24
+ * it is safe to import without that optional peer installed. Browser-mode
25
+ * config lives in the sibling `./browser.js` entry point instead, so a
26
+ * plain node consumer never pulls in a Playwright import transitively.
27
+ */
28
+ export declare const nodeTestConfig: ViteUserConfig;
@@ -0,0 +1,34 @@
1
+ //#region src/vitest/index.ts
2
+ /**
3
+ * Preset Vitest config for node-environment unit tests of libraries and
4
+ * tooling: Node globals, no DOM, `test.globals` enabled so specs can call
5
+ * `describe`/`it`/`expect` without importing them.
6
+ *
7
+ * Extend it with `mergeConfig` rather than spreading manually, so array
8
+ * fields (e.g. `test.include`) merge instead of getting clobbered:
9
+ *
10
+ * ```ts
11
+ * // vitest.config.ts
12
+ * import { mergeConfig, defineConfig } from 'vitest/config';
13
+ * import { nodeTestConfig } from '@coryrylan/tools/vitest';
14
+ *
15
+ * export default mergeConfig(
16
+ * nodeTestConfig,
17
+ * defineConfig({
18
+ * test: { coverage: { provider: 'istanbul' } },
19
+ * }),
20
+ * );
21
+ * ```
22
+ *
23
+ * This module has zero runtime dependency on `@vitest/browser-playwright` -
24
+ * it is safe to import without that optional peer installed. Browser-mode
25
+ * config lives in the sibling `./browser.js` entry point instead, so a
26
+ * plain node consumer never pulls in a Playwright import transitively.
27
+ */
28
+ var nodeTestConfig = { test: {
29
+ globals: true,
30
+ environment: "node",
31
+ include: ["src/**/*.test.ts"]
32
+ } };
33
+ //#endregion
34
+ export { nodeTestConfig };