@xmachines/shared 1.0.0-beta.5 → 1.0.0-beta.7
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 +138 -7
- package/config/vite-aliases.ts +0 -3
- package/config/vitest.node.setup.ts +15 -0
- package/config/vitest.setup.ts +4 -35
- package/config/vitest.ts +87 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Centralized config enabling composite build system with project references.
|
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
`@xmachines/shared` provides shared configuration files for all XMachines packages, establishing consistent TypeScript compilation, linting rules, and
|
|
9
|
+
`@xmachines/shared` provides shared configuration files for all XMachines packages, establishing consistent TypeScript compilation, linting rules, formatting, and Vitest setup across the monorepo.
|
|
10
10
|
|
|
11
11
|
**Enhancement:** Added TypeScript composite build system with project references enabling correct build order, incremental compilation, and IDE source navigation via declaration maps.
|
|
12
12
|
|
|
@@ -21,6 +21,10 @@ Configuration files in `config/` directory:
|
|
|
21
21
|
- **`config/tsconfig.json`** - TypeScript compiler settings with composite project support
|
|
22
22
|
- **`config/oxlint.json`** - Linting rules and plugins
|
|
23
23
|
- **`config/oxfmt.json`** - Code formatting preferences
|
|
24
|
+
- **`config/vitest.ts`** - Shared Vitest config helper (`defineXmVitestConfig`)
|
|
25
|
+
- **`config/vitest.setup.ts`** - Shared cross-runtime Vitest setup (polyfills, jest-dom matchers)
|
|
26
|
+
- **`config/vitest.node.setup.ts`** - Node-only Vitest setup (Node runtime/version preflight)
|
|
27
|
+
- **`config/vite-aliases.ts`** - Workspace alias mapping for `@xmachines/*`
|
|
24
28
|
|
|
25
29
|
## Usage
|
|
26
30
|
|
|
@@ -74,6 +78,93 @@ Configuration files in `config/` directory:
|
|
|
74
78
|
}
|
|
75
79
|
```
|
|
76
80
|
|
|
81
|
+
### Vite Alias Configuration
|
|
82
|
+
|
|
83
|
+
Use the shared alias helper in Vite and Vitest configs so workspace packages resolve to source files without requiring `dist/` builds:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { defineConfig } from "vite";
|
|
87
|
+
import { xmAliases } from "@xmachines/shared/vite-aliases";
|
|
88
|
+
|
|
89
|
+
export default defineConfig({
|
|
90
|
+
resolve: {
|
|
91
|
+
alias: xmAliases(import.meta.url),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This is especially important after `npm run clean`, where package `exports` targeting `dist/` are temporarily unavailable until rebuild.
|
|
97
|
+
|
|
98
|
+
### Vitest Configuration
|
|
99
|
+
|
|
100
|
+
Use the shared helper so package configs inherit common aliasing and runtime setup:
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { defineXmVitestConfig } from "@xmachines/shared/vitest";
|
|
104
|
+
|
|
105
|
+
export default defineXmVitestConfig(import.meta.url, {
|
|
106
|
+
test: {
|
|
107
|
+
environment: "node",
|
|
108
|
+
include: ["test/**/*.test.ts"],
|
|
109
|
+
exclude: ["node_modules/**"],
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Browser project example:**
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { defineXmVitestConfig } from "@xmachines/shared/vitest";
|
|
118
|
+
import { playwright } from "@vitest/browser-playwright";
|
|
119
|
+
|
|
120
|
+
export default defineXmVitestConfig(import.meta.url, {
|
|
121
|
+
test: {
|
|
122
|
+
name: "my-demo-browser",
|
|
123
|
+
browser: {
|
|
124
|
+
enabled: true,
|
|
125
|
+
provider: playwright(),
|
|
126
|
+
headless: true,
|
|
127
|
+
instances: [{ browser: "chromium" }],
|
|
128
|
+
},
|
|
129
|
+
include: ["test/browser/**/*.browser.test.ts"],
|
|
130
|
+
exclude: ["node_modules/**"],
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
What the shared Vitest layer provides automatically:
|
|
136
|
+
|
|
137
|
+
- `resolve.alias` for all `@xmachines/*` packages via `xmAliases(...)`
|
|
138
|
+
- Shared setup injection (`config/vitest.setup.ts`) unless already present
|
|
139
|
+
- Node setup injection (`config/vitest.node.setup.ts`) for non-browser projects
|
|
140
|
+
- Runtime preflight:
|
|
141
|
+
- Node.js runtime/version check (`>=22`) in node setup
|
|
142
|
+
- `urlpattern-polyfill` load in shared setup
|
|
143
|
+
- `URLPattern` availability assertion in shared setup
|
|
144
|
+
- DOM matcher extension via `@testing-library/jest-dom/vitest`
|
|
145
|
+
|
|
146
|
+
### Vitest Setup Injection Rules
|
|
147
|
+
|
|
148
|
+
`defineXmVitestConfig(...)` applies setup files predictably:
|
|
149
|
+
|
|
150
|
+
1. It normalizes `test.setupFiles` into an array.
|
|
151
|
+
2. It injects `config/vitest.setup.ts` if missing.
|
|
152
|
+
3. It injects `config/vitest.node.setup.ts` only when `test.browser.enabled !== true`.
|
|
153
|
+
4. It avoids duplicates by checking filenames (suffix match).
|
|
154
|
+
5. Injected setup files run before user-provided setup files.
|
|
155
|
+
|
|
156
|
+
This gives a safe default for Node package tests while keeping browser projects compatible.
|
|
157
|
+
|
|
158
|
+
### Vitest Exports
|
|
159
|
+
|
|
160
|
+
`@xmachines/shared` exports these Vitest entry points:
|
|
161
|
+
|
|
162
|
+
- `@xmachines/shared/vitest` → `config/vitest.ts`
|
|
163
|
+
- `@xmachines/shared/vitest-setup` → `config/vitest.setup.ts`
|
|
164
|
+
- `@xmachines/shared/vitest-node-setup` → `config/vitest.node.setup.ts`
|
|
165
|
+
|
|
166
|
+
Use `@xmachines/shared/vitest` for package config files; direct setup exports are for advanced customization only.
|
|
167
|
+
|
|
77
168
|
## Configuration Details
|
|
78
169
|
|
|
79
170
|
### TypeScript (`config/tsconfig.json`)
|
|
@@ -107,6 +198,48 @@ import { foo } from "./bar"; // ❌ Wrong
|
|
|
107
198
|
- **Style:** Double quotes, semicolons, trailing commas
|
|
108
199
|
- **Final Newline:** Always insert
|
|
109
200
|
|
|
201
|
+
### Vitest (`config/vitest.ts`, `config/vitest.setup.ts`, `config/vitest.node.setup.ts`)
|
|
202
|
+
|
|
203
|
+
- **Helper API:** `defineXmVitestConfig(importMetaUrl, overrides)` merges shared defaults with package-specific Vitest config
|
|
204
|
+
- **Auto-aliasing:** `resolve.alias` is wired to `xmAliases(importMetaUrl)` so workspace packages resolve to source
|
|
205
|
+
- **Cross-runtime setup (`config/vitest.setup.ts`):**
|
|
206
|
+
- loads `urlpattern-polyfill`
|
|
207
|
+
- extends `expect` with `@testing-library/jest-dom/vitest`
|
|
208
|
+
- asserts `URLPattern` availability
|
|
209
|
+
- **Node-only setup (`config/vitest.node.setup.ts`):**
|
|
210
|
+
- asserts Node runtime (`process.release.name === "node"`)
|
|
211
|
+
- enforces Node version `>=22`
|
|
212
|
+
- **Browser behavior:** Browser projects do not receive node-only setup injection
|
|
213
|
+
- **Override model:** Package-level config still controls test environment, includes/excludes, plugins, coverage, and browser provider
|
|
214
|
+
|
|
215
|
+
### Vite Aliases (`config/vite-aliases.ts`)
|
|
216
|
+
|
|
217
|
+
- **Helper API:** `xmAliases(importMetaUrl)` returns a complete `resolve.alias` map for all `@xmachines/*` workspace packages
|
|
218
|
+
- **Root discovery:** Walks upward from `import.meta.url` until it finds the workspace root (`package.json` with `workspaces`)
|
|
219
|
+
- **Source-first resolution:** Maps package imports to `packages/*/src/index.ts` for fast local dev without prebuild
|
|
220
|
+
- **Extra aliases:** Includes `@xmachines/play-router-shared` and `@xmachines/play-router-shared/index.css` for demo shared assets
|
|
221
|
+
- **Primary use cases:** `vite.config.ts`, `vitest` browser configs, and any local tooling relying on Vite resolver semantics
|
|
222
|
+
|
|
223
|
+
### Vite Alias Troubleshooting
|
|
224
|
+
|
|
225
|
+
- **`[xmAliases] Could not find workspace root`**
|
|
226
|
+
- Ensure `xmAliases` receives `import.meta.url` from the calling config file.
|
|
227
|
+
- Ensure the config file lives somewhere under the monorepo root.
|
|
228
|
+
- **Workspace package import resolves to missing `dist/` files**
|
|
229
|
+
- Ensure config includes `resolve.alias: xmAliases(import.meta.url)`.
|
|
230
|
+
- Re-run after cleaning caches when lockfile/deps changed.
|
|
231
|
+
|
|
232
|
+
### Vitest Troubleshooting
|
|
233
|
+
|
|
234
|
+
- **`URLPattern is unavailable after setup`**
|
|
235
|
+
- Ensure `test.setupFiles` is not replacing shared setup unexpectedly.
|
|
236
|
+
- Ensure config is created via `defineXmVitestConfig(...)`.
|
|
237
|
+
- **`Node runtime is required for this Vitest configuration`**
|
|
238
|
+
- This indicates node-only setup loaded in a browser project.
|
|
239
|
+
- Set `test.browser.enabled: true` in that project config.
|
|
240
|
+
- **Workspace import resolution failures (e.g. `@xmachines/play-signals`)**
|
|
241
|
+
- Confirm config uses `defineXmVitestConfig(import.meta.url, ...)` so aliases are injected.
|
|
242
|
+
|
|
110
243
|
## TypeScript Composite Build System
|
|
111
244
|
|
|
112
245
|
This package introduced TypeScript project references for proper build-order management in the monorepo.
|
|
@@ -228,11 +361,9 @@ To add a new shared configuration (e.g., for Jest, Vitest, etc.):
|
|
|
228
361
|
3. Document in this README with usage examples
|
|
229
362
|
4. Note whether the tool supports package exports or requires relative paths
|
|
230
363
|
|
|
231
|
-
## Links
|
|
232
|
-
|
|
233
|
-
- [XMachines Monorepo](../../README.md)
|
|
234
|
-
- Agent Guidelines: `AGENTS.md`
|
|
235
|
-
|
|
236
364
|
## License
|
|
237
365
|
|
|
238
|
-
|
|
366
|
+
Copyright (c) 2016 [Mikael Karon](mailto:mikael@karon.se). All rights reserved.
|
|
367
|
+
|
|
368
|
+
This work is licensed under the terms of the MIT license.
|
|
369
|
+
For a copy, see <https://opensource.org/licenses/MIT>.
|
package/config/vite-aliases.ts
CHANGED
|
@@ -19,9 +19,6 @@ import { fileURLToPath } from "url";
|
|
|
19
19
|
*
|
|
20
20
|
* @param importMetaUrl - Pass `import.meta.url` from the calling config file.
|
|
21
21
|
* Used to locate the workspace root (the directory containing `packages/`).
|
|
22
|
-
* @internal
|
|
23
|
-
* @hidden
|
|
24
|
-
* @ignore
|
|
25
22
|
*/
|
|
26
23
|
export function xmAliases(importMetaUrl: string): Record<string, string> {
|
|
27
24
|
const configDir = fileURLToPath(new URL(".", importMetaUrl));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const isNodeRuntime =
|
|
2
|
+
typeof process !== "undefined" &&
|
|
3
|
+
process.release?.name === "node" &&
|
|
4
|
+
typeof process.versions?.node === "string";
|
|
5
|
+
|
|
6
|
+
if (!isNodeRuntime) {
|
|
7
|
+
throw new Error("Node runtime is required for this Vitest configuration");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const [nodeMajorRaw] = process.versions.node.split(".");
|
|
11
|
+
const nodeMajor = Number.parseInt(nodeMajorRaw ?? "0", 10);
|
|
12
|
+
|
|
13
|
+
if (!Number.isFinite(nodeMajor) || nodeMajor < 22) {
|
|
14
|
+
throw new Error(`Vitest runtime requires Node.js >=22.0.0, received ${process.versions.node}`);
|
|
15
|
+
}
|
package/config/vitest.setup.ts
CHANGED
|
@@ -4,41 +4,10 @@
|
|
|
4
4
|
* @internal
|
|
5
5
|
*/
|
|
6
6
|
// eslint-disable-next-line import/no-unassigned-import
|
|
7
|
-
import "@testing-library/jest-dom/vitest";
|
|
8
|
-
// eslint-disable-next-line import/no-unassigned-import
|
|
9
7
|
import "urlpattern-polyfill";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (!(globalThis as { URLPattern?: unknown }).URLPattern) {
|
|
13
|
-
throw new Error("URLPattern polyfill failed to load in Vitest setup");
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
let originalEmit: typeof process.emit | undefined;
|
|
17
|
-
|
|
18
|
-
if (typeof process !== "undefined") {
|
|
19
|
-
type ProcessWithEmit = NodeJS.Process & {
|
|
20
|
-
emit: (event: string, error: unknown, ...args: unknown[]) => boolean;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
originalEmit = process.emit.bind(process);
|
|
24
|
-
const processWithEmit = process as ProcessWithEmit;
|
|
25
|
-
|
|
26
|
-
processWithEmit.emit = function (event: string, error: unknown, ...args: unknown[]) {
|
|
27
|
-
if (
|
|
28
|
-
event === "uncaughtException" &&
|
|
29
|
-
error instanceof Error &&
|
|
30
|
-
(error.message.includes("Unable to evaluate guard") ||
|
|
31
|
-
error.message.includes("Cannot read properties of null"))
|
|
32
|
-
) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
8
|
+
// eslint-disable-next-line import/no-unassigned-import
|
|
9
|
+
import "@testing-library/jest-dom/vitest";
|
|
35
10
|
|
|
36
|
-
|
|
37
|
-
|
|
11
|
+
if (typeof (globalThis as { URLPattern?: unknown }).URLPattern !== "function") {
|
|
12
|
+
throw new Error("URLPattern is unavailable after setup");
|
|
38
13
|
}
|
|
39
|
-
|
|
40
|
-
afterAll(() => {
|
|
41
|
-
if (typeof process !== "undefined" && originalEmit) {
|
|
42
|
-
process.emit = originalEmit;
|
|
43
|
-
}
|
|
44
|
-
});
|
package/config/vitest.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { dirname, resolve } from "node:path";
|
|
3
|
+
import { defineConfig, mergeConfig } from "vitest/config";
|
|
4
|
+
import { xmAliases } from "./vite-aliases.js";
|
|
5
|
+
|
|
6
|
+
const configDir = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const nodeSetupFile = resolve(configDir, "vitest.node.setup.ts");
|
|
8
|
+
const setupFile = resolve(configDir, "vitest.setup.ts");
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Normalize Vitest `test.setupFiles` into a string array.
|
|
12
|
+
*/
|
|
13
|
+
function normalizeSetupFiles(setupFiles: unknown): string[] {
|
|
14
|
+
if (!setupFiles) return [];
|
|
15
|
+
if (Array.isArray(setupFiles)) {
|
|
16
|
+
return setupFiles.filter((entry): entry is string => typeof entry === "string");
|
|
17
|
+
}
|
|
18
|
+
return typeof setupFiles === "string" ? [setupFiles] : [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* True when Vitest browser mode is explicitly enabled.
|
|
23
|
+
*/
|
|
24
|
+
function isBrowserEnabled(browserConfig: unknown): boolean {
|
|
25
|
+
if (!browserConfig || typeof browserConfig !== "object") return false;
|
|
26
|
+
return (browserConfig as { enabled?: unknown }).enabled === true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Checks whether setup files already include a file by suffix.
|
|
31
|
+
*
|
|
32
|
+
* This supports both absolute and relative setup file entries and normalizes
|
|
33
|
+
* Windows separators for cross-platform matching.
|
|
34
|
+
*/
|
|
35
|
+
function hasSetupFile(setupFiles: string[], fileName: string): boolean {
|
|
36
|
+
const normalizedSuffix = `/${fileName}`;
|
|
37
|
+
return setupFiles.some((entry) => entry.replaceAll("\\", "/").endsWith(normalizedSuffix));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a Vitest config with XMachines workspace defaults.
|
|
42
|
+
*
|
|
43
|
+
* What this applies automatically:
|
|
44
|
+
* - `resolve.alias` from `xmAliases(importMetaUrl)` so `@xmachines/*` imports
|
|
45
|
+
* resolve to source in local/dev test runs.
|
|
46
|
+
* - `config/vitest.setup.ts` when missing.
|
|
47
|
+
* - `config/vitest.node.setup.ts` for non-browser projects when missing.
|
|
48
|
+
*
|
|
49
|
+
* Setup injection is additive and preserves caller-provided ordering after
|
|
50
|
+
* required shared setup files.
|
|
51
|
+
*
|
|
52
|
+
* @param importMetaUrl Pass `import.meta.url` from the calling config file.
|
|
53
|
+
* @param overrides Package/project-specific Vitest config overrides.
|
|
54
|
+
* @returns Merged Vitest configuration object.
|
|
55
|
+
*/
|
|
56
|
+
export function defineXmVitestConfig(importMetaUrl: string, overrides: Record<string, unknown>) {
|
|
57
|
+
const merged = mergeConfig(
|
|
58
|
+
defineConfig({
|
|
59
|
+
resolve: {
|
|
60
|
+
alias: xmAliases(importMetaUrl),
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
defineConfig(overrides),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const testConfig = merged.test ?? {};
|
|
67
|
+
const setupFiles = normalizeSetupFiles(testConfig.setupFiles);
|
|
68
|
+
const browserEnabled = isBrowserEnabled(testConfig.browser);
|
|
69
|
+
const hasNodeSetup = hasSetupFile(setupFiles, "vitest.node.setup.ts");
|
|
70
|
+
const hasSharedSetup = hasSetupFile(setupFiles, "vitest.setup.ts");
|
|
71
|
+
const injectedSetupFiles: string[] = [];
|
|
72
|
+
|
|
73
|
+
if (!browserEnabled && !hasNodeSetup) {
|
|
74
|
+
injectedSetupFiles.push(nodeSetupFile);
|
|
75
|
+
}
|
|
76
|
+
if (!hasSharedSetup) {
|
|
77
|
+
injectedSetupFiles.push(setupFile);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
...merged,
|
|
82
|
+
test: {
|
|
83
|
+
...testConfig,
|
|
84
|
+
setupFiles: [...injectedSetupFiles, ...setupFiles],
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xmachines/shared",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.7",
|
|
4
4
|
"description": "Shared configurations for XMachines packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"config",
|
|
@@ -25,10 +25,14 @@
|
|
|
25
25
|
"./oxlint": "./config/oxlint.json",
|
|
26
26
|
"./oxfmt": "./config/oxfmt.json",
|
|
27
27
|
"./vite-aliases": "./config/vite-aliases.ts",
|
|
28
|
+
"./vitest": "./config/vitest.ts",
|
|
29
|
+
"./vitest-node-setup": "./config/vitest.node.setup.ts",
|
|
28
30
|
"./vitest-setup": "./config/vitest.setup.ts",
|
|
29
31
|
"./config/tsconfig.json": "./config/tsconfig.json",
|
|
30
32
|
"./config/oxlint.json": "./config/oxlint.json",
|
|
31
33
|
"./config/oxfmt.json": "./config/oxfmt.json",
|
|
34
|
+
"./config/vitest.ts": "./config/vitest.ts",
|
|
35
|
+
"./config/vitest.node.setup.ts": "./config/vitest.node.setup.ts",
|
|
32
36
|
"./config/vitest.setup.ts": "./config/vitest.setup.ts"
|
|
33
37
|
},
|
|
34
38
|
"publishConfig": {
|