@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 +238 -0
- package/config/oxfmt.json +10 -0
- package/config/oxlint.json +27 -0
- package/config/tsconfig.json +40 -0
- package/config/vite-aliases.ts +87 -0
- package/config/vitest.setup.ts +44 -0
- package/package.json +39 -0
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,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
|
+
}
|