@reasonabletech/config-vitest 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.
- package/README.md +96 -0
- package/dist/src/global-setup.d.ts +15 -0
- package/dist/src/global-setup.d.ts.map +1 -0
- package/dist/src/index.d.ts +63 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +352 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/node.d.ts +29 -0
- package/dist/src/node.d.ts.map +1 -0
- package/dist/src/node.js +209 -0
- package/dist/src/node.js.map +1 -0
- package/dist/src/react.d.ts +46 -0
- package/dist/src/react.d.ts.map +1 -0
- package/dist/src/react.js +225 -0
- package/dist/src/react.js.map +1 -0
- package/dist/src/workspace.d.ts +29 -0
- package/dist/src/workspace.d.ts.map +1 -0
- package/dist/src/workspace.js +33 -0
- package/dist/src/workspace.js.map +1 -0
- package/docs/README.md +28 -0
- package/docs/api/api-reference.md +469 -0
- package/docs/api/base-config.md +542 -0
- package/docs/guides/migration.md +47 -0
- package/docs/guides/usage-guide.md +81 -0
- package/package.json +100 -0
- package/src/global-setup.ts +58 -0
- package/src/index.ts +280 -0
- package/src/node.ts +74 -0
- package/src/react.ts +241 -0
- package/src/workspace.ts +62 -0
package/src/react.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React-specific Vitest configuration
|
|
3
|
+
* @module @reasonabletech/config-vitest/react
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import {
|
|
9
|
+
defaultClientConditions,
|
|
10
|
+
defaultServerConditions,
|
|
11
|
+
type PluginOption,
|
|
12
|
+
} from "vite";
|
|
13
|
+
import { defineConfig } from "vitest/config";
|
|
14
|
+
import type { InlineConfig } from "vitest/node";
|
|
15
|
+
import react from "@vitejs/plugin-react";
|
|
16
|
+
import { baseConfig, type DeepReadonly } from "./index.js";
|
|
17
|
+
import { readPackageName } from "./workspace.js";
|
|
18
|
+
|
|
19
|
+
type VitestConfig = DeepReadonly<{
|
|
20
|
+
test?: InlineConfig;
|
|
21
|
+
resolve?: {
|
|
22
|
+
alias?: Record<string, string>;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
};
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}>;
|
|
27
|
+
|
|
28
|
+
// Empty readonly config and plugins array for default parameters
|
|
29
|
+
const EMPTY_CONFIG = {} as const satisfies VitestConfig;
|
|
30
|
+
const EMPTY_PLUGINS = [] as const satisfies readonly PluginOption[];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Generates resolve aliases mapping a package's own name to its source directory.
|
|
34
|
+
* @param projectDir - Absolute path to the project directory
|
|
35
|
+
* @returns A record mapping the package name to its `src/` directory
|
|
36
|
+
* @see index.ts for the equivalent function used in the base config
|
|
37
|
+
*/
|
|
38
|
+
function generateSelfPackageAliases(
|
|
39
|
+
projectDir: string,
|
|
40
|
+
): Record<string, string> {
|
|
41
|
+
const packageName = readPackageName(projectDir);
|
|
42
|
+
if (packageName === null) {
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
[packageName]: `${projectDir}/src`,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Auto-detects setup files in a project directory
|
|
52
|
+
* @param projectDir - The project directory path
|
|
53
|
+
* @returns Array of detected setup file paths
|
|
54
|
+
*/
|
|
55
|
+
function autoDetectSetupFiles(projectDir?: string): string[] {
|
|
56
|
+
if (projectDir === undefined || projectDir === "") {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const vitestSetupPath = join(projectDir, "vitest.setup.ts");
|
|
61
|
+
if (existsSync(vitestSetupPath)) {
|
|
62
|
+
return ["./vitest.setup.ts"];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const testsSetupPath = join(projectDir, "tests/setup.ts");
|
|
66
|
+
if (existsSync(testsSetupPath)) {
|
|
67
|
+
return ["./tests/setup.ts"];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Auto-detects test include patterns based on project structure
|
|
75
|
+
* @returns Array of include patterns
|
|
76
|
+
*/
|
|
77
|
+
function autoDetectIncludePatterns(): string[] {
|
|
78
|
+
return ["tests/**/*.test.{ts,tsx,js,jsx}"];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* React-specific configuration options
|
|
83
|
+
*/
|
|
84
|
+
export const reactConfig = {
|
|
85
|
+
test: {
|
|
86
|
+
environment: "jsdom",
|
|
87
|
+
exclude: ["**/node_modules/**", "**/dist/**"],
|
|
88
|
+
// Suppress vitest's unhandled error reporting for expected test errors
|
|
89
|
+
silent: false,
|
|
90
|
+
onConsoleLog: (): boolean | undefined => {
|
|
91
|
+
// Allow packages to customize console filtering in their own config
|
|
92
|
+
return undefined;
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/** @overload */
|
|
98
|
+
/**
|
|
99
|
+
* Creates a Vitest configuration for React-based packages.
|
|
100
|
+
* @param projectDirOrConfig - Either the absolute project directory or an existing configuration to merge.
|
|
101
|
+
* @param customConfig - Additional configuration options for the project-dir overload.
|
|
102
|
+
* @returns A Vitest configuration optimized for React components.
|
|
103
|
+
*/
|
|
104
|
+
export function createReactConfig(
|
|
105
|
+
projectDirOrConfig?: string | VitestConfig,
|
|
106
|
+
customConfig?: VitestConfig,
|
|
107
|
+
): ReturnType<typeof defineConfig> {
|
|
108
|
+
// Handle overloaded parameters
|
|
109
|
+
let projectDir: string | undefined;
|
|
110
|
+
let config: VitestConfig;
|
|
111
|
+
|
|
112
|
+
if (typeof projectDirOrConfig === "string") {
|
|
113
|
+
projectDir = projectDirOrConfig;
|
|
114
|
+
config = customConfig ?? ({} as const);
|
|
115
|
+
} else {
|
|
116
|
+
projectDir = undefined;
|
|
117
|
+
config = projectDirOrConfig ?? ({} as const);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (projectDir !== undefined) {
|
|
121
|
+
return createReactConfigWithPlugins([react()], projectDir, config);
|
|
122
|
+
}
|
|
123
|
+
return createReactConfigWithPlugins([react()], config);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** @overload */
|
|
127
|
+
/**
|
|
128
|
+
* Creates a Vitest configuration for React-based packages with plugins.
|
|
129
|
+
* @param plugins - Array of Vite plugins to include.
|
|
130
|
+
* @param projectDirOrConfig - Either the absolute project directory or an existing configuration to merge.
|
|
131
|
+
* @param customConfig - Additional configuration options for the project-dir overload.
|
|
132
|
+
* @returns A Vitest configuration optimized for React components with plugins.
|
|
133
|
+
*/
|
|
134
|
+
export function createReactConfigWithPlugins(
|
|
135
|
+
plugins: readonly PluginOption[] = EMPTY_PLUGINS,
|
|
136
|
+
projectDirOrConfig?: string | VitestConfig,
|
|
137
|
+
customConfig?: VitestConfig,
|
|
138
|
+
): ReturnType<typeof defineConfig> {
|
|
139
|
+
// Handle overloaded parameters
|
|
140
|
+
let projectDir: string | undefined;
|
|
141
|
+
let config: VitestConfig;
|
|
142
|
+
|
|
143
|
+
if (typeof projectDirOrConfig === "string") {
|
|
144
|
+
projectDir = projectDirOrConfig;
|
|
145
|
+
config = customConfig ?? EMPTY_CONFIG;
|
|
146
|
+
} else {
|
|
147
|
+
projectDir = undefined;
|
|
148
|
+
config = projectDirOrConfig ?? EMPTY_CONFIG;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Auto-detect configuration if not explicitly provided
|
|
152
|
+
const autoSetupFiles =
|
|
153
|
+
config.test?.setupFiles !== undefined && config.test.setupFiles.length > 0
|
|
154
|
+
? []
|
|
155
|
+
: autoDetectSetupFiles(projectDir);
|
|
156
|
+
const autoIncludePatterns =
|
|
157
|
+
config.test?.include !== undefined && config.test.include.length > 0
|
|
158
|
+
? []
|
|
159
|
+
: autoDetectIncludePatterns();
|
|
160
|
+
|
|
161
|
+
return defineConfig({
|
|
162
|
+
plugins: [...plugins],
|
|
163
|
+
...baseConfig,
|
|
164
|
+
...reactConfig,
|
|
165
|
+
...config,
|
|
166
|
+
test: {
|
|
167
|
+
...baseConfig.test,
|
|
168
|
+
...reactConfig.test,
|
|
169
|
+
// Auto-detect setupFiles if not explicitly provided
|
|
170
|
+
...(autoSetupFiles.length > 0 && { setupFiles: autoSetupFiles }),
|
|
171
|
+
// Auto-detect include patterns if not explicitly provided
|
|
172
|
+
...(autoIncludePatterns.length > 0 && { include: autoIncludePatterns }),
|
|
173
|
+
...config.test,
|
|
174
|
+
coverage: {
|
|
175
|
+
...baseConfig.test.coverage,
|
|
176
|
+
...config.test?.coverage,
|
|
177
|
+
exclude: [
|
|
178
|
+
"**/node_modules/**",
|
|
179
|
+
"**/dist/**",
|
|
180
|
+
"**/tests/**",
|
|
181
|
+
"**/*.d.ts",
|
|
182
|
+
"**/*.config.{js,ts,mjs,mts}",
|
|
183
|
+
"**/coverage/**",
|
|
184
|
+
"**/examples/**",
|
|
185
|
+
"**/src/index.ts",
|
|
186
|
+
"**/src/*/index.ts",
|
|
187
|
+
"**/src/types/**",
|
|
188
|
+
"tsup.config.ts",
|
|
189
|
+
"vitest.config.mts",
|
|
190
|
+
"tailwind.config.mjs",
|
|
191
|
+
"**/.next/**",
|
|
192
|
+
"**/vitest.setup.ts",
|
|
193
|
+
"**/types.ts",
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
resolve: {
|
|
198
|
+
// Prefer "source" condition in package.json exports, allowing
|
|
199
|
+
// Vitest to resolve workspace dependencies directly to TypeScript source
|
|
200
|
+
// files without requiring a prior build step.
|
|
201
|
+
//
|
|
202
|
+
// Since Vite 6, resolve.conditions REPLACES the defaults instead of
|
|
203
|
+
// extending them. We must include defaultClientConditions to preserve
|
|
204
|
+
// standard conditions like "module", "browser", "development|production".
|
|
205
|
+
conditions: ["source", ...defaultClientConditions],
|
|
206
|
+
// Deduplicate React to prevent multiple-instance issues in tests.
|
|
207
|
+
// This is Vite's standard API for singleton enforcement — no filesystem
|
|
208
|
+
// paths needed, Vite resolves from the project root automatically.
|
|
209
|
+
dedupe: [
|
|
210
|
+
"react",
|
|
211
|
+
"react-dom",
|
|
212
|
+
"react/jsx-runtime",
|
|
213
|
+
"react/jsx-dev-runtime",
|
|
214
|
+
],
|
|
215
|
+
alias: {
|
|
216
|
+
// Auto-generate self-package alias
|
|
217
|
+
...(projectDir !== undefined &&
|
|
218
|
+
projectDir !== "" &&
|
|
219
|
+
generateSelfPackageAliases(projectDir)),
|
|
220
|
+
// Standard "@" → src alias
|
|
221
|
+
...(projectDir !== undefined &&
|
|
222
|
+
projectDir !== "" && { "@": `${projectDir}/src` }),
|
|
223
|
+
// Consumer-provided aliases override everything above
|
|
224
|
+
...config.resolve?.alias,
|
|
225
|
+
},
|
|
226
|
+
...config.resolve,
|
|
227
|
+
},
|
|
228
|
+
// Vitest runs in Vite's SSR mode. Since Vite 6, ssr.resolve.conditions is
|
|
229
|
+
// independent from resolve.conditions and defaults to defaultServerConditions.
|
|
230
|
+
// We must explicitly include "source" in ssr.resolve.conditions so
|
|
231
|
+
// workspace dependencies resolve to TypeScript source during test execution.
|
|
232
|
+
ssr: {
|
|
233
|
+
resolve: {
|
|
234
|
+
conditions: ["source", ...defaultServerConditions],
|
|
235
|
+
externalConditions: ["source"],
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export default createReactConfig;
|
package/src/workspace.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace utility functions for discovering monorepo structure
|
|
3
|
+
*
|
|
4
|
+
* These are extracted from global-setup.ts so they can be reused by
|
|
5
|
+
* the Vitest config factories (e.g. auto self-package aliasing).
|
|
6
|
+
* @module @reasonabletech/config-vitest/workspace
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
10
|
+
import * as path from "node:path";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Walks up the directory tree from `startDir` until it finds a directory
|
|
14
|
+
* containing `pnpm-workspace.yaml`, which marks the monorepo root.
|
|
15
|
+
*
|
|
16
|
+
* Returns `startDir` unchanged if no workspace root is found (e.g. when
|
|
17
|
+
* running outside the monorepo).
|
|
18
|
+
* @param startDir - The directory to start searching from
|
|
19
|
+
* @returns The absolute path to the monorepo root, or `startDir` if not found
|
|
20
|
+
*/
|
|
21
|
+
export function findRepoRoot(startDir: string): string {
|
|
22
|
+
let currentDir = startDir;
|
|
23
|
+
for (;;) {
|
|
24
|
+
if (existsSync(path.join(currentDir, "pnpm-workspace.yaml"))) {
|
|
25
|
+
return currentDir;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const parentDir = path.dirname(currentDir);
|
|
29
|
+
if (parentDir === currentDir) {
|
|
30
|
+
return startDir;
|
|
31
|
+
}
|
|
32
|
+
currentDir = parentDir;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Reads the `name` field from the `package.json` in the given directory.
|
|
38
|
+
*
|
|
39
|
+
* Returns `null` when:
|
|
40
|
+
* - No `package.json` exists at `packageDir`
|
|
41
|
+
* - The file cannot be parsed as JSON
|
|
42
|
+
* - The `name` field is missing, empty, or not a string
|
|
43
|
+
* @param packageDir - The directory containing the package.json to read
|
|
44
|
+
* @returns The package name string, or `null` if unavailable
|
|
45
|
+
*/
|
|
46
|
+
export function readPackageName(packageDir: string): string | null {
|
|
47
|
+
const packageJsonPath = path.join(packageDir, "package.json");
|
|
48
|
+
if (!existsSync(packageJsonPath)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as {
|
|
54
|
+
name?: unknown;
|
|
55
|
+
};
|
|
56
|
+
return typeof parsed.name === "string" && parsed.name.length > 0
|
|
57
|
+
? parsed.name
|
|
58
|
+
: null;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|