1import * as os from "node:os"
2import * as path from "node:path"
3import { defineConfig, mergeConfig, type ViteUserConfig } from "vitest/config"
4
5const isDeno = process.versions.deno !== undefined
6const isBun = process.versions.bun !== undefined
7const isNode = typeof process !== "undefined" &&
8 process.release.name === "node" &&
9 !isDeno &&
10 !isBun
11const integrationTestsEnabled = process.env.EFFECT_INTEGRATION_TESTS === "1"
12const clusterTestsEnabled = process.env.EFFECT_CLUSTER_TESTS === "1"
13
14const project = (
15 name: string,
16 directory: string,
17 include: boolean = true,
18 config: ViteUserConfig = {},
19 projectExclude?: ReadonlyArray<string>,
20 projectInclude?: ReadonlyArray<string>
21) => {
22 if (!include) {
23 return []
24 }
25
26 const cfg = mergeConfig({
27 root: directory,
28 test: { name }
29 }, config)
30
31 const merged = mergeConfig(shared, cfg)
32 if (projectExclude !== undefined) {
33 merged.test!.exclude = [...projectExclude]
34 }
35 if (projectInclude !== undefined) {
36 merged.test!.include = [...projectInclude]
37 }
38 return [merged]
39}
40
41export const exclude = [
42 "**/.*/**",
43 "**/node_modules/**",
44 "**/dist/**",
45 "**/benchmark/**",
46 "**/bundle/**",
47 "**/typetest/**",
48 "**/coverage/**",
49 "**/test/utils/**",
50 "**/test/cluster-integration/**",
51 ...(!integrationTestsEnabled ? ["**/*.integration.test.{ts,tsx}"] : []),
52 "**/*.d.ts",
53 "**/*.config.*",
54 "**/vitest.*"
55]
56
57const shared: ViteUserConfig = {
58 optimizeDeps: {
59 exclude: ["bun:sqlite"]
60 },
61 server: {
62 watch: {
63 ignored: exclude
64 }
65 },
66 resolve: {
67 tsconfigPaths: true
68 },
69 test: {
70 exclude,
71 passWithNoTests: true,
72 setupFiles: [path.join(import.meta.dirname, "vitest.setup.ts")],
73 sequence: {
74 concurrent: true
75 },
76 include: ["test/**/*.test.{ts,tsx}"],
77 coverage: {
78 provider: "v8",
79 reporter: ["html"],
80 reportsDirectory: "coverage",
81 exclude
82 }
83 }
84}
85
86export default defineConfig({
87 test: {
88 passWithNoTests: true,
89 projects: [
90 ...project(
91 "effect",
92 "packages/effect",
93 true,
94 {},
95 isBun
96 ? [
97 ...exclude,
98 // These tests assert Node-specific Web API or stack trace behavior.
99 "test/reactivity/Atom.test.ts",
100 "test/schema/Schema.test.ts",
101 "test/schema/SchemaGetter.test.ts",
102 "test/schema/toCodec.test.ts",
103 "test/schema/toDifferJsonPatch.test.ts",
104 "test/http/HttpEffect.test.ts",
105 "test/http/HttpServerRequest.test.ts"
106 ]
107 : undefined
108 ),
109 ...project("@effect/ai-anthropic", "packages/ai/anthropic"),
110 ...project("@effect/ai-cloudflare", "packages/ai/cloudflare"),
111 ...project("@effect/ai-openai", "packages/ai/openai"),
112 ...project("@effect/ai-typesafe", "packages/ai/typesafe"),
113 ...project("@effect/ai-openai-compat", "packages/ai/openai-compat"),
114 ...project("@effect/ai-openrouter", "packages/ai/openrouter"),
115 ...project("@effect/atom-react", "packages/atom/react", true, {
116 test: {
117 environment: "jsdom",
118 setupFiles: [path.join(import.meta.dirname, "packages/atom/react/vitest.setup.ts")]
119 }
120 }),
121 ...project("@effect/atom-solid", "packages/atom/solid", true, {
122 resolve: {
123 conditions: ["browser"]
124 },
125 test: {
126 environment: "jsdom",
127 setupFiles: [path.join(import.meta.dirname, "packages/atom/solid/vitest.setup.ts")]
128 }
129 }),
130 ...project("@effect/atom-vue", "packages/atom/vue", true, {
131 test: {
132 environment: "happy-dom"
133 }
134 }),
135 ...project("@effect/opentelemetry", "packages/opentelemetry"),
136 ...project("@effect/platform-browser", "packages/platform/browser", true, {
137 test: {
138 environment: "happy-dom",
139 // Bun interprets Node's --localstorage-file value as a module path.
140 execArgv: isBun
141 ? []
142 : [
143 "--localstorage-file",
144 path.resolve(os.tmpdir(), `vitest-${process.pid}.localstorage`)
145 ],
146 setupFiles: [path.join(import.meta.dirname, "packages/platform/browser/vitest.setup.ts")]
147 }
148 }),
149 ...project("@effect/platform-bun", "packages/platform/bun", isBun),
150 ...project("@effect/platform-deno", "packages/platform/deno", isDeno),
151 ...project("@effect/platform-node", "packages/platform/node", isNode),
152 ...project(
153 "cluster-integration",
154 "packages/platform/node",
155 isNode && clusterTestsEnabled,
156 {
157 test: {
158 globalSetup: [
159 path.join(import.meta.dirname, "packages/platform/node/test/cluster-integration/globalSetup.ts")
160 ],
161 include: ["test/cluster-integration/**/*.test.ts"],
162 retry: 0,
163 sequence: {
164 concurrent: false
165 },
166 testTimeout: 60_000
167 }
168 },
169 exclude.filter((path) => path !== "**/test/cluster-integration/**"),
170 [
171 "test/cluster-integration/**/*.test.ts"
172 ]
173 ),
174 ...project("@effect/platform-node-shared", "packages/platform/node-shared", isNode),
175 ...project("@effect/vitest", "packages/vitest"),
176 ...project("@effect/sql-clickhouse", "packages/sql/clickhouse"),
177 ...project("@effect/sql-d1", "packages/sql/d1", !isDeno),
178 ...project("@effect/sql-libsql", "packages/sql/libsql"),
179 ...project("@effect/sql-mssql", "packages/sql/mssql"),
180 // MySQL starts a fresh container per integration test suite, so avoid competing
181 // with the other container-backed projects on integration-test runners.
182 ...project(
183 "@effect/sql-mysql2",
184 "packages/sql/mysql2",
185 true,
186 integrationTestsEnabled
187 ? {
188 test: {
189 fileParallelism: false,
190 sequence: {
191 groupOrder: 1
192 }
193 }
194 }
195 : {}
196 ),
197 ...project("@effect/sql-pg", "packages/sql/pg"),
198 ...project("@effect/sql-pglite", "packages/sql/pglite"),
199 ...project("@effect/sql-sqlite-bun", "packages/sql/sqlite-bun"),
200 ...project(
201 "@effect/sql-sqlite-do",
202 "packages/sql/sqlite-do",
203 true,
204 {},
205 // Miniflare needs Node/Bun; keep the in-process client tests on Deno.
206 isDeno ? [...exclude, "test/Miniflare.test.ts"] : undefined
207 ),
208 ...project("@effect/sql-sqlite-node", "packages/sql/sqlite-node", isNode),
209 ...project("@effect/sql-sqlite-react-native", "packages/sql/sqlite-react-native"),
210 ...project("@effect/sql-sqlite-wasm", "packages/sql/sqlite-wasm"),
211 ...project("@effect/ai-codegen", "packages/tools/ai-codegen", isNode),
212 ...project("@effect/api-diff", "packages/tools/api-diff"),
213 ...project("@effect/bundle", "packages/tools/bundle"),
214 ...project("@effect/doctest", "packages/tools/doctest"),
215 ...project("@effect/docgen", "packages/tools/docgen"),
216 ...project("@effect/jsdocs", "packages/tools/jsdocs"),
217 ...project("@effect/openapi-generator", "packages/tools/openapi-generator"),
218 ...project("@effect/utils", "packages/tools/utils", isNode),
219 ...project("@effect/oxc", "packages/tools/oxc")
220 ]
221 }
222})