@xmachines/shared 1.0.0-beta.4

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 ADDED
@@ -0,0 +1,238 @@
1
+ # @xmachines/shared
2
+
3
+ **Shared TypeScript, linting, and formatting configurations for XMachines monorepo**
4
+
5
+ Centralized config enabling composite build system with project references.
6
+
7
+ ## Overview
8
+
9
+ `@xmachines/shared` provides shared configuration files for all XMachines packages, establishing consistent TypeScript compilation, linting rules, and code formatting across the monorepo.
10
+
11
+ **Enhancement:** Added TypeScript composite build system with project references enabling correct build order, incremental compilation, and IDE source navigation via declaration maps.
12
+
13
+ ## Installation
14
+
15
+ This package is automatically available to all workspace packages via npm workspaces.
16
+
17
+ ## Contents
18
+
19
+ Configuration files in `config/` directory:
20
+
21
+ - **`config/tsconfig.json`** - TypeScript compiler settings with composite project support
22
+ - **`config/oxlint.json`** - Linting rules and plugins
23
+ - **`config/oxfmt.json`** - Code formatting preferences
24
+
25
+ ## Usage
26
+
27
+ ### TypeScript Configuration
28
+
29
+ **Recommended (short alias):**
30
+
31
+ ```json
32
+ {
33
+ "extends": "@xmachines/shared/tsconfig",
34
+ "compilerOptions": {
35
+ "composite": true,
36
+ "rootDir": "./src",
37
+ "outDir": "./dist"
38
+ }
39
+ }
40
+ ```
41
+
42
+ **Alternative (full path):**
43
+
44
+ ```json
45
+ {
46
+ "extends": "@xmachines/shared/config/tsconfig.json",
47
+ "compilerOptions": {
48
+ "composite": true,
49
+ "rootDir": "./src",
50
+ "outDir": "./dist"
51
+ }
52
+ }
53
+ ```
54
+
55
+ > **Note:** Both import styles work. The short alias is recommended for cleaner config files.
56
+
57
+ ### Oxlint Configuration
58
+
59
+ **Recommended:**
60
+
61
+ ```json
62
+ {
63
+ "extends": ["@xmachines/shared/oxlint"]
64
+ }
65
+ ```
66
+
67
+ ### Oxfmt Configuration
68
+
69
+ **Recommended:**
70
+
71
+ ```json
72
+ {
73
+ "extends": ["@xmachines/shared/oxfmt"]
74
+ }
75
+ ```
76
+
77
+ ## Configuration Details
78
+
79
+ ### TypeScript (`config/tsconfig.json`)
80
+
81
+ - **Target:** ESNext (Node.js 22+)
82
+ - **Module:** NodeNext (ESM with `.js` extensions required)
83
+ - **Strict:** Full strict mode enabled
84
+ - **Output:** Colocated `.d.ts` files with source maps
85
+ - **Composite:** Enabled for project references ()
86
+
87
+ **Important:** You must use `.js` extensions in imports:
88
+
89
+ ```typescript
90
+ import { foo } from "./bar.js"; // ✅ Correct
91
+ import { foo } from "./bar"; // ❌ Wrong
92
+ ```
93
+
94
+ ### Oxlint (`config/oxlint.json`)
95
+
96
+ - **Plugins:** TypeScript, Unicorn, Import
97
+ - **Categories:** Correctness (error), Suspicious (warn), Performance (warn)
98
+ - **Key Rules:**
99
+ - Circular dependency detection (`import/no-cycle`)
100
+ - Unused variables (allow `_` prefix)
101
+ - TypeScript-specific checks
102
+
103
+ ### Oxfmt (`config/oxfmt.json`)
104
+
105
+ - **Line Width:** 100 characters
106
+ - **Indentation:** Tabs (4 spaces)
107
+ - **Style:** Double quotes, semicolons, trailing commas
108
+ - **Final Newline:** Always insert
109
+
110
+ ## TypeScript Composite Build System
111
+
112
+ This package introduced TypeScript project references for proper build-order management in the monorepo.
113
+
114
+ ### What This Means
115
+
116
+ - **Root `tsconfig.json`** coordinates all packages via `references` array
117
+ - **Each package** has `composite: true` enabling incremental builds
118
+ - **Packages with dependencies** declare `references` to their deps
119
+ - **TypeScript builds in correct order automatically** (no manual sequencing needed)
120
+
121
+ ### Building
122
+
123
+ ```bash
124
+ npm run build # Root-level tsc --build handles everything
125
+ npm run build -w <pkg> # Automatically builds dependencies first
126
+ ```
127
+
128
+ The root build command uses `tsc --build` which:
129
+
130
+ - Understands the dependency graph
131
+ - Builds packages in correct order
132
+ - Only rebuilds changed packages (incremental)
133
+ - Produces `.d.ts.map` files for IDE navigation
134
+
135
+ ### Declaration Maps
136
+
137
+ The base config enables **declaration maps** (`declarationMap: true`) for better IDE experience in composite projects.
138
+
139
+ When TypeScript compiles with `declarationMap: true`, it produces `.d.ts.map` files that map type definitions back to source. This enables:
140
+
141
+ - **Go to Definition** jumps to `.ts` source files (not compiled `.d.ts`)
142
+ - **Rename refactoring** works across package boundaries
143
+ - **Error messages** reference source locations
144
+
145
+ **Cost:** Slightly larger `dist/` output (~10% more)
146
+ **Benefit:** Dramatically better IDE experience
147
+
148
+ ### Project References
149
+
150
+ Packages with internal dependencies should declare `references` in their `tsconfig.json`:
151
+
152
+ ```json
153
+ {
154
+ "extends": "@xmachines/shared/tsconfig",
155
+ "compilerOptions": {
156
+ "composite": true,
157
+ "rootDir": "./src",
158
+ "outDir": "./dist"
159
+ },
160
+ "references": [{ "path": "../dependency-package" }]
161
+ }
162
+ ```
163
+
164
+ The root `tsconfig.json` maintains the full package reference graph, enabling TypeScript to:
165
+
166
+ - Build packages in correct dependency order
167
+ - Support incremental builds (only rebuild what changed)
168
+ - Enable cross-package type checking and navigation
169
+
170
+ ### Adding New Packages
171
+
172
+ When creating a new package:
173
+
174
+ 1. **Add to root tsconfig.json references:**
175
+
176
+ ```json
177
+ {
178
+ "references": [{ "path": "./packages/your-new-package" }]
179
+ }
180
+ ```
181
+
182
+ 2. **Enable composite in package tsconfig.json:**
183
+
184
+ ```json
185
+ {
186
+ "extends": "@xmachines/shared/tsconfig",
187
+ "compilerOptions": {
188
+ "composite": true,
189
+ "rootDir": "./src",
190
+ "outDir": "./dist"
191
+ }
192
+ }
193
+ ```
194
+
195
+ 3. **Declare dependencies (if any):**
196
+ ```json
197
+ {
198
+ "references": [{ "path": "../dependency-package" }]
199
+ }
200
+ ```
201
+
202
+ ### IDE Benefits
203
+
204
+ With `declarationMap: true` in the base config:
205
+
206
+ - **Go to Definition** jumps to source files (not compiled `.d.ts`)
207
+ - **Real-time errors** across package boundaries
208
+ - **Refactoring works** across the entire monorepo
209
+
210
+ No build required for IDE features - TypeScript understands the source graph directly.
211
+
212
+ See `AGENTS.md` (section: TypeScript Composite Build System) for complete composite build documentation.
213
+
214
+ ## Adding New Configurations
215
+
216
+ To add a new shared configuration (e.g., for Jest, Vitest, etc.):
217
+
218
+ 1. Create `config/<tool>.json` (or `.js`, `.ts` as appropriate)
219
+ 2. Add exports to `package.json` exports field:
220
+ ```json
221
+ {
222
+ "exports": {
223
+ "./<tool>": "./config/<tool>.json",
224
+ "./config/<tool>.json": "./config/<tool>.json"
225
+ }
226
+ }
227
+ ```
228
+ 3. Document in this README with usage examples
229
+ 4. Note whether the tool supports package exports or requires relative paths
230
+
231
+ ## Links
232
+
233
+ - [XMachines Monorepo](../../README.md)
234
+ - Agent Guidelines: `AGENTS.md`
235
+
236
+ ## License
237
+
238
+ MIT
@@ -0,0 +1,10 @@
1
+ {
2
+ "$schema": "../../../node_modules/oxfmt/configuration_schema.json",
3
+ "printWidth": 100,
4
+ "tabWidth": 4,
5
+ "useTabs": true,
6
+ "semi": true,
7
+ "singleQuote": false,
8
+ "trailingComma": "all",
9
+ "insertFinalNewline": true
10
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "../../../node_modules/oxlint/configuration_schema.json",
3
+ "plugins": ["typescript", "unicorn", "import"],
4
+ "categories": {
5
+ "correctness": "error",
6
+ "suspicious": "warn",
7
+ "pedantic": "off",
8
+ "perf": "warn",
9
+ "style": "off"
10
+ },
11
+ "rules": {
12
+ "unicorn/filename-case": "off",
13
+ "import/no-cycle": "error",
14
+ "typescript/no-explicit-any": "warn",
15
+ "typescript/no-unused-vars": [
16
+ "error",
17
+ {
18
+ "argsIgnorePattern": "^_",
19
+ "varsIgnorePattern": "^_"
20
+ }
21
+ ]
22
+ },
23
+ "env": {
24
+ "es2025": true,
25
+ "node": true
26
+ }
27
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "compilerOptions": {
4
+ /* Language and Environment */
5
+ "target": "ESNext",
6
+ "lib": ["ESNext"],
7
+
8
+ /* Modules */
9
+ "module": "NodeNext",
10
+ "moduleResolution": "NodeNext",
11
+
12
+ /* Type Checking */
13
+ "strict": true,
14
+ "noUnusedLocals": true,
15
+ "noUnusedParameters": true,
16
+ "noFallthroughCasesInSwitch": true,
17
+ "noImplicitReturns": true,
18
+ "noImplicitOverride": true,
19
+ "exactOptionalPropertyTypes": true,
20
+
21
+ /* Emit */
22
+ "declaration": true,
23
+ "declarationMap": true,
24
+ "sourceMap": true,
25
+ "removeComments": false,
26
+ "importHelpers": false,
27
+
28
+ /* Interop Constraints */
29
+ "esModuleInterop": true,
30
+ "allowSyntheticDefaultImports": true,
31
+ "forceConsistentCasingInFileNames": true,
32
+ "isolatedModules": true,
33
+ "verbatimModuleSyntax": true,
34
+
35
+ /* Completeness */
36
+ "skipLibCheck": true
37
+ },
38
+ "include": ["src/**/*"],
39
+ "exclude": ["dist", "node_modules", "**/*.spec.ts", "**/*.test.ts"]
40
+ }
@@ -0,0 +1,87 @@
1
+ import { resolve } from "path";
2
+ import { fileURLToPath } from "url";
3
+
4
+ /**
5
+ * Vite resolve.alias entries for all @xmachines/* workspace packages.
6
+ *
7
+ * Maps every package to its TypeScript source entry so that `npm run dev`
8
+ * works without a prior `npm run build`. Required because `npm run clean`
9
+ * removes all dist/ directories — including transitive dependencies —
10
+ * leaving Vite unable to resolve package.json `exports` that point to dist/.
11
+ *
12
+ * Usage in a demo vite.config.ts:
13
+ *
14
+ * import { xmAliases } from "@xmachines/shared/vite-aliases";
15
+ *
16
+ * export default defineConfig({
17
+ * resolve: { alias: xmAliases(import.meta.url) },
18
+ * });
19
+ *
20
+ * @param importMetaUrl - Pass `import.meta.url` from the calling config file.
21
+ * Used to locate the workspace root (the directory containing `packages/`).
22
+ * @internal
23
+ * @hidden
24
+ * @ignore
25
+ */
26
+ export function xmAliases(importMetaUrl: string): Record<string, string> {
27
+ const configDir = fileURLToPath(new URL(".", importMetaUrl));
28
+
29
+ // Walk up from the config file's directory until we find the folder that
30
+ // contains a `packages/` subdirectory — that's the workspace root.
31
+ let root = configDir;
32
+ while (!isWorkspaceRoot(root)) {
33
+ const parent = resolve(root, "..");
34
+ if (parent === root)
35
+ throw new Error(`[xmAliases] Could not find workspace root from ${configDir}`);
36
+ root = parent;
37
+ }
38
+
39
+ const p = (pkg: string) => resolve(root, "packages", pkg, "src/index.ts");
40
+
41
+ return {
42
+ "@xmachines/play": p("play"),
43
+ "@xmachines/play-actor": p("play-actor"),
44
+ "@xmachines/play-catalog": p("play-catalog"),
45
+ "@xmachines/play-signals": p("play-signals"),
46
+ "@xmachines/play-router": p("play-router"),
47
+ "@xmachines/play-xstate": p("play-xstate"),
48
+ "@xmachines/play-react": p("play-react"),
49
+ "@xmachines/play-react-router": p("play-react-router"),
50
+ "@xmachines/play-solid": p("play-solid"),
51
+ "@xmachines/play-solid-router": p("play-solid-router"),
52
+ "@xmachines/play-vue": p("play-vue"),
53
+ "@xmachines/play-vue-router": p("play-vue-router"),
54
+ "@xmachines/play-tanstack-react-router": p("play-tanstack-react-router"),
55
+ "@xmachines/play-tanstack-solid-router": p("play-tanstack-solid-router"),
56
+ "@xmachines/play-router-shared/index.css": resolve(
57
+ root,
58
+ "packages",
59
+ "play-router",
60
+ "examples",
61
+ "shared",
62
+ "src/index.css",
63
+ ),
64
+ "@xmachines/play-router-shared": resolve(
65
+ root,
66
+ "packages",
67
+ "play-router",
68
+ "examples",
69
+ "shared",
70
+ "src",
71
+ ),
72
+ };
73
+ }
74
+
75
+ import { existsSync, readFileSync } from "fs";
76
+
77
+ function isWorkspaceRoot(dir: string): boolean {
78
+ const pkgPath = resolve(dir, "package.json");
79
+ if (!existsSync(pkgPath)) return false;
80
+ try {
81
+ const raw = readFileSync(pkgPath, "utf8");
82
+ const pkg = JSON.parse(raw) as { workspaces?: unknown };
83
+ return Array.isArray(pkg.workspaces);
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Vitest setup shared across packages.
3
+ * Extends matchers with @testing-library/jest-dom.
4
+ * @internal
5
+ */
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
+ import "urlpattern-polyfill";
10
+ import { afterAll } from "vitest";
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
+ }
35
+
36
+ return originalEmit!(event, error, ...args);
37
+ };
38
+ }
39
+
40
+ afterAll(() => {
41
+ if (typeof process !== "undefined" && originalEmit) {
42
+ process.emit = originalEmit;
43
+ }
44
+ });
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@xmachines/shared",
3
+ "version": "1.0.0-beta.4",
4
+ "description": "Shared configurations for XMachines packages",
5
+ "keywords": [
6
+ "config",
7
+ "shared",
8
+ "xmachines"
9
+ ],
10
+ "license": "MIT",
11
+ "author": "Mikael Karon <mikael@karon.se>",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+ssh://git@gitlab.com/xmachin-es/xmachines-js.git",
15
+ "directory": "packages/shared"
16
+ },
17
+ "files": [
18
+ "config",
19
+ "README.md"
20
+ ],
21
+ "type": "module",
22
+ "exports": {
23
+ "./tsconfig": "./config/tsconfig.json",
24
+ "./oxlint": "./config/oxlint.json",
25
+ "./oxfmt": "./config/oxfmt.json",
26
+ "./vite-aliases": "./config/vite-aliases.ts",
27
+ "./vitest-setup": "./config/vitest.setup.ts",
28
+ "./config/tsconfig.json": "./config/tsconfig.json",
29
+ "./config/oxlint.json": "./config/oxlint.json",
30
+ "./config/oxfmt.json": "./config/oxfmt.json",
31
+ "./config/vitest.setup.ts": "./config/vitest.setup.ts"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "dependencies": {
37
+ "@testing-library/jest-dom": "^6.9.1"
38
+ }
39
+ }