@raypx-kit/devkit 0.0.0-alpha.0 → 0.0.0-alpha.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,28 @@
1
1
  # @raypx-kit/devkit
2
2
 
3
+ ## 0.0.0-alpha.2
4
+
5
+ ### Minor Changes
6
+
7
+ - 5515e5f: Support Bun-only repository toolchain checks and active runtime validation without
8
+ requiring Node pins. Preserve the existing Node/Bun defaults. Export independent
9
+ Vite+ lint presets and plain Bun test defaults for consumer-owned configuration.
10
+
11
+ ## 0.0.0-alpha.1
12
+
13
+ ### Major Changes
14
+
15
+ - a6af9fe: Raise the minimum supported Node.js version from 22.14.0 to 22.21.0. Update
16
+ package engine declarations and library build targets to the new floor, and
17
+ verify installed consumers on Node 22.21.0. Upgrade older Node installations
18
+ before installing these releases. The repository development pin remains 24.22.0.
19
+
20
+ ### Patch Changes
21
+
22
+ - a6af9fe: Allow apps and tooling to import explicitly bundled internal workspaces declared in development dependencies. Keep public export boundaries, platform compatibility, application restrictions and dependency cycle checks in force.
23
+ - 53ce687: Update compatible PostHog and dotenvx dependencies and the bundled ICU message
24
+ parser, preserving existing public interfaces and peer compatibility.
25
+
3
26
  ## 0.0.0-alpha.0
4
27
 
5
28
  Initial Raypx Kit prerelease.
package/README.md CHANGED
@@ -3,12 +3,13 @@
3
3
  Maintenance checks and task helpers for Bun monorepos: workspace boundaries,
4
4
  toolchain pins, static message catalogs, documentation links and relocated build
5
5
  artifacts.
6
+ The package also exports plain Vite+ lint presets and Bun test defaults.
6
7
 
7
8
  ```sh
8
9
  bun add -d @raypx-kit/devkit
9
10
  ```
10
11
 
11
- ESM only, for Node 22.14+ and Bun. Each entry is independent, so a consumer loads
12
+ ESM only, for Node 22.21+ and Bun. Each entry is independent, so a consumer loads
12
13
  only the dependencies it uses. The package reads files and spawns processes; it
13
14
  contains no product rules, so repositories compose their own policy from it.
14
15
 
@@ -21,6 +22,8 @@ contains no product rules, so repositories compose their own policy from it.
21
22
  | `./docs` | Report broken relative Markdown links |
22
23
  | `./process` | Run commands, load dotenv files and run Turbo with a guarded cache |
23
24
  | `./artifact` | Stage a relocated output and probe a running Node server |
25
+ | `./lint` | Base and opt-in strict Vite+ lint presets without repository paths |
26
+ | `./testing` | Plain Vitest defaults for Bun workers |
24
27
 
25
28
  ## Workspace architecture
26
29
 
@@ -82,10 +85,11 @@ within its workspace and correspond to an actual export. Without a mapping,
82
85
  the check follows the declared export targets directly. Package-root JSON
83
86
  configuration exports therefore need no source mapping.
84
87
 
85
- `bundledDependencies` lists external development dependencies deliberately bundled
86
- into the published runtime. Each name must be declared in `devDependencies`, and
87
- the package build must bundle it. This option permits those source imports; it
88
- does not exempt workspace boundaries or platform checks. Ordinary runtime
88
+ `bundledDependencies` lists development dependencies deliberately bundled into
89
+ runtime by the package build. Each name must also be declared in
90
+ `devDependencies`. Internal workspace bundles are allowed only for apps and
91
+ tooling. Public export boundaries, platform compatibility, application and
92
+ tooling restrictions, and dependency cycle checks still apply. Ordinary runtime
89
93
  dependencies and peers remain in their respective manifest fields.
90
94
 
91
95
  Without an explicit `metadata` map, discovery retains the manifest interface.
@@ -146,6 +150,71 @@ documented `Node x.y.z` and `Bun x.y.z` versions and Turbo global dependencies.
146
150
  `pinnedNode` returns the Node executable after checking its version; the binary
147
151
  variable selects an executable but never relaxes the version.
148
152
 
153
+ ### Bun-only repositories
154
+
155
+ The default policy still requires Node and Bun. Repositories that run JavaScript
156
+ only with Bun can select that policy explicitly:
157
+
158
+ ```ts
159
+ import { assertBunRuntime, checkToolchain, readBunPin } from "@raypx-kit/devkit/toolchain";
160
+
161
+ const findings = checkToolchain(root, { runtime: "bun", documents: ["README.md"] });
162
+ const version = readBunPin(root);
163
+ const { bun } = assertBunRuntime(root);
164
+ ```
165
+
166
+ This mode reads only `package.json#packageManager`. It checks `devEngines`, Bun
167
+ types, mise, Dockerfiles, GitHub workflows/composite actions and selected docs.
168
+ It rejects Node engine declarations, Node version files, Node setup actions and
169
+ Node runtime images. Node API types remain allowed without a Node runtime pin.
170
+ `readBunPin` and `assertBunRuntime` never read `.nvmrc` or execute Node.
171
+
172
+ ### Lint and test presets
173
+
174
+ With Vite+ 1.3 or later, import the independent configuration entries. They contain
175
+ plain data and do not load Vite+, application plugins, test setup or repository
176
+ configuration. Their declarations do not require Vite+ in unrelated consumers.
177
+
178
+ ```ts
179
+ // vite.config.ts
180
+ import { defineConfig } from "vite-plus";
181
+ import { strictLint } from "@raypx-kit/devkit/lint";
182
+
183
+ export default defineConfig({
184
+ lint: {
185
+ ...strictLint,
186
+ ignorePatterns: ["**/dist/**", "**/*.gen.ts"],
187
+ rules: { ...strictLint.rules, "react/no-danger": "off" },
188
+ },
189
+ });
190
+ ```
191
+
192
+ `baseLint` provides type-aware checking and the Vite+ import rule. `strictLint`
193
+ adds TypeScript, React, accessibility, promise and import correctness checks;
194
+ adopt it explicitly because stricter rules can introduce findings. Neither
195
+ preset chooses generated files, workspace paths or project exceptions.
196
+ Consumers own formatting and staged Git commands as well.
197
+
198
+ ```ts
199
+ // vitest.config.ts
200
+ import { defineConfig, mergeConfig } from "vite-plus/test/config";
201
+ import { bunTestDefaults } from "@raypx-kit/devkit/testing";
202
+
203
+ export default mergeConfig(
204
+ bunTestDefaults,
205
+ defineConfig({
206
+ test: { include: ["tests/**/*.test.ts"], setupFiles: ["./tests/setup.ts"] },
207
+ }),
208
+ );
209
+ ```
210
+
211
+ `bunTestDefaults` selects the Node environment, one worker, Bun's `--no-env-file`
212
+ and `--no-install` flags, and V8 coverage with text/JSON/LCOV reporters. Execute
213
+ tests with `bun --bun vp test --run`; install the matching coverage provider when
214
+ requesting coverage. Suites, DOM environments, setup files, React deduplication
215
+ and application fixtures remain consumer-owned. Use native `mergeConfig` for
216
+ overrides; no configuration factory or custom option schema is required.
217
+
149
218
  ## Catalogs and documentation
150
219
 
151
220
  ```ts
@@ -1,5 +1,4 @@
1
1
  import { WorkspaceIndex } from "./workspaces.mjs";
2
- //#region src/architecture.d.ts
3
2
  export interface ImportEdge {
4
3
  request: string;
5
4
  typesOnly: boolean;
@@ -11,5 +10,4 @@ export declare function imports(file: string, source: string): ImportEdge[];
11
10
  * declarations, internal paths, platform targets, tooling and test-only boundaries.
12
11
  */
13
12
  export declare function checkArchitecture(index: WorkspaceIndex): string[];
14
- //#endregion
15
13
  //# sourceMappingURL=architecture.d.mts.map
@@ -3,7 +3,7 @@ import { builtinModules } from "node:module";
3
3
  import { existsSync, globSync, readFileSync } from "node:fs";
4
4
  import { join, posix } from "node:path";
5
5
  import { parseSync, visitorKeys } from "oxc-parser";
6
- //#region src/architecture.ts
6
+ // src/architecture.ts
7
7
  /** Static import, re-export, dynamic import and `import()` type edges, without executing code. */
8
8
  function imports(file, source) {
9
9
  const parsed = parseSync(file, source);
@@ -36,6 +36,9 @@ const builtins = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]
36
36
  const testFile = /(?:\.(?:test|spec)\.[cm]?[jt]sx?$|\/(?:testing|test-utils)(?:\.[cm]?[jt]sx?$|\/)|\/(?:tests?|__tests__|test-fixtures)\/)/;
37
37
  const codeFile = /\.[cm]?[jt]sx?$/;
38
38
  const runtimeFile = /\/(?:src|server|native|app)\//;
39
+ function bundledWorkspace(workspace, name) {
40
+ return (workspace.metadata?.kind === "app" || workspace.metadata?.kind === "tooling") && workspace.metadata.bundledDependencies?.includes(name) === true && !!workspace.manifest.devDependencies?.[name];
41
+ }
39
42
  function children(node) {
40
43
  const record = node;
41
44
  return (visitorKeys[node.type] ?? []).flatMap((key) => (Array.isArray(record[key]) ? record[key] : [record[key]]).filter((child) => !!child && typeof child === "object" && "type" in child));
@@ -150,7 +153,7 @@ function checkArchitecture(index) {
150
153
  for (const [entry, target] of Object.entries(metadata.entryTargets ?? {})) if (!manifest.exports?.[entry] || !targets.includes(target)) findings.push(`${path}: invalid entryTargets override ${entry}`);
151
154
  for (const [scope, target] of Object.entries(metadata.sourceTargets ?? {})) if (!scope || !targets.includes(target)) findings.push(`${path}: invalid sourceTargets override ${scope}`);
152
155
  for (const [entry, source] of Object.entries(metadata.sourceEntries ?? {})) if (!manifest.exports?.[entry] || !source.startsWith("./") || source.split("/").includes("..") || !codeFile.test(source) || (source.includes("*") ? !entry.includes("*") || !globSync(posix.join(path, source), { cwd: index.root }).length : !existsSync(join(index.root, path, source)))) findings.push(`${path}: invalid sourceEntries override ${entry}: ${source}`);
153
- for (const name of metadata.bundledDependencies ?? []) if (!manifest.devDependencies?.[name] || index.byName.has(name)) findings.push(`${path}: bundled dependency must be an external development dependency: ${name}`);
156
+ for (const name of metadata.bundledDependencies ?? []) if (!manifest.devDependencies?.[name] || index.byName.has(name) && !bundledWorkspace(workspace, name)) findings.push(`${path}: bundled dependency must be an external development dependency: ${name}`);
154
157
  if (metadata.kind === "library" && !Object.keys(manifest.exports ?? {}).length) findings.push(`${path}: libraries need explicit exports`);
155
158
  for (const [entry, value] of Object.entries(manifest.exports ?? {})) for (const exported of exportPaths(value)) {
156
159
  const exists = exported.includes("*") ? globSync(posix.join(path, exported), { cwd: index.root }).length > 0 : existsSync(join(index.root, path, exported));
@@ -204,7 +207,7 @@ function checkArchitecture(index) {
204
207
  if (resolved.internalPath) findings.push(`${file}: internal cross-workspace import ${request}`);
205
208
  if (to !== workspace) {
206
209
  if (to.metadata?.kind === "app") findings.push(`${file}: application source cannot be imported: ${request}`);
207
- if (!(workspace.manifest.dependencies?.[to.manifest.name] || workspace.manifest.peerDependencies?.[to.manifest.name]) && !(typesOnly && workspace.manifest.devDependencies?.[to.manifest.name])) findings.push(`${file}: undeclared ${typesOnly ? "type" : "runtime"} dependency ${request}`);
210
+ if (!(workspace.manifest.dependencies?.[to.manifest.name] || workspace.manifest.peerDependencies?.[to.manifest.name] || bundledWorkspace(workspace, to.manifest.name)) && !(typesOnly && workspace.manifest.devDependencies?.[to.manifest.name])) findings.push(`${file}: undeclared ${typesOnly ? "type" : "runtime"} dependency ${request}`);
208
211
  if (!typesOnly && workspace.metadata?.kind !== "tooling" && to.metadata?.kind === "tooling") findings.push(`${file}: runtime cannot import tooling ${request}`);
209
212
  if (!typesOnly && !compatible(platform, resolved.targetPlatform)) findings.push(`${file}: ${platform} entry cannot import ${resolved.targetPlatform} entry ${request}${transitive ? " (transitive)" : ""}`);
210
213
  }
@@ -260,7 +263,6 @@ function checkArchitecture(index) {
260
263
  }
261
264
  return [...new Set(findings)];
262
265
  }
263
- //#endregion
264
266
  export { checkArchitecture, imports };
265
267
 
266
268
  //# sourceMappingURL=architecture.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"architecture.mjs","names":[],"sources":["../src/architecture.ts"],"sourcesContent":["import { existsSync, globSync, readFileSync } from \"node:fs\";\nimport { builtinModules } from \"node:module\";\nimport { join, posix } from \"node:path\";\nimport { parseSync, visitorKeys, type Node } from \"oxc-parser\";\nimport {\n compatible,\n exportPaths,\n kinds,\n resolveImport,\n sourceTarget,\n targets,\n workspaceFor,\n type Target,\n type Workspace,\n type WorkspaceIndex,\n} from \"./workspaces.js\";\n\nexport interface ImportEdge {\n request: string;\n typesOnly: boolean;\n}\n/** Static import, re-export, dynamic import and `import()` type edges, without executing code. */\nexport function imports(file: string, source: string): ImportEdge[] {\n const parsed = parseSync(file, source);\n if (parsed.errors.length) throw new Error(`${file}: ${parsed.errors[0]!.message}`);\n const found: ImportEdge[] = [];\n function walk(node: Node) {\n if (\n (node.type === \"ImportDeclaration\" ||\n node.type === \"ExportNamedDeclaration\" ||\n node.type === \"ExportAllDeclaration\") &&\n node.source\n ) {\n const record = node as unknown as {\n importKind?: string;\n exportKind?: string;\n specifiers?: { importKind?: string; exportKind?: string }[];\n };\n const typesOnly =\n record.importKind === \"type\" ||\n record.exportKind === \"type\" ||\n (!!record.specifiers?.length &&\n record.specifiers.every((s) => s.importKind === \"type\" || s.exportKind === \"type\"));\n found.push({ request: node.source.value, typesOnly });\n }\n if (\n node.type === \"ImportExpression\" &&\n node.source.type === \"Literal\" &&\n typeof node.source.value === \"string\"\n )\n found.push({ request: node.source.value, typesOnly: false });\n if (node.type === \"TSImportType\") found.push({ request: node.source.value, typesOnly: true });\n const record = node as unknown as Record<string, unknown>;\n for (const key of visitorKeys[node.type] ?? []) {\n for (const child of Array.isArray(record[key]) ? record[key] : [record[key]]) {\n if (child && typeof child === \"object\" && \"type\" in child) walk(child as Node);\n }\n }\n }\n walk(parsed.program);\n return found;\n}\n\nconst builtins = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));\nconst testFile =\n /(?:\\.(?:test|spec)\\.[cm]?[jt]sx?$|\\/(?:testing|test-utils)(?:\\.[cm]?[jt]sx?$|\\/)|\\/(?:tests?|__tests__|test-fixtures)\\/)/;\nconst codeFile = /\\.[cm]?[jt]sx?$/;\nconst runtimeFile = /\\/(?:src|server|native|app)\\//;\n\nfunction children(node: Node): Node[] {\n const record = node as unknown as Record<string, unknown>;\n return (visitorKeys[node.type] ?? []).flatMap((key) =>\n (Array.isArray(record[key]) ? record[key] : [record[key]]).filter(\n (child): child is Node => !!child && typeof child === \"object\" && \"type\" in child,\n ),\n );\n}\n\n/** Resolve local bindings so data named `document` is not mistaken for the DOM. */\nfunction reachesDomGlobal(file: string, source: string): boolean {\n const { program, errors } = parseSync(file, source);\n if (errors.length) throw new Error(`${file}: ${errors[0]!.message}`);\n interface Scope {\n names: Set<string>;\n parent: Scope | undefined;\n }\n const lexicalScopes = new Set([\n \"BlockStatement\",\n \"ForStatement\",\n \"ForInStatement\",\n \"ForOfStatement\",\n \"SwitchStatement\",\n ]);\n function bind(node: Node | null | undefined, names: Set<string>) {\n if (!node) return;\n if (node.type === \"Identifier\") names.add(node.name);\n else if (node.type === \"RestElement\") bind(node.argument, names);\n else if (node.type === \"AssignmentPattern\") bind(node.left, names);\n else if (node.type === \"ArrayPattern\") for (const child of node.elements) bind(child, names);\n else if (node.type === \"ObjectPattern\")\n for (const property of node.properties)\n bind(property.type === \"RestElement\" ? property.argument : property.value, names);\n }\n function declarations(node: Node, names: Set<string>, nested = false) {\n if (\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"ClassDeclaration\" ||\n node.type === \"ClassExpression\"\n ) {\n if (!nested && \"id\" in node) bind(node.id, names);\n return;\n }\n if (node.type === \"VariableDeclaration\") {\n if (!nested || node.kind === \"var\")\n for (const declaration of node.declarations) bind(declaration.id, names);\n return;\n }\n if (!nested && node.type === \"ImportDeclaration\") {\n for (const specifier of node.specifiers) bind(specifier.local, names);\n return;\n }\n for (const child of children(node))\n declarations(child, names, nested || lexicalScopes.has(child.type));\n }\n function bound(name: string, scope: Scope | undefined): boolean {\n return !!scope && (scope.names.has(name) || bound(name, scope.parent));\n }\n const dom = new Set([\"window\", \"document\", \"localStorage\", \"sessionStorage\"]);\n function walk(node: Node, parent: Scope | undefined): boolean {\n let scope = parent;\n if (\n node.type === \"Program\" ||\n lexicalScopes.has(node.type) ||\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"CatchClause\"\n ) {\n scope = { names: new Set(), parent };\n if (\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\"\n ) {\n for (const parameter of node.params) bind(parameter, scope.names);\n if (\"id\" in node) bind(node.id, scope.names);\n if (node.body) declarations(node.body, scope.names);\n } else if (node.type === \"CatchClause\") bind(node.param, scope.names);\n else\n for (const child of children(node))\n declarations(child, scope.names, lexicalScopes.has(child.type));\n }\n if (\n node.type === \"MemberExpression\" &&\n node.object.type === \"Identifier\" &&\n dom.has(node.object.name) &&\n !bound(node.object.name, scope)\n )\n return true;\n return children(node).some((child) => walk(child, scope));\n }\n return walk(program, undefined);\n}\n\nfunction hostViolation(request: string, target: Target): boolean {\n if (request.startsWith(\"bun:\")) return target !== \"bun\";\n if (request.startsWith(\"node:\") || builtins.has(request))\n return target !== \"node\" && target !== \"bun\";\n if (/^(react-dom|@base-ui\\/react)(\\/|$)/.test(request)) return target !== \"browser\";\n if (/^(react-native|expo(?:-[^/]+)?)(\\/|$)/.test(request)) return target !== \"native\";\n if (/^electrobun\\/(bun|main)(\\/|$)/.test(request)) return target !== \"bun\";\n return false;\n}\n\nfunction resolveCode(index: WorkspaceIndex, path: string): string | undefined {\n const withoutJs = path.replace(/\\.[cm]?js$/, \"\");\n return [\n path,\n `${path}.ts`,\n `${path}.tsx`,\n `${withoutJs}.ts`,\n `${withoutJs}.tsx`,\n `${path}/index.ts`,\n `${path}/index.tsx`,\n ].find((candidate) => codeFile.test(candidate) && existsSync(join(index.root, candidate)));\n}\n\n/**\n * Checks workspace metadata, exports, the dependency graph and every runtime import:\n * declarations, internal paths, platform targets, tooling and test-only boundaries.\n */\nexport function checkArchitecture(index: WorkspaceIndex): string[] {\n const findings = [...index.findings];\n const graph = new Map<string, Set<string>>();\n const parsed = new Map<string, ImportEdge[]>();\n const getImports = (file: string) => {\n let edges = parsed.get(file);\n if (!edges) {\n edges = imports(file, readFileSync(join(index.root, file), \"utf8\"));\n parsed.set(file, edges);\n }\n return edges;\n };\n for (const workspace of index.workspaces) {\n const { manifest, path, metadata } = workspace;\n if (!metadata || !kinds.includes(metadata.kind) || !targets.includes(metadata.target)) {\n const finding =\n index.metadataSource === \"options\"\n ? `${path}: ${metadata === undefined ? \"missing\" : \"invalid\"} supplied workspace metadata`\n : `${path}/package.json: invalid or missing ${index.metadataKey} metadata`;\n if (!findings.includes(finding)) findings.push(finding);\n continue;\n }\n if (manifest.type !== \"module\") findings.push(`${path}: workspaces must be ESM`);\n if (metadata.kind === \"app\" && !manifest.private)\n findings.push(`${path}: applications must be private`);\n for (const [entry, target] of Object.entries(metadata.entryTargets ?? {})) {\n if (!manifest.exports?.[entry] || !targets.includes(target))\n findings.push(`${path}: invalid entryTargets override ${entry}`);\n }\n for (const [scope, target] of Object.entries(metadata.sourceTargets ?? {})) {\n if (!scope || !targets.includes(target))\n findings.push(`${path}: invalid sourceTargets override ${scope}`);\n }\n for (const [entry, source] of Object.entries(metadata.sourceEntries ?? {})) {\n if (\n !manifest.exports?.[entry] ||\n !source.startsWith(\"./\") ||\n source.split(\"/\").includes(\"..\") ||\n !codeFile.test(source) ||\n (source.includes(\"*\")\n ? !entry.includes(\"*\") || !globSync(posix.join(path, source), { cwd: index.root }).length\n : !existsSync(join(index.root, path, source)))\n )\n findings.push(`${path}: invalid sourceEntries override ${entry}: ${source}`);\n }\n for (const name of metadata.bundledDependencies ?? []) {\n if (!manifest.devDependencies?.[name] || index.byName.has(name))\n findings.push(\n `${path}: bundled dependency must be an external development dependency: ${name}`,\n );\n }\n if (metadata.kind === \"library\" && !Object.keys(manifest.exports ?? {}).length)\n findings.push(`${path}: libraries need explicit exports`);\n for (const [entry, value] of Object.entries(manifest.exports ?? {})) {\n for (const exported of exportPaths(value)) {\n const exists = exported.includes(\"*\")\n ? globSync(posix.join(path, exported), { cwd: index.root }).length > 0\n : existsSync(join(index.root, path, exported));\n if (\n !exported.startsWith(\"./\") ||\n exported.includes(\"..\") ||\n (!metadata.sourceEntries?.[entry] && !exists)\n )\n findings.push(`${path}: missing or invalid export ${entry}: ${exported}`);\n }\n }\n const edges = new Set<string>();\n graph.set(manifest.name, edges);\n for (const [field, deps] of Object.entries({\n dependencies: manifest.dependencies,\n devDependencies: manifest.devDependencies,\n peerDependencies: manifest.peerDependencies,\n })) {\n for (const [name, range] of Object.entries(deps ?? {})) {\n const to = index.byName.get(name);\n if (!to) {\n if (range.startsWith(\"workspace:\"))\n findings.push(`${path}: missing workspace dependency ${name}`);\n continue;\n }\n edges.add(name);\n if (range !== \"workspace:*\")\n findings.push(`${path}: internal dependency ${name} must use workspace:*`);\n if (to.metadata?.kind === \"app\")\n findings.push(`${path}: applications cannot be imported: ${name}`);\n if (\n field === \"dependencies\" &&\n metadata.kind !== \"tooling\" &&\n to.metadata?.kind === \"tooling\"\n )\n findings.push(`${path}: runtime cannot depend on tooling: ${name}`);\n }\n }\n }\n\n const visited = new Set<string>();\n const active: string[] = [];\n function visit(name: string) {\n if (active.includes(name)) {\n findings.push(\n `workspace dependency cycle: ${[...active.slice(active.indexOf(name)), name].join(\" -> \")}`,\n );\n return;\n }\n if (visited.has(name)) return;\n active.push(name);\n for (const next of graph.get(name) ?? []) visit(next);\n active.pop();\n visited.add(name);\n }\n for (const name of graph.keys()) visit(name);\n\n function checkEdge(\n workspace: Workspace,\n file: string,\n edge: ImportEdge,\n platform: Target,\n transitive: boolean,\n ): string[] {\n const { request, typesOnly } = edge;\n if (\n /\\/(?:testing|test-utils)(?:\\/|$)/.test(request) &&\n !testFile.test(file) &&\n workspace.metadata?.kind !== \"tooling\"\n )\n findings.push(`${file}: runtime cannot import test-only entry ${request}`);\n if (!typesOnly && hostViolation(request, platform))\n findings.push(`${file}: ${platform} code cannot import ${request}`);\n const resolved = resolveImport(index, workspace, file, request);\n const to = resolved.target;\n if (!to) {\n if (\n !resolved.paths.length &&\n !request.startsWith(\".\") &&\n !request.startsWith(\"#\") &&\n !request.startsWith(\"virtual:\") &&\n !request.startsWith(\"bun:\") &&\n !request.startsWith(\"node:\") &&\n !builtins.has(request)\n ) {\n const name = request.startsWith(\"@\")\n ? request.split(\"/\").slice(0, 2).join(\"/\")\n : request.split(\"/\")[0]!;\n const declared =\n workspace.manifest.dependencies?.[name] ||\n workspace.manifest.peerDependencies?.[name] ||\n (workspace.metadata?.bundledDependencies?.includes(name) &&\n workspace.manifest.devDependencies?.[name]) ||\n (typesOnly &&\n (workspace.manifest.devDependencies?.[name] ||\n workspace.manifest.devDependencies?.[\n `@types/${name.startsWith(\"@\") ? name.slice(1).replace(\"/\", \"__\") : name}`\n ]));\n if (!declared)\n findings.push(\n `${file}: undeclared ${typesOnly ? \"type\" : \"runtime\"} dependency ${request}`,\n );\n }\n return resolved.paths;\n }\n if (resolved.internalPath) findings.push(`${file}: internal cross-workspace import ${request}`);\n if (to !== workspace) {\n if (to.metadata?.kind === \"app\")\n findings.push(`${file}: application source cannot be imported: ${request}`);\n const declared =\n workspace.manifest.dependencies?.[to.manifest.name] ||\n workspace.manifest.peerDependencies?.[to.manifest.name];\n if (!declared && !(typesOnly && workspace.manifest.devDependencies?.[to.manifest.name]))\n findings.push(\n `${file}: undeclared ${typesOnly ? \"type\" : \"runtime\"} dependency ${request}`,\n );\n if (!typesOnly && workspace.metadata?.kind !== \"tooling\" && to.metadata?.kind === \"tooling\")\n findings.push(`${file}: runtime cannot import tooling ${request}`);\n if (!typesOnly && !compatible(platform, resolved.targetPlatform))\n findings.push(\n `${file}: ${platform} entry cannot import ${resolved.targetPlatform} entry ${request}${transitive ? \" (transitive)\" : \"\"}`,\n );\n }\n for (const path of resolved.paths)\n if (!testFile.test(file) && testFile.test(path))\n findings.push(`${file}: runtime cannot import test-only source ${request}`);\n return typesOnly ? [] : resolved.paths;\n }\n\n for (const workspace of index.workspaces) {\n const files = globSync(`${workspace.path}/**/*.{ts,tsx,mts,js,mjs}`, {\n cwd: index.root,\n exclude: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/.output/**\",\n \"**/.hutch/**\",\n \"**/build/**\",\n \"**/artifacts/**\",\n \"**/routeTree.gen.ts\",\n \"**/*.d.ts\",\n ],\n });\n for (const file of files) {\n const normalized = file.replaceAll(\"\\\\\", \"/\");\n if (!runtimeFile.test(normalized) || testFile.test(normalized)) continue;\n try {\n for (const edge of getImports(normalized))\n checkEdge(workspace, normalized, edge, sourceTarget(workspace, normalized), false);\n } catch (error) {\n findings.push(String(error));\n }\n }\n for (const [entry, value] of Object.entries(workspace.manifest.exports ?? {})) {\n const target =\n workspace.metadata?.entryTargets?.[entry] ?? workspace.metadata?.target ?? \"universal\";\n const seen = new Set<string>();\n function walk(path: string) {\n const file = resolveCode(index, path);\n if (!file || seen.has(file)) return;\n seen.add(file);\n const owner = workspaceFor(index, file);\n if (!owner) return;\n try {\n if (\n (target === \"universal\" || target === \"native\") &&\n reachesDomGlobal(file, readFileSync(join(index.root, file), \"utf8\"))\n )\n findings.push(`${workspace.path}: ${entry} reaches DOM global in ${file}`);\n for (const edge of getImports(file))\n for (const next of checkEdge(owner, file, edge, target, true)) walk(next);\n } catch (error) {\n findings.push(String(error));\n }\n }\n const source = workspace.metadata?.sourceEntries?.[entry];\n for (const path of source ? [source] : exportPaths(value)) {\n const exported = posix.join(workspace.path, path);\n if (exported.includes(\"*\"))\n for (const file of globSync(exported, { cwd: index.root }))\n walk(file.replaceAll(\"\\\\\", \"/\"));\n else walk(exported);\n }\n }\n }\n return [...new Set(findings)];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"architecture.mjs","names":[],"sources":["../src/architecture.ts"],"sourcesContent":["import { existsSync, globSync, readFileSync } from \"node:fs\";\nimport { builtinModules } from \"node:module\";\nimport { join, posix } from \"node:path\";\nimport { parseSync, visitorKeys, type Node } from \"oxc-parser\";\nimport {\n compatible,\n exportPaths,\n kinds,\n resolveImport,\n sourceTarget,\n targets,\n workspaceFor,\n type Target,\n type Workspace,\n type WorkspaceIndex,\n} from \"./workspaces.js\";\n\nexport interface ImportEdge {\n request: string;\n typesOnly: boolean;\n}\n/** Static import, re-export, dynamic import and `import()` type edges, without executing code. */\nexport function imports(file: string, source: string): ImportEdge[] {\n const parsed = parseSync(file, source);\n if (parsed.errors.length) throw new Error(`${file}: ${parsed.errors[0]!.message}`);\n const found: ImportEdge[] = [];\n function walk(node: Node) {\n if (\n (node.type === \"ImportDeclaration\" ||\n node.type === \"ExportNamedDeclaration\" ||\n node.type === \"ExportAllDeclaration\") &&\n node.source\n ) {\n const record = node as unknown as {\n importKind?: string;\n exportKind?: string;\n specifiers?: { importKind?: string; exportKind?: string }[];\n };\n const typesOnly =\n record.importKind === \"type\" ||\n record.exportKind === \"type\" ||\n (!!record.specifiers?.length &&\n record.specifiers.every((s) => s.importKind === \"type\" || s.exportKind === \"type\"));\n found.push({ request: node.source.value, typesOnly });\n }\n if (\n node.type === \"ImportExpression\" &&\n node.source.type === \"Literal\" &&\n typeof node.source.value === \"string\"\n )\n found.push({ request: node.source.value, typesOnly: false });\n if (node.type === \"TSImportType\") found.push({ request: node.source.value, typesOnly: true });\n const record = node as unknown as Record<string, unknown>;\n for (const key of visitorKeys[node.type] ?? []) {\n for (const child of Array.isArray(record[key]) ? record[key] : [record[key]]) {\n if (child && typeof child === \"object\" && \"type\" in child) walk(child as Node);\n }\n }\n }\n walk(parsed.program);\n return found;\n}\n\nconst builtins = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));\nconst testFile =\n /(?:\\.(?:test|spec)\\.[cm]?[jt]sx?$|\\/(?:testing|test-utils)(?:\\.[cm]?[jt]sx?$|\\/)|\\/(?:tests?|__tests__|test-fixtures)\\/)/;\nconst codeFile = /\\.[cm]?[jt]sx?$/;\nconst runtimeFile = /\\/(?:src|server|native|app)\\//;\n\nfunction bundledWorkspace(workspace: Workspace, name: string): boolean {\n return (\n (workspace.metadata?.kind === \"app\" || workspace.metadata?.kind === \"tooling\") &&\n workspace.metadata.bundledDependencies?.includes(name) === true &&\n !!workspace.manifest.devDependencies?.[name]\n );\n}\n\nfunction children(node: Node): Node[] {\n const record = node as unknown as Record<string, unknown>;\n return (visitorKeys[node.type] ?? []).flatMap((key) =>\n (Array.isArray(record[key]) ? record[key] : [record[key]]).filter(\n (child): child is Node => !!child && typeof child === \"object\" && \"type\" in child,\n ),\n );\n}\n\n/** Resolve local bindings so data named `document` is not mistaken for the DOM. */\nfunction reachesDomGlobal(file: string, source: string): boolean {\n const { program, errors } = parseSync(file, source);\n if (errors.length) throw new Error(`${file}: ${errors[0]!.message}`);\n interface Scope {\n names: Set<string>;\n parent: Scope | undefined;\n }\n const lexicalScopes = new Set([\n \"BlockStatement\",\n \"ForStatement\",\n \"ForInStatement\",\n \"ForOfStatement\",\n \"SwitchStatement\",\n ]);\n function bind(node: Node | null | undefined, names: Set<string>) {\n if (!node) return;\n if (node.type === \"Identifier\") names.add(node.name);\n else if (node.type === \"RestElement\") bind(node.argument, names);\n else if (node.type === \"AssignmentPattern\") bind(node.left, names);\n else if (node.type === \"ArrayPattern\") for (const child of node.elements) bind(child, names);\n else if (node.type === \"ObjectPattern\")\n for (const property of node.properties)\n bind(property.type === \"RestElement\" ? property.argument : property.value, names);\n }\n function declarations(node: Node, names: Set<string>, nested = false) {\n if (\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"ClassDeclaration\" ||\n node.type === \"ClassExpression\"\n ) {\n if (!nested && \"id\" in node) bind(node.id, names);\n return;\n }\n if (node.type === \"VariableDeclaration\") {\n if (!nested || node.kind === \"var\")\n for (const declaration of node.declarations) bind(declaration.id, names);\n return;\n }\n if (!nested && node.type === \"ImportDeclaration\") {\n for (const specifier of node.specifiers) bind(specifier.local, names);\n return;\n }\n for (const child of children(node))\n declarations(child, names, nested || lexicalScopes.has(child.type));\n }\n function bound(name: string, scope: Scope | undefined): boolean {\n return !!scope && (scope.names.has(name) || bound(name, scope.parent));\n }\n const dom = new Set([\"window\", \"document\", \"localStorage\", \"sessionStorage\"]);\n function walk(node: Node, parent: Scope | undefined): boolean {\n let scope = parent;\n if (\n node.type === \"Program\" ||\n lexicalScopes.has(node.type) ||\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"CatchClause\"\n ) {\n scope = { names: new Set(), parent };\n if (\n node.type === \"FunctionDeclaration\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"ArrowFunctionExpression\"\n ) {\n for (const parameter of node.params) bind(parameter, scope.names);\n if (\"id\" in node) bind(node.id, scope.names);\n if (node.body) declarations(node.body, scope.names);\n } else if (node.type === \"CatchClause\") bind(node.param, scope.names);\n else\n for (const child of children(node))\n declarations(child, scope.names, lexicalScopes.has(child.type));\n }\n if (\n node.type === \"MemberExpression\" &&\n node.object.type === \"Identifier\" &&\n dom.has(node.object.name) &&\n !bound(node.object.name, scope)\n )\n return true;\n return children(node).some((child) => walk(child, scope));\n }\n return walk(program, undefined);\n}\n\nfunction hostViolation(request: string, target: Target): boolean {\n if (request.startsWith(\"bun:\")) return target !== \"bun\";\n if (request.startsWith(\"node:\") || builtins.has(request))\n return target !== \"node\" && target !== \"bun\";\n if (/^(react-dom|@base-ui\\/react)(\\/|$)/.test(request)) return target !== \"browser\";\n if (/^(react-native|expo(?:-[^/]+)?)(\\/|$)/.test(request)) return target !== \"native\";\n if (/^electrobun\\/(bun|main)(\\/|$)/.test(request)) return target !== \"bun\";\n return false;\n}\n\nfunction resolveCode(index: WorkspaceIndex, path: string): string | undefined {\n const withoutJs = path.replace(/\\.[cm]?js$/, \"\");\n return [\n path,\n `${path}.ts`,\n `${path}.tsx`,\n `${withoutJs}.ts`,\n `${withoutJs}.tsx`,\n `${path}/index.ts`,\n `${path}/index.tsx`,\n ].find((candidate) => codeFile.test(candidate) && existsSync(join(index.root, candidate)));\n}\n\n/**\n * Checks workspace metadata, exports, the dependency graph and every runtime import:\n * declarations, internal paths, platform targets, tooling and test-only boundaries.\n */\nexport function checkArchitecture(index: WorkspaceIndex): string[] {\n const findings = [...index.findings];\n const graph = new Map<string, Set<string>>();\n const parsed = new Map<string, ImportEdge[]>();\n const getImports = (file: string) => {\n let edges = parsed.get(file);\n if (!edges) {\n edges = imports(file, readFileSync(join(index.root, file), \"utf8\"));\n parsed.set(file, edges);\n }\n return edges;\n };\n for (const workspace of index.workspaces) {\n const { manifest, path, metadata } = workspace;\n if (!metadata || !kinds.includes(metadata.kind) || !targets.includes(metadata.target)) {\n const finding =\n index.metadataSource === \"options\"\n ? `${path}: ${metadata === undefined ? \"missing\" : \"invalid\"} supplied workspace metadata`\n : `${path}/package.json: invalid or missing ${index.metadataKey} metadata`;\n if (!findings.includes(finding)) findings.push(finding);\n continue;\n }\n if (manifest.type !== \"module\") findings.push(`${path}: workspaces must be ESM`);\n if (metadata.kind === \"app\" && !manifest.private)\n findings.push(`${path}: applications must be private`);\n for (const [entry, target] of Object.entries(metadata.entryTargets ?? {})) {\n if (!manifest.exports?.[entry] || !targets.includes(target))\n findings.push(`${path}: invalid entryTargets override ${entry}`);\n }\n for (const [scope, target] of Object.entries(metadata.sourceTargets ?? {})) {\n if (!scope || !targets.includes(target))\n findings.push(`${path}: invalid sourceTargets override ${scope}`);\n }\n for (const [entry, source] of Object.entries(metadata.sourceEntries ?? {})) {\n if (\n !manifest.exports?.[entry] ||\n !source.startsWith(\"./\") ||\n source.split(\"/\").includes(\"..\") ||\n !codeFile.test(source) ||\n (source.includes(\"*\")\n ? !entry.includes(\"*\") || !globSync(posix.join(path, source), { cwd: index.root }).length\n : !existsSync(join(index.root, path, source)))\n )\n findings.push(`${path}: invalid sourceEntries override ${entry}: ${source}`);\n }\n for (const name of metadata.bundledDependencies ?? []) {\n if (\n !manifest.devDependencies?.[name] ||\n (index.byName.has(name) && !bundledWorkspace(workspace, name))\n )\n findings.push(\n `${path}: bundled dependency must be an external development dependency: ${name}`,\n );\n }\n if (metadata.kind === \"library\" && !Object.keys(manifest.exports ?? {}).length)\n findings.push(`${path}: libraries need explicit exports`);\n for (const [entry, value] of Object.entries(manifest.exports ?? {})) {\n for (const exported of exportPaths(value)) {\n const exists = exported.includes(\"*\")\n ? globSync(posix.join(path, exported), { cwd: index.root }).length > 0\n : existsSync(join(index.root, path, exported));\n if (\n !exported.startsWith(\"./\") ||\n exported.includes(\"..\") ||\n (!metadata.sourceEntries?.[entry] && !exists)\n )\n findings.push(`${path}: missing or invalid export ${entry}: ${exported}`);\n }\n }\n const edges = new Set<string>();\n graph.set(manifest.name, edges);\n for (const [field, deps] of Object.entries({\n dependencies: manifest.dependencies,\n devDependencies: manifest.devDependencies,\n peerDependencies: manifest.peerDependencies,\n })) {\n for (const [name, range] of Object.entries(deps ?? {})) {\n const to = index.byName.get(name);\n if (!to) {\n if (range.startsWith(\"workspace:\"))\n findings.push(`${path}: missing workspace dependency ${name}`);\n continue;\n }\n edges.add(name);\n if (range !== \"workspace:*\")\n findings.push(`${path}: internal dependency ${name} must use workspace:*`);\n if (to.metadata?.kind === \"app\")\n findings.push(`${path}: applications cannot be imported: ${name}`);\n if (\n field === \"dependencies\" &&\n metadata.kind !== \"tooling\" &&\n to.metadata?.kind === \"tooling\"\n )\n findings.push(`${path}: runtime cannot depend on tooling: ${name}`);\n }\n }\n }\n\n const visited = new Set<string>();\n const active: string[] = [];\n function visit(name: string) {\n if (active.includes(name)) {\n findings.push(\n `workspace dependency cycle: ${[...active.slice(active.indexOf(name)), name].join(\" -> \")}`,\n );\n return;\n }\n if (visited.has(name)) return;\n active.push(name);\n for (const next of graph.get(name) ?? []) visit(next);\n active.pop();\n visited.add(name);\n }\n for (const name of graph.keys()) visit(name);\n\n function checkEdge(\n workspace: Workspace,\n file: string,\n edge: ImportEdge,\n platform: Target,\n transitive: boolean,\n ): string[] {\n const { request, typesOnly } = edge;\n if (\n /\\/(?:testing|test-utils)(?:\\/|$)/.test(request) &&\n !testFile.test(file) &&\n workspace.metadata?.kind !== \"tooling\"\n )\n findings.push(`${file}: runtime cannot import test-only entry ${request}`);\n if (!typesOnly && hostViolation(request, platform))\n findings.push(`${file}: ${platform} code cannot import ${request}`);\n const resolved = resolveImport(index, workspace, file, request);\n const to = resolved.target;\n if (!to) {\n if (\n !resolved.paths.length &&\n !request.startsWith(\".\") &&\n !request.startsWith(\"#\") &&\n !request.startsWith(\"virtual:\") &&\n !request.startsWith(\"bun:\") &&\n !request.startsWith(\"node:\") &&\n !builtins.has(request)\n ) {\n const name = request.startsWith(\"@\")\n ? request.split(\"/\").slice(0, 2).join(\"/\")\n : request.split(\"/\")[0]!;\n const declared =\n workspace.manifest.dependencies?.[name] ||\n workspace.manifest.peerDependencies?.[name] ||\n (workspace.metadata?.bundledDependencies?.includes(name) &&\n workspace.manifest.devDependencies?.[name]) ||\n (typesOnly &&\n (workspace.manifest.devDependencies?.[name] ||\n workspace.manifest.devDependencies?.[\n `@types/${name.startsWith(\"@\") ? name.slice(1).replace(\"/\", \"__\") : name}`\n ]));\n if (!declared)\n findings.push(\n `${file}: undeclared ${typesOnly ? \"type\" : \"runtime\"} dependency ${request}`,\n );\n }\n return resolved.paths;\n }\n if (resolved.internalPath) findings.push(`${file}: internal cross-workspace import ${request}`);\n if (to !== workspace) {\n if (to.metadata?.kind === \"app\")\n findings.push(`${file}: application source cannot be imported: ${request}`);\n const declared =\n workspace.manifest.dependencies?.[to.manifest.name] ||\n workspace.manifest.peerDependencies?.[to.manifest.name] ||\n bundledWorkspace(workspace, to.manifest.name);\n if (!declared && !(typesOnly && workspace.manifest.devDependencies?.[to.manifest.name]))\n findings.push(\n `${file}: undeclared ${typesOnly ? \"type\" : \"runtime\"} dependency ${request}`,\n );\n if (!typesOnly && workspace.metadata?.kind !== \"tooling\" && to.metadata?.kind === \"tooling\")\n findings.push(`${file}: runtime cannot import tooling ${request}`);\n if (!typesOnly && !compatible(platform, resolved.targetPlatform))\n findings.push(\n `${file}: ${platform} entry cannot import ${resolved.targetPlatform} entry ${request}${transitive ? \" (transitive)\" : \"\"}`,\n );\n }\n for (const path of resolved.paths)\n if (!testFile.test(file) && testFile.test(path))\n findings.push(`${file}: runtime cannot import test-only source ${request}`);\n return typesOnly ? [] : resolved.paths;\n }\n\n for (const workspace of index.workspaces) {\n const files = globSync(`${workspace.path}/**/*.{ts,tsx,mts,js,mjs}`, {\n cwd: index.root,\n exclude: [\n \"**/node_modules/**\",\n \"**/dist/**\",\n \"**/.output/**\",\n \"**/.hutch/**\",\n \"**/build/**\",\n \"**/artifacts/**\",\n \"**/routeTree.gen.ts\",\n \"**/*.d.ts\",\n ],\n });\n for (const file of files) {\n const normalized = file.replaceAll(\"\\\\\", \"/\");\n if (!runtimeFile.test(normalized) || testFile.test(normalized)) continue;\n try {\n for (const edge of getImports(normalized))\n checkEdge(workspace, normalized, edge, sourceTarget(workspace, normalized), false);\n } catch (error) {\n findings.push(String(error));\n }\n }\n for (const [entry, value] of Object.entries(workspace.manifest.exports ?? {})) {\n const target =\n workspace.metadata?.entryTargets?.[entry] ?? workspace.metadata?.target ?? \"universal\";\n const seen = new Set<string>();\n function walk(path: string) {\n const file = resolveCode(index, path);\n if (!file || seen.has(file)) return;\n seen.add(file);\n const owner = workspaceFor(index, file);\n if (!owner) return;\n try {\n if (\n (target === \"universal\" || target === \"native\") &&\n reachesDomGlobal(file, readFileSync(join(index.root, file), \"utf8\"))\n )\n findings.push(`${workspace.path}: ${entry} reaches DOM global in ${file}`);\n for (const edge of getImports(file))\n for (const next of checkEdge(owner, file, edge, target, true)) walk(next);\n } catch (error) {\n findings.push(String(error));\n }\n }\n const source = workspace.metadata?.sourceEntries?.[entry];\n for (const path of source ? [source] : exportPaths(value)) {\n const exported = posix.join(workspace.path, path);\n if (exported.includes(\"*\"))\n for (const file of globSync(exported, { cwd: index.root }))\n walk(file.replaceAll(\"\\\\\", \"/\"));\n else walk(exported);\n }\n }\n }\n return [...new Set(findings)];\n}\n"],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- //#region src/artifact.d.ts
2
1
  /** A relocated probe must not quietly copy dependencies from outside the artifact. */
3
2
  export declare function stageArtifact(source: string, destination: string): Promise<void>;
4
3
  export interface ArtifactProbe {
@@ -21,5 +20,4 @@ export interface ArtifactProbe {
21
20
  }
22
21
  /** Starts a relocated Node server output on a free port and probes it while it runs. */
23
22
  export declare function probeArtifact({ name, output, node, entry, healthPath, environment, check, failOnOutput }: ArtifactProbe): Promise<void>;
24
- //#endregion
25
23
  //# sourceMappingURL=artifact.d.mts.map
package/dist/artifact.mjs CHANGED
@@ -5,7 +5,7 @@ import { cp, mkdtemp, readdir, readlink, realpath, rm } from "node:fs/promises";
5
5
  import { createServer } from "node:net";
6
6
  import { tmpdir } from "node:os";
7
7
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
8
- //#region src/artifact.ts
8
+ // src/artifact.ts
9
9
  /** A relocated probe must not quietly copy dependencies from outside the artifact. */
10
10
  async function stageArtifact(source, destination) {
11
11
  const root = await realpath(source);
@@ -98,7 +98,6 @@ async function probeArtifact({ name, output, node, entry = ".output/server/index
98
98
  });
99
99
  }
100
100
  }
101
- //#endregion
102
101
  export { probeArtifact, stageArtifact };
103
102
 
104
103
  //# sourceMappingURL=artifact.mjs.map
@@ -1,4 +1,3 @@
1
- //#region src/catalogs.d.ts
2
1
  /** A message catalog: nested objects whose leaves are strings. */
3
2
  export interface CatalogTree {
4
3
  readonly [key: string]: string | CatalogTree;
@@ -9,5 +8,4 @@ export interface CatalogTree {
9
8
  * are accepted; anything computed is rejected.
10
9
  */
11
10
  export declare function readStaticCatalog(path: string, source?: string): CatalogTree;
12
- //#endregion
13
11
  //# sourceMappingURL=catalogs.d.mts.map
package/dist/catalogs.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { parseSync } from "oxc-parser";
3
- //#region src/catalogs.ts
3
+ // src/catalogs.ts
4
4
  /**
5
5
  * Reads a `.json` catalog or the static default export of a TS/JS module without
6
6
  * executing it. Only object literals with string values, `as const` and `satisfies`
@@ -43,7 +43,6 @@ function readStaticCatalog(path, source = readFileSync(path, "utf8")) {
43
43
  if (typeof result === "string") throw new Error(`${path}: expected a catalog object`);
44
44
  return result;
45
45
  }
46
- //#endregion
47
46
  export { readStaticCatalog };
48
47
 
49
48
  //# sourceMappingURL=catalogs.mjs.map
package/dist/docs.d.mts CHANGED
@@ -1,5 +1,3 @@
1
- //#region src/docs.d.ts
2
1
  /** Reports relative Markdown links whose target file does not exist. */
3
2
  export declare function checkDocumentLinks(root: string, files: readonly string[]): string[];
4
- //#endregion
5
3
  //# sourceMappingURL=docs.d.mts.map
package/dist/docs.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
- //#region src/docs.ts
3
+ // src/docs.ts
4
4
  /** Reports relative Markdown links whose target file does not exist. */
5
5
  function checkDocumentLinks(root, files) {
6
6
  const findings = [];
@@ -15,7 +15,6 @@ function checkDocumentLinks(root, files) {
15
15
  }
16
16
  return findings;
17
17
  }
18
- //#endregion
19
18
  export { checkDocumentLinks };
20
19
 
21
20
  //# sourceMappingURL=docs.mjs.map
@@ -0,0 +1,68 @@
1
+ /** Vite+ checks shared by libraries and applications; paths belong to the consumer. */
2
+ export declare const baseLint: {
3
+ jsPlugins: {
4
+ name: string;
5
+ specifier: string;
6
+ }[];
7
+ options: {
8
+ typeAware: true;
9
+ typeCheck: true;
10
+ };
11
+ rules: {
12
+ "vite-plus/prefer-vite-plus-imports": "error";
13
+ };
14
+ };
15
+ /** Opt-in correctness rules for TypeScript and React repositories. */
16
+ export declare const strictLint: {
17
+ jsPlugins: {
18
+ name: string;
19
+ specifier: string;
20
+ }[];
21
+ plugins: ("import" | "jsx-a11y" | "node" | "oxc" | "promise" | "react" | "typescript" | "unicorn" | "vitest")[];
22
+ categories: {
23
+ correctness: "error";
24
+ suspicious: "error";
25
+ };
26
+ options: {
27
+ typeAware: true;
28
+ typeCheck: true;
29
+ reportUnusedDisableDirectives: "error";
30
+ };
31
+ rules: {
32
+ "vite-plus/prefer-vite-plus-imports": "error";
33
+ "array-callback-return": "error";
34
+ eqeqeq: "error";
35
+ "prefer-const": "error";
36
+ "no-duplicate-imports": ["error", {
37
+ allowSeparateTypeImports: true;
38
+ }];
39
+ "import/no-cycle": "error";
40
+ "oxc/no-accumulating-spread": "error";
41
+ "react/rules-of-hooks": "error";
42
+ "react/no-danger": "error";
43
+ "typescript/consistent-type-imports": ["error", {
44
+ disallowTypeAnnotations: false;
45
+ }];
46
+ "typescript/switch-exhaustiveness-check": "error";
47
+ "import/no-unassigned-import": "off";
48
+ "import/no-named-as-default": "off";
49
+ "import/no-named-as-default-member": "off";
50
+ "promise/always-return": "off";
51
+ "promise/no-callback-in-promise": "off";
52
+ "promise/no-promise-in-callback": "off";
53
+ "react/exhaustive-effect-dependencies": "off";
54
+ "react/react-in-jsx-scope": "off";
55
+ "vitest/require-mock-type-parameters": "off";
56
+ "vitest/warn-todo": "off";
57
+ "vitest/prefer-snapshot-hint": "off";
58
+ "typescript/consistent-return": "off";
59
+ "typescript/no-unsafe-type-assertion": "off";
60
+ "unicorn/no-array-sort": "off";
61
+ "unicorn/no-array-reverse": "off";
62
+ "no-underscore-dangle": "off";
63
+ "unicorn/consistent-function-scoping": "off";
64
+ "jsx-a11y/prefer-tag-over-role": "off";
65
+ "jsx-a11y/no-autofocus": "off";
66
+ };
67
+ };
68
+ //# sourceMappingURL=lint.d.mts.map
package/dist/lint.mjs ADDED
@@ -0,0 +1,71 @@
1
+ // src/lint.ts
2
+ /** Vite+ checks shared by libraries and applications; paths belong to the consumer. */
3
+ const baseLint = {
4
+ jsPlugins: [{
5
+ name: "vite-plus",
6
+ specifier: "vite-plus/oxlint-plugin"
7
+ }],
8
+ options: {
9
+ typeAware: true,
10
+ typeCheck: true
11
+ },
12
+ rules: { "vite-plus/prefer-vite-plus-imports": "error" }
13
+ };
14
+ /** Opt-in correctness rules for TypeScript and React repositories. */
15
+ const strictLint = {
16
+ ...baseLint,
17
+ plugins: [
18
+ "typescript",
19
+ "unicorn",
20
+ "oxc",
21
+ "import",
22
+ "react",
23
+ "vitest",
24
+ "jsx-a11y",
25
+ "promise",
26
+ "node"
27
+ ],
28
+ categories: {
29
+ correctness: "error",
30
+ suspicious: "error"
31
+ },
32
+ options: {
33
+ ...baseLint.options,
34
+ reportUnusedDisableDirectives: "error"
35
+ },
36
+ rules: {
37
+ ...baseLint.rules,
38
+ "array-callback-return": "error",
39
+ eqeqeq: "error",
40
+ "prefer-const": "error",
41
+ "no-duplicate-imports": ["error", { allowSeparateTypeImports: true }],
42
+ "import/no-cycle": "error",
43
+ "oxc/no-accumulating-spread": "error",
44
+ "react/rules-of-hooks": "error",
45
+ "react/no-danger": "error",
46
+ "typescript/consistent-type-imports": ["error", { disallowTypeAnnotations: false }],
47
+ "typescript/switch-exhaustiveness-check": "error",
48
+ "import/no-unassigned-import": "off",
49
+ "import/no-named-as-default": "off",
50
+ "import/no-named-as-default-member": "off",
51
+ "promise/always-return": "off",
52
+ "promise/no-callback-in-promise": "off",
53
+ "promise/no-promise-in-callback": "off",
54
+ "react/exhaustive-effect-dependencies": "off",
55
+ "react/react-in-jsx-scope": "off",
56
+ "vitest/require-mock-type-parameters": "off",
57
+ "vitest/warn-todo": "off",
58
+ "vitest/prefer-snapshot-hint": "off",
59
+ "typescript/consistent-return": "off",
60
+ "typescript/no-unsafe-type-assertion": "off",
61
+ "unicorn/no-array-sort": "off",
62
+ "unicorn/no-array-reverse": "off",
63
+ "no-underscore-dangle": "off",
64
+ "unicorn/consistent-function-scoping": "off",
65
+ "jsx-a11y/prefer-tag-over-role": "off",
66
+ "jsx-a11y/no-autofocus": "off"
67
+ }
68
+ };
69
+ export { baseLint, strictLint };
70
+
71
+ //# sourceMappingURL=lint.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lint.mjs","names":[],"sources":["../src/lint.ts"],"sourcesContent":["import type { ViteUserConfig } from \"vite-plus\";\n\n/** Vite+ checks shared by libraries and applications; paths belong to the consumer. */\nexport const baseLint = {\n jsPlugins: [{ name: \"vite-plus\", specifier: \"vite-plus/oxlint-plugin\" }],\n options: { typeAware: true, typeCheck: true },\n rules: { \"vite-plus/prefer-vite-plus-imports\": \"error\" },\n} satisfies NonNullable<ViteUserConfig[\"lint\"]>;\n\n/** Opt-in correctness rules for TypeScript and React repositories. */\nexport const strictLint = {\n ...baseLint,\n plugins: [\n \"typescript\",\n \"unicorn\",\n \"oxc\",\n \"import\",\n \"react\",\n \"vitest\",\n \"jsx-a11y\",\n \"promise\",\n \"node\",\n ],\n categories: { correctness: \"error\", suspicious: \"error\" },\n options: { ...baseLint.options, reportUnusedDisableDirectives: \"error\" },\n rules: {\n ...baseLint.rules,\n \"array-callback-return\": \"error\",\n eqeqeq: \"error\",\n \"prefer-const\": \"error\",\n \"no-duplicate-imports\": [\"error\", { allowSeparateTypeImports: true }],\n \"import/no-cycle\": \"error\",\n \"oxc/no-accumulating-spread\": \"error\",\n \"react/rules-of-hooks\": \"error\",\n \"react/no-danger\": \"error\",\n \"typescript/consistent-type-imports\": [\"error\", { disallowTypeAnnotations: false }],\n \"typescript/switch-exhaustiveness-check\": \"error\",\n // Bootstrap imports, third-party defaults and callback adapters are valid.\n \"import/no-unassigned-import\": \"off\",\n \"import/no-named-as-default\": \"off\",\n \"import/no-named-as-default-member\": \"off\",\n \"promise/always-return\": \"off\",\n \"promise/no-callback-in-promise\": \"off\",\n \"promise/no-promise-in-callback\": \"off\",\n \"react/exhaustive-effect-dependencies\": \"off\",\n \"react/react-in-jsx-scope\": \"off\",\n \"vitest/require-mock-type-parameters\": \"off\",\n \"vitest/warn-todo\": \"off\",\n \"vitest/prefer-snapshot-hint\": \"off\",\n \"typescript/consistent-return\": \"off\",\n \"typescript/no-unsafe-type-assertion\": \"off\",\n // ES2022 consumers do not have to polyfill the copying array methods.\n \"unicorn/no-array-sort\": \"off\",\n \"unicorn/no-array-reverse\": \"off\",\n // Keep naming, helper placement and deliberate focus under local control.\n \"no-underscore-dangle\": \"off\",\n \"unicorn/consistent-function-scoping\": \"off\",\n \"jsx-a11y/prefer-tag-over-role\": \"off\",\n \"jsx-a11y/no-autofocus\": \"off\",\n },\n} satisfies NonNullable<ViteUserConfig[\"lint\"]>;\n"],"mappings":";;AAGA,MAAa,WAAW;CACtB,WAAW,CAAC;EAAE,MAAM;EAAa,WAAW;CAA0B,CAAC;CACvE,SAAS;EAAE,WAAW;EAAM,WAAW;CAAK;CAC5C,OAAO,EAAE,sCAAsC,QAAQ;AACzD;;AAGA,MAAa,aAAa;CACxB,GAAG;CACH,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,YAAY;EAAE,aAAa;EAAS,YAAY;CAAQ;CACxD,SAAS;EAAE,GAAG,SAAS;EAAS,+BAA+B;CAAQ;CACvE,OAAO;EACL,GAAG,SAAS;EACZ,yBAAyB;EACzB,QAAQ;EACR,gBAAgB;EAChB,wBAAwB,CAAC,SAAS,EAAE,0BAA0B,KAAK,CAAC;EACpE,mBAAmB;EACnB,8BAA8B;EAC9B,wBAAwB;EACxB,mBAAmB;EACnB,sCAAsC,CAAC,SAAS,EAAE,yBAAyB,MAAM,CAAC;EAClF,0CAA0C;EAE1C,+BAA+B;EAC/B,8BAA8B;EAC9B,qCAAqC;EACrC,yBAAyB;EACzB,kCAAkC;EAClC,kCAAkC;EAClC,wCAAwC;EACxC,4BAA4B;EAC5B,uCAAuC;EACvC,oBAAoB;EACpB,+BAA+B;EAC/B,gCAAgC;EAChC,uCAAuC;EAEvC,yBAAyB;EACzB,4BAA4B;EAE5B,wBAAwB;EACxB,uCAAuC;EACvC,iCAAiC;EACjC,yBAAyB;CAC3B;AACF"}
@@ -1,4 +1,3 @@
1
- //#region src/process.d.ts
2
1
  /**
3
2
  * Copies the current environment and layers local dotenv files beneath it; values
4
3
  * already set in the process win. Loaded secrets are never printed.
@@ -19,5 +18,4 @@ export interface TurboOptions {
19
18
  * the local cache only, so forks never read or write the shared remote cache.
20
19
  */
21
20
  export declare function turbo(root: string, task: string, args?: string[], { developmentTasks, env: extra, trustedCacheEnv }?: TurboOptions): Promise<void>;
22
- //#endregion
23
21
  //# sourceMappingURL=process.d.mts.map
package/dist/process.mjs CHANGED
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { spawn } from "node:child_process";
4
4
  import { config } from "@dotenvx/dotenvx";
5
- //#region src/process.ts
5
+ // src/process.ts
6
6
  /**
7
7
  * Copies the current environment and layers local dotenv files beneath it; values
8
8
  * already set in the process win. Loaded secrets are never printed.
@@ -76,7 +76,6 @@ function turbo(root, task, args = [], { developmentTasks = ["dev", "build"], env
76
76
  ...args
77
77
  ], root, env);
78
78
  }
79
- //#endregion
80
79
  export { developmentEnvironment, run, turbo };
81
80
 
82
81
  //# sourceMappingURL=process.mjs.map
@@ -0,0 +1,13 @@
1
+ /** Plain Vitest defaults for Bun workers; consumers own suites, setup and environments. */
2
+ export declare const bunTestDefaults: {
3
+ test: {
4
+ environment: "node";
5
+ maxWorkers: number;
6
+ execArgv: string[];
7
+ coverage: {
8
+ provider: "v8";
9
+ reporter: ("json" | "lcov" | "text")[];
10
+ };
11
+ };
12
+ };
13
+ //# sourceMappingURL=testing.d.mts.map
@@ -0,0 +1,18 @@
1
+ // src/testing.ts
2
+ /** Plain Vitest defaults for Bun workers; consumers own suites, setup and environments. */
3
+ const bunTestDefaults = { test: {
4
+ environment: "node",
5
+ maxWorkers: 1,
6
+ execArgv: ["--no-env-file", "--no-install"],
7
+ coverage: {
8
+ provider: "v8",
9
+ reporter: [
10
+ "text",
11
+ "json",
12
+ "lcov"
13
+ ]
14
+ }
15
+ } };
16
+ export { bunTestDefaults };
17
+
18
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../src/testing.ts"],"sourcesContent":["import type { ViteUserConfig } from \"vite-plus\";\n\n/** Plain Vitest defaults for Bun workers; consumers own suites, setup and environments. */\nexport const bunTestDefaults = {\n test: {\n environment: \"node\",\n maxWorkers: 1,\n execArgv: [\"--no-env-file\", \"--no-install\"],\n coverage: { provider: \"v8\", reporter: [\"text\", \"json\", \"lcov\"] },\n },\n} satisfies ViteUserConfig;\n"],"mappings":";;AAGA,MAAa,kBAAkB,EAC7B,MAAM;CACJ,aAAa;CACb,YAAY;CACZ,UAAU,CAAC,iBAAiB,cAAc;CAC1C,UAAU;EAAE,UAAU;EAAM,UAAU;GAAC;GAAQ;GAAQ;EAAM;CAAE;AACjE,EACF"}
@@ -1,9 +1,10 @@
1
- //#region src/toolchain.d.ts
2
1
  export interface Toolchain {
3
2
  node: string;
4
3
  bun: string;
5
4
  }
6
5
  export interface ToolchainOptions {
6
+ /** Bun-only repositories forbid Node runtime pins; the default checks both runtimes. */
7
+ runtime?: "node-bun" | "bun";
7
8
  /** Repository-relative documents whose `Node x.y.z` and `Bun x.y.z` mentions must match. */
8
9
  documents?: readonly string[];
9
10
  /** Files every Turbo cache key must include when `turbo.json` exists. */
@@ -13,6 +14,12 @@ export interface RuntimeOptions {
13
14
  /** Environment variable that may select a Node executable outside PATH. */
14
15
  nodeBinaryEnv?: string;
15
16
  }
17
+ /** Read the exact Bun pin without requiring a Node development runtime. */
18
+ export declare function readBunPin(root: string): string;
19
+ /** Check the active Bun runtime without executing Node or inspected repository code. */
20
+ export declare function assertBunRuntime(root: string): {
21
+ bun: string;
22
+ };
16
23
  /** `.nvmrc` pins Node and the root `packageManager` pins Bun; every other pin follows them. */
17
24
  export declare function readToolchain(root: string): Toolchain;
18
25
  /** Returns the Node executable after confirming it provides the pinned release. */
@@ -22,6 +29,5 @@ export declare function assertToolchainRuntime(root: string, options?: RuntimeOp
22
29
  nodeBinary: string;
23
30
  };
24
31
  /** Check duplicated declarations without installing tools or running application code. */
25
- export declare function checkToolchain(root: string, { documents, turboGlobalDependencies }?: ToolchainOptions): string[];
26
- //#endregion
32
+ export declare function checkToolchain(root: string, { runtime, documents, turboGlobalDependencies }?: ToolchainOptions): string[];
27
33
  //# sourceMappingURL=toolchain.d.mts.map
@@ -4,7 +4,7 @@ import { join } from "node:path";
4
4
  import { execFileSync } from "node:child_process";
5
5
  import { parseTOML } from "confbox/toml";
6
6
  import { parseYAML } from "confbox/yaml";
7
- //#region src/toolchain.ts
7
+ // src/toolchain.ts
8
8
  const exactVersion = /^\d+\.\d+\.\d+$/;
9
9
  const manifestAt = (root, file = "package.json") => JSON.parse(readFileSync(join(root, file), "utf8"));
10
10
  const catalogsOf = (manifest) => {
@@ -13,10 +13,26 @@ const catalogsOf = (manifest) => {
13
13
  const { catalog, catalogs } = workspaces;
14
14
  return [...catalog ? [catalog] : [], ...Object.values(catalogs ?? {})];
15
15
  };
16
+ const bunVersion = (root) => /^bun@(\d+\.\d+\.\d+)$/.exec(manifestAt(root).packageManager ?? "")?.[1];
17
+ /** Read the exact Bun pin without requiring a Node development runtime. */
18
+ function readBunPin(root) {
19
+ const bun = bunVersion(root);
20
+ if (!bun) throw new Error("Pin an exact Bun version in package.json packageManager.");
21
+ return bun;
22
+ }
23
+ function assertBunVersion(bun) {
24
+ if (process.versions.bun !== bun) throw new Error(`Expected Bun ${bun}, got ${process.versions.bun ?? "none"}.`);
25
+ }
26
+ /** Check the active Bun runtime without executing Node or inspected repository code. */
27
+ function assertBunRuntime(root) {
28
+ const bun = readBunPin(root);
29
+ assertBunVersion(bun);
30
+ return { bun };
31
+ }
16
32
  /** `.nvmrc` pins Node and the root `packageManager` pins Bun; every other pin follows them. */
17
33
  function readToolchain(root) {
18
34
  const node = readFileSync(join(root, ".nvmrc"), "utf8").trim().replace(/^v/, "");
19
- const bun = /^bun@(.+)$/.exec(manifestAt(root).packageManager ?? "")?.[1] ?? "";
35
+ const bun = bunVersion(root) ?? "";
20
36
  if (!exactVersion.test(node) || !exactVersion.test(bun)) throw new Error("Pin exact Node and Bun versions in .nvmrc and package.json packageManager.");
21
37
  return {
22
38
  node,
@@ -42,7 +58,7 @@ function pinnedNode(root, { nodeBinaryEnv = "DEVKIT_NODE_BINARY" } = {}) {
42
58
  /** The binary override changes the consumer executable, never the required version. */
43
59
  function assertToolchainRuntime(root, options = {}) {
44
60
  const pins = readToolchain(root);
45
- if (process.versions.bun !== pins.bun) throw new Error(`Expected Bun ${pins.bun}, got ${process.versions.bun ?? "none"}.`);
61
+ assertBunVersion(pins.bun);
46
62
  return {
47
63
  ...pins,
48
64
  nodeBinary: pinnedNode(root, options)
@@ -51,7 +67,7 @@ function assertToolchainRuntime(root, options = {}) {
51
67
  function isRecord(value) {
52
68
  return typeof value === "object" && value !== null && !Array.isArray(value);
53
69
  }
54
- function checkMise(root) {
70
+ function checkMise(root, bunOnly) {
55
71
  const file = "mise.toml";
56
72
  const path = join(root, file);
57
73
  if (!existsSync(path)) return [`${file}: missing runtime manager configuration`];
@@ -65,10 +81,11 @@ function checkMise(root) {
65
81
  const findings = [];
66
82
  if (Object.hasOwn(mise, "tools")) findings.push(`${file}: read existing pins instead of declaring [tools] versions`);
67
83
  const tools = isRecord(mise.settings) ? mise.settings.idiomatic_version_file_enable_tools : void 0;
68
- if (!Array.isArray(tools) || tools.length !== 2 || !["node", "bun"].every((tool) => tools.includes(tool))) findings.push(`${file}: enable idiomatic version files for node and bun`);
84
+ const expected = bunOnly ? ["bun"] : ["node", "bun"];
85
+ if (!Array.isArray(tools) || tools.length !== expected.length || !expected.every((tool) => tools.includes(tool))) findings.push(`${file}: enable idiomatic version files for ${bunOnly ? "bun only" : "node and bun"}`);
69
86
  return findings;
70
87
  }
71
- function checkSetupStep(file, location, step) {
88
+ function checkSetupStep(file, location, step, bunOnly) {
72
89
  if (typeof step.uses !== "string") return [];
73
90
  const refSeparator = step.uses.indexOf("@");
74
91
  if (refSeparator < 0) return [];
@@ -84,6 +101,7 @@ function checkSetupStep(file, location, step) {
84
101
  ]].find(([, action]) => repository === action);
85
102
  if (!setup) return [];
86
103
  const [tool, , source] = setup;
104
+ if (bunOnly && tool === "node") return [`${file}: remove actions/setup-node; Bun is the only runtime`];
87
105
  const findings = [];
88
106
  if (step.with !== void 0 && !isRecord(step.with)) findings.push(`${file}: ${location}.with must be a mapping`);
89
107
  const inputs = isRecord(step.with) ? step.with : {};
@@ -93,7 +111,7 @@ function checkSetupStep(file, location, step) {
93
111
  else if (versionFile !== source) findings.push(`${file}: ${tool}-version-file must be ${source}`);
94
112
  return findings;
95
113
  }
96
- function checkSetupSteps(file, location, steps) {
114
+ function checkSetupSteps(file, location, steps, bunOnly) {
97
115
  if (!Array.isArray(steps)) return [`${file}: ${location} must be an array`];
98
116
  const findings = [];
99
117
  for (const [index, step] of steps.entries()) {
@@ -102,11 +120,11 @@ function checkSetupSteps(file, location, steps) {
102
120
  findings.push(`${file}: ${stepLocation} must be a mapping`);
103
121
  continue;
104
122
  }
105
- findings.push(...checkSetupStep(file, stepLocation, step));
123
+ findings.push(...checkSetupStep(file, stepLocation, step, bunOnly));
106
124
  }
107
125
  return findings;
108
126
  }
109
- function checkWorkflows(root) {
127
+ function checkWorkflows(root, bunOnly) {
110
128
  const findings = [];
111
129
  for (const file of globSync(".github/workflows/*.{yml,yaml}", { cwd: root })) {
112
130
  let workflow;
@@ -127,12 +145,12 @@ function checkWorkflows(root) {
127
145
  continue;
128
146
  }
129
147
  if (job.steps === void 0) continue;
130
- findings.push(...checkSetupSteps(file, `${location}.steps`, job.steps));
148
+ findings.push(...checkSetupSteps(file, `${location}.steps`, job.steps, bunOnly));
131
149
  }
132
150
  }
133
151
  return findings;
134
152
  }
135
- function checkCompositeActions(root) {
153
+ function checkCompositeActions(root, bunOnly) {
136
154
  const findings = [];
137
155
  for (const file of globSync(".github/actions/**/action.{yml,yaml}", { cwd: root })) {
138
156
  let action;
@@ -147,15 +165,16 @@ function checkCompositeActions(root) {
147
165
  continue;
148
166
  }
149
167
  if (action.runs.using !== "composite") continue;
150
- findings.push(...checkSetupSteps(file, "runs.steps", action.runs.steps));
168
+ findings.push(...checkSetupSteps(file, "runs.steps", action.runs.steps, bunOnly));
151
169
  }
152
170
  return findings;
153
171
  }
154
172
  /** Check duplicated declarations without installing tools or running application code. */
155
- function checkToolchain(root, { documents = ["README.md", "AGENTS.md"], turboGlobalDependencies = [".nvmrc", "mise.toml"] } = {}) {
173
+ function checkToolchain(root, { runtime = "node-bun", documents = ["README.md", "AGENTS.md"], turboGlobalDependencies = runtime === "bun" ? ["mise.toml"] : [".nvmrc", "mise.toml"] } = {}) {
174
+ const bunOnly = runtime === "bun";
156
175
  let pins;
157
176
  try {
158
- pins = readToolchain(root);
177
+ pins = bunOnly ? { bun: readBunPin(root) } : readToolchain(root);
159
178
  } catch (error) {
160
179
  return [error instanceof Error ? error.message : String(error)];
161
180
  }
@@ -164,23 +183,26 @@ function checkToolchain(root, { documents = ["README.md", "AGENTS.md"], turboGlo
164
183
  const expect = (file, label, actual, expected) => {
165
184
  if (actual !== expected) findings.push(`${file}: ${label} is ${actual ?? "missing"}, expected ${expected}`);
166
185
  };
167
- expect("package.json", "engines.node", manifest.engines?.node, pins.node);
186
+ if (bunOnly) {
187
+ if (manifest.engines?.node !== void 0) findings.push("package.json: remove engines.node; Bun is the only runtime");
188
+ } else expect("package.json", "engines.node", manifest.engines?.node, pins.node);
168
189
  for (const key of ["packageManager", "runtime"]) {
169
190
  const engine = manifest.devEngines?.[key];
170
191
  expect("package.json", `devEngines.${key}`, `${engine?.name}@${engine?.version}`, `bun@${pins.bun}`);
171
192
  expect("package.json", `devEngines.${key}.onFail`, engine?.onFail, "error");
172
193
  }
173
194
  for (const file of [
195
+ ...bunOnly ? [".nvmrc"] : [],
174
196
  ".node-version",
175
197
  ".bun-version",
176
198
  ".tool-versions"
177
- ]) if (existsSync(join(root, file))) findings.push(`${file}: use .nvmrc and package.json instead of a second version source`);
178
- findings.push(...checkMise(root));
179
- const nodeMajor = pins.node.split(".")[0];
199
+ ]) if (existsSync(join(root, file))) findings.push(bunOnly ? `${file}: package.json packageManager is the only runtime pin` : `${file}: use .nvmrc and package.json instead of a second version source`);
200
+ findings.push(...checkMise(root, bunOnly));
201
+ const nodeMajor = pins.node?.split(".")[0];
180
202
  const version = (range) => range && !/^[a-z]+:/.test(range) ? range.replace(/^[\^~]/, "") : void 0;
181
203
  const checkTypes = (file, dependencies) => {
182
204
  const node = version(dependencies?.["@types/node"]);
183
- if (node && node.split(".")[0] !== nodeMajor) findings.push(`${file}: @types/node must match the Node ${nodeMajor} major`);
205
+ if (nodeMajor && node && node.split(".")[0] !== nodeMajor) findings.push(`${file}: @types/node must match the Node ${nodeMajor} major`);
184
206
  const bun = version(dependencies?.["@types/bun"]);
185
207
  if (bun) expect(file, "@types/bun", bun, pins.bun);
186
208
  };
@@ -191,14 +213,17 @@ function checkToolchain(root, { documents = ["README.md", "AGENTS.md"], turboGlo
191
213
  exclude: ["**/node_modules/**"]
192
214
  })) {
193
215
  const source = readFileSync(join(root, file), "utf8");
194
- for (const [, version] of source.matchAll(/^FROM\s+node:(\d+\.\d+\.\d+)/gm)) expect(file, "node image", version, pins.node);
216
+ if (bunOnly) {
217
+ if (/^\s*FROM\s+(?:--platform=\S+\s+)?node:/im.test(source)) findings.push(`${file}: build and run on oven/bun images`);
218
+ } else for (const [, version] of source.matchAll(/^FROM\s+node:(\d+\.\d+\.\d+)/gm)) expect(file, "node image", version, pins.node);
195
219
  for (const [, version] of source.matchAll(/^FROM\s+oven\/bun:(\d+\.\d+\.\d+)/gm)) expect(file, "bun image", version, pins.bun);
196
220
  }
197
- findings.push(...checkWorkflows(root), ...checkCompositeActions(root));
221
+ findings.push(...checkWorkflows(root, bunOnly), ...checkCompositeActions(root, bunOnly));
198
222
  for (const file of documents) {
199
223
  if (!existsSync(join(root, file))) continue;
200
224
  const document = readFileSync(join(root, file), "utf8").replaceAll("**", "");
201
- for (const [, tool, version] of document.matchAll(/\b(Node|Bun) v?(\d+\.\d+\.\d+)\b/g)) expect(file, `documented ${tool}`, version, tool === "Node" ? pins.node : pins.bun);
225
+ for (const [, tool, version] of document.matchAll(/\b(Node|Bun) v?(\d+\.\d+\.\d+)\b/g)) if (bunOnly && tool === "Node") findings.push(`${file}: documents a Node release; Bun is the only runtime`);
226
+ else expect(file, `documented ${tool}`, version, tool === "Node" ? pins.node : pins.bun);
202
227
  }
203
228
  const turboFile = join(root, "turbo.json");
204
229
  if (existsSync(turboFile)) {
@@ -207,7 +232,6 @@ function checkToolchain(root, { documents = ["README.md", "AGENTS.md"], turboGlo
207
232
  }
208
233
  return [...new Set(findings)];
209
234
  }
210
- //#endregion
211
- export { assertToolchainRuntime, checkToolchain, pinnedNode, readToolchain };
235
+ export { assertBunRuntime, assertToolchainRuntime, checkToolchain, pinnedNode, readBunPin, readToolchain };
212
236
 
213
237
  //# sourceMappingURL=toolchain.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"toolchain.mjs","names":[],"sources":["../src/toolchain.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { existsSync, globSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseTOML } from \"confbox/toml\";\nimport { parseYAML } from \"confbox/yaml\";\nimport { workspacePatterns } from \"./workspaces.js\";\n\nexport interface Toolchain {\n node: string;\n bun: string;\n}\n\nexport interface ToolchainOptions {\n /** Repository-relative documents whose `Node x.y.z` and `Bun x.y.z` mentions must match. */\n documents?: readonly string[];\n /** Files every Turbo cache key must include when `turbo.json` exists. */\n turboGlobalDependencies?: readonly string[];\n}\n\nexport interface RuntimeOptions {\n /** Environment variable that may select a Node executable outside PATH. */\n nodeBinaryEnv?: string;\n}\n\ninterface Manifest {\n packageManager?: string;\n engines?: { node?: string };\n devEngines?: Record<string, { name?: string; version?: string; onFail?: string }>;\n devDependencies?: Record<string, string>;\n workspaces?: unknown;\n}\n\nconst exactVersion = /^\\d+\\.\\d+\\.\\d+$/;\nconst manifestAt = (root: string, file = \"package.json\"): Manifest =>\n JSON.parse(readFileSync(join(root, file), \"utf8\")) as Manifest;\nconst catalogsOf = (manifest: Manifest): Record<string, string>[] => {\n const workspaces = manifest.workspaces;\n if (!workspaces || typeof workspaces !== \"object\" || Array.isArray(workspaces)) return [];\n const { catalog, catalogs } = workspaces as {\n catalog?: Record<string, string>;\n catalogs?: Record<string, Record<string, string>>;\n };\n return [...(catalog ? [catalog] : []), ...Object.values(catalogs ?? {})];\n};\n\n/** `.nvmrc` pins Node and the root `packageManager` pins Bun; every other pin follows them. */\nexport function readToolchain(root: string): Toolchain {\n const node = readFileSync(join(root, \".nvmrc\"), \"utf8\").trim().replace(/^v/, \"\");\n const bun = /^bun@(.+)$/.exec(manifestAt(root).packageManager ?? \"\")?.[1] ?? \"\";\n if (!exactVersion.test(node) || !exactVersion.test(bun))\n throw new Error(\"Pin exact Node and Bun versions in .nvmrc and package.json packageManager.\");\n return { node, bun };\n}\n\n/** Returns the Node executable after confirming it provides the pinned release. */\nexport function pinnedNode(\n root: string,\n { nodeBinaryEnv = \"DEVKIT_NODE_BINARY\" }: RuntimeOptions = {},\n) {\n const pins = readToolchain(root);\n const nodeBinary = process.env[nodeBinaryEnv] || \"node\";\n let node: string;\n try {\n node = execFileSync(nodeBinary, [\"--version\"], { encoding: \"utf8\", stdio: \"pipe\" }).trim();\n } catch {\n throw new Error(\n `Node is unavailable; select Node ${pins.node} from .nvmrc or set ${nodeBinaryEnv}.`,\n );\n }\n if (node !== `v${pins.node}`)\n throw new Error(\n `Expected Node ${pins.node}, got ${node}; select the pinned Node from .nvmrc or set ${nodeBinaryEnv}.`,\n );\n return nodeBinary;\n}\n\n/** The binary override changes the consumer executable, never the required version. */\nexport function assertToolchainRuntime(\n root: string,\n options: RuntimeOptions = {},\n): Toolchain & { nodeBinary: string } {\n const pins = readToolchain(root);\n if (process.versions.bun !== pins.bun)\n throw new Error(`Expected Bun ${pins.bun}, got ${process.versions.bun ?? \"none\"}.`);\n return { ...pins, nodeBinary: pinnedNode(root, options) };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction checkMise(root: string): string[] {\n const file = \"mise.toml\";\n const path = join(root, file);\n if (!existsSync(path)) return [`${file}: missing runtime manager configuration`];\n let mise: unknown;\n try {\n mise = parseTOML(readFileSync(path, \"utf8\"));\n } catch {\n return [`${file}: invalid TOML configuration`];\n }\n if (!isRecord(mise)) return [`${file}: configuration must be a table`];\n const findings: string[] = [];\n if (Object.hasOwn(mise, \"tools\"))\n findings.push(`${file}: read existing pins instead of declaring [tools] versions`);\n const tools = isRecord(mise.settings)\n ? mise.settings.idiomatic_version_file_enable_tools\n : undefined;\n if (\n !Array.isArray(tools) ||\n tools.length !== 2 ||\n ![\"node\", \"bun\"].every((tool) => tools.includes(tool))\n )\n findings.push(`${file}: enable idiomatic version files for node and bun`);\n return findings;\n}\n\nfunction checkSetupStep(file: string, location: string, step: Record<string, unknown>): string[] {\n if (typeof step.uses !== \"string\") return [];\n const refSeparator = step.uses.indexOf(\"@\");\n if (refSeparator < 0) return [];\n const repository = step.uses.slice(0, refSeparator).toLowerCase();\n const setup = (\n [\n [\"node\", \"actions/setup-node\", \".nvmrc\"],\n [\"bun\", \"oven-sh/setup-bun\", \"package.json\"],\n ] as const\n ).find(([, action]) => repository === action);\n if (!setup) return [];\n const [tool, , source] = setup;\n const findings: string[] = [];\n if (step.with !== undefined && !isRecord(step.with))\n findings.push(`${file}: ${location}.with must be a mapping`);\n const inputs = isRecord(step.with) ? step.with : {};\n if (Object.hasOwn(inputs, `${tool}-version`))\n findings.push(`${file}: read ${tool}-version-file: ${source} instead of a literal version`);\n const versionFile = inputs[`${tool}-version-file`];\n if (versionFile === undefined) findings.push(`${file}: every ${tool} setup must read ${source}`);\n else if (versionFile !== source) findings.push(`${file}: ${tool}-version-file must be ${source}`);\n return findings;\n}\n\nfunction checkSetupSteps(file: string, location: string, steps: unknown): string[] {\n if (!Array.isArray(steps)) return [`${file}: ${location} must be an array`];\n const findings: string[] = [];\n for (const [index, step] of steps.entries()) {\n const stepLocation = `${location}[${index}]`;\n if (!isRecord(step)) {\n findings.push(`${file}: ${stepLocation} must be a mapping`);\n continue;\n }\n findings.push(...checkSetupStep(file, stepLocation, step));\n }\n return findings;\n}\n\nfunction checkWorkflows(root: string): string[] {\n const findings: string[] = [];\n for (const file of globSync(\".github/workflows/*.{yml,yaml}\", { cwd: root })) {\n let workflow: unknown;\n try {\n workflow = parseYAML(readFileSync(join(root, file), \"utf8\"), { filename: file });\n } catch {\n findings.push(`${file}: invalid YAML configuration`);\n continue;\n }\n if (!isRecord(workflow) || !isRecord(workflow.jobs)) {\n findings.push(`${file}: workflow jobs must be a mapping`);\n continue;\n }\n for (const [name, job] of Object.entries(workflow.jobs)) {\n const location = `jobs.${name}`;\n if (!isRecord(job)) {\n findings.push(`${file}: ${location} must be a mapping`);\n continue;\n }\n if (job.steps === undefined) continue;\n findings.push(...checkSetupSteps(file, `${location}.steps`, job.steps));\n }\n }\n return findings;\n}\n\nfunction checkCompositeActions(root: string): string[] {\n const findings: string[] = [];\n for (const file of globSync(\".github/actions/**/action.{yml,yaml}\", { cwd: root })) {\n let action: unknown;\n try {\n action = parseYAML(readFileSync(join(root, file), \"utf8\"), { filename: file });\n } catch {\n findings.push(`${file}: invalid YAML configuration`);\n continue;\n }\n if (!isRecord(action) || !isRecord(action.runs)) {\n findings.push(`${file}: action runs must be a mapping`);\n continue;\n }\n if (action.runs.using !== \"composite\") continue;\n findings.push(...checkSetupSteps(file, \"runs.steps\", action.runs.steps));\n }\n return findings;\n}\n\n/** Check duplicated declarations without installing tools or running application code. */\nexport function checkToolchain(\n root: string,\n {\n documents = [\"README.md\", \"AGENTS.md\"],\n turboGlobalDependencies = [\".nvmrc\", \"mise.toml\"],\n }: ToolchainOptions = {},\n): string[] {\n let pins: Toolchain;\n try {\n pins = readToolchain(root);\n } catch (error) {\n return [error instanceof Error ? error.message : String(error)];\n }\n const findings: string[] = [];\n const manifest = manifestAt(root);\n const expect = (file: string, label: string, actual: string | undefined, expected: string) => {\n if (actual !== expected)\n findings.push(`${file}: ${label} is ${actual ?? \"missing\"}, expected ${expected}`);\n };\n expect(\"package.json\", \"engines.node\", manifest.engines?.node, pins.node);\n for (const key of [\"packageManager\", \"runtime\"]) {\n const engine = manifest.devEngines?.[key];\n expect(\n \"package.json\",\n `devEngines.${key}`,\n `${engine?.name}@${engine?.version}`,\n `bun@${pins.bun}`,\n );\n expect(\"package.json\", `devEngines.${key}.onFail`, engine?.onFail, \"error\");\n }\n for (const file of [\".node-version\", \".bun-version\", \".tool-versions\"])\n if (existsSync(join(root, file)))\n findings.push(`${file}: use .nvmrc and package.json instead of a second version source`);\n findings.push(...checkMise(root));\n\n // Runtime type packages follow the pins: @types/node by major, @types/bun exactly.\n const nodeMajor = pins.node.split(\".\")[0]!;\n // Protocol ranges such as catalog: and workspace: are checked where they resolve.\n const version = (range: string | undefined) =>\n range && !/^[a-z]+:/.test(range) ? range.replace(/^[\\^~]/, \"\") : undefined;\n const checkTypes = (file: string, dependencies: Record<string, string> | undefined) => {\n const node = version(dependencies?.[\"@types/node\"]);\n if (node && node.split(\".\")[0] !== nodeMajor)\n findings.push(`${file}: @types/node must match the Node ${nodeMajor} major`);\n const bun = version(dependencies?.[\"@types/bun\"]);\n if (bun) expect(file, \"@types/bun\", bun, pins.bun);\n };\n for (const catalog of catalogsOf(manifest)) checkTypes(\"package.json catalog\", catalog);\n for (const file of [\n \"package.json\",\n ...globSync(\n workspacePatterns(manifest).map((path) => `${path}/package.json`),\n { cwd: root },\n ),\n ])\n checkTypes(file, manifestAt(root, file).devDependencies);\n\n for (const file of globSync(\"**/Dockerfile\", { cwd: root, exclude: [\"**/node_modules/**\"] })) {\n const source = readFileSync(join(root, file), \"utf8\");\n for (const [, version] of source.matchAll(/^FROM\\s+node:(\\d+\\.\\d+\\.\\d+)/gm))\n expect(file, \"node image\", version, pins.node);\n for (const [, version] of source.matchAll(/^FROM\\s+oven\\/bun:(\\d+\\.\\d+\\.\\d+)/gm))\n expect(file, \"bun image\", version, pins.bun);\n }\n findings.push(...checkWorkflows(root), ...checkCompositeActions(root));\n for (const file of documents) {\n if (!existsSync(join(root, file))) continue;\n const document = readFileSync(join(root, file), \"utf8\").replaceAll(\"**\", \"\");\n for (const [, tool, version] of document.matchAll(/\\b(Node|Bun) v?(\\d+\\.\\d+\\.\\d+)\\b/g))\n expect(file, `documented ${tool}`, version, tool === \"Node\" ? pins.node : pins.bun);\n }\n const turboFile = join(root, \"turbo.json\");\n if (existsSync(turboFile)) {\n const turbo = JSON.parse(readFileSync(turboFile, \"utf8\")) as { globalDependencies?: string[] };\n for (const file of turboGlobalDependencies)\n if (!turbo.globalDependencies?.includes(file))\n findings.push(`turbo.json: include ${file} in globalDependencies`);\n }\n return [...new Set(findings)];\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"toolchain.mjs","names":[],"sources":["../src/toolchain.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { existsSync, globSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parseTOML } from \"confbox/toml\";\nimport { parseYAML } from \"confbox/yaml\";\nimport { workspacePatterns } from \"./workspaces.js\";\n\nexport interface Toolchain {\n node: string;\n bun: string;\n}\n\nexport interface ToolchainOptions {\n /** Bun-only repositories forbid Node runtime pins; the default checks both runtimes. */\n runtime?: \"node-bun\" | \"bun\";\n /** Repository-relative documents whose `Node x.y.z` and `Bun x.y.z` mentions must match. */\n documents?: readonly string[];\n /** Files every Turbo cache key must include when `turbo.json` exists. */\n turboGlobalDependencies?: readonly string[];\n}\n\nexport interface RuntimeOptions {\n /** Environment variable that may select a Node executable outside PATH. */\n nodeBinaryEnv?: string;\n}\n\ninterface Manifest {\n packageManager?: string;\n engines?: { node?: string };\n devEngines?: Record<string, { name?: string; version?: string; onFail?: string }>;\n devDependencies?: Record<string, string>;\n workspaces?: unknown;\n}\n\nconst exactVersion = /^\\d+\\.\\d+\\.\\d+$/;\nconst manifestAt = (root: string, file = \"package.json\"): Manifest =>\n JSON.parse(readFileSync(join(root, file), \"utf8\")) as Manifest;\nconst catalogsOf = (manifest: Manifest): Record<string, string>[] => {\n const workspaces = manifest.workspaces;\n if (!workspaces || typeof workspaces !== \"object\" || Array.isArray(workspaces)) return [];\n const { catalog, catalogs } = workspaces as {\n catalog?: Record<string, string>;\n catalogs?: Record<string, Record<string, string>>;\n };\n return [...(catalog ? [catalog] : []), ...Object.values(catalogs ?? {})];\n};\n\nconst bunVersion = (root: string) =>\n /^bun@(\\d+\\.\\d+\\.\\d+)$/.exec(manifestAt(root).packageManager ?? \"\")?.[1];\n\n/** Read the exact Bun pin without requiring a Node development runtime. */\nexport function readBunPin(root: string): string {\n const bun = bunVersion(root);\n if (!bun) throw new Error(\"Pin an exact Bun version in package.json packageManager.\");\n return bun;\n}\n\nfunction assertBunVersion(bun: string) {\n if (process.versions.bun !== bun)\n throw new Error(`Expected Bun ${bun}, got ${process.versions.bun ?? \"none\"}.`);\n}\n\n/** Check the active Bun runtime without executing Node or inspected repository code. */\nexport function assertBunRuntime(root: string): { bun: string } {\n const bun = readBunPin(root);\n assertBunVersion(bun);\n return { bun };\n}\n\n/** `.nvmrc` pins Node and the root `packageManager` pins Bun; every other pin follows them. */\nexport function readToolchain(root: string): Toolchain {\n const node = readFileSync(join(root, \".nvmrc\"), \"utf8\").trim().replace(/^v/, \"\");\n const bun = bunVersion(root) ?? \"\";\n if (!exactVersion.test(node) || !exactVersion.test(bun))\n throw new Error(\"Pin exact Node and Bun versions in .nvmrc and package.json packageManager.\");\n return { node, bun };\n}\n\n/** Returns the Node executable after confirming it provides the pinned release. */\nexport function pinnedNode(\n root: string,\n { nodeBinaryEnv = \"DEVKIT_NODE_BINARY\" }: RuntimeOptions = {},\n) {\n const pins = readToolchain(root);\n const nodeBinary = process.env[nodeBinaryEnv] || \"node\";\n let node: string;\n try {\n node = execFileSync(nodeBinary, [\"--version\"], { encoding: \"utf8\", stdio: \"pipe\" }).trim();\n } catch {\n throw new Error(\n `Node is unavailable; select Node ${pins.node} from .nvmrc or set ${nodeBinaryEnv}.`,\n );\n }\n if (node !== `v${pins.node}`)\n throw new Error(\n `Expected Node ${pins.node}, got ${node}; select the pinned Node from .nvmrc or set ${nodeBinaryEnv}.`,\n );\n return nodeBinary;\n}\n\n/** The binary override changes the consumer executable, never the required version. */\nexport function assertToolchainRuntime(\n root: string,\n options: RuntimeOptions = {},\n): Toolchain & { nodeBinary: string } {\n const pins = readToolchain(root);\n assertBunVersion(pins.bun);\n return { ...pins, nodeBinary: pinnedNode(root, options) };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction checkMise(root: string, bunOnly: boolean): string[] {\n const file = \"mise.toml\";\n const path = join(root, file);\n if (!existsSync(path)) return [`${file}: missing runtime manager configuration`];\n let mise: unknown;\n try {\n mise = parseTOML(readFileSync(path, \"utf8\"));\n } catch {\n return [`${file}: invalid TOML configuration`];\n }\n if (!isRecord(mise)) return [`${file}: configuration must be a table`];\n const findings: string[] = [];\n if (Object.hasOwn(mise, \"tools\"))\n findings.push(`${file}: read existing pins instead of declaring [tools] versions`);\n const tools = isRecord(mise.settings)\n ? mise.settings.idiomatic_version_file_enable_tools\n : undefined;\n const expected = bunOnly ? [\"bun\"] : [\"node\", \"bun\"];\n if (\n !Array.isArray(tools) ||\n tools.length !== expected.length ||\n !expected.every((tool) => tools.includes(tool))\n )\n findings.push(\n `${file}: enable idiomatic version files for ${bunOnly ? \"bun only\" : \"node and bun\"}`,\n );\n return findings;\n}\n\nfunction checkSetupStep(\n file: string,\n location: string,\n step: Record<string, unknown>,\n bunOnly: boolean,\n): string[] {\n if (typeof step.uses !== \"string\") return [];\n const refSeparator = step.uses.indexOf(\"@\");\n if (refSeparator < 0) return [];\n const repository = step.uses.slice(0, refSeparator).toLowerCase();\n const setup = (\n [\n [\"node\", \"actions/setup-node\", \".nvmrc\"],\n [\"bun\", \"oven-sh/setup-bun\", \"package.json\"],\n ] as const\n ).find(([, action]) => repository === action);\n if (!setup) return [];\n const [tool, , source] = setup;\n if (bunOnly && tool === \"node\")\n return [`${file}: remove actions/setup-node; Bun is the only runtime`];\n const findings: string[] = [];\n if (step.with !== undefined && !isRecord(step.with))\n findings.push(`${file}: ${location}.with must be a mapping`);\n const inputs = isRecord(step.with) ? step.with : {};\n if (Object.hasOwn(inputs, `${tool}-version`))\n findings.push(`${file}: read ${tool}-version-file: ${source} instead of a literal version`);\n const versionFile = inputs[`${tool}-version-file`];\n if (versionFile === undefined) findings.push(`${file}: every ${tool} setup must read ${source}`);\n else if (versionFile !== source) findings.push(`${file}: ${tool}-version-file must be ${source}`);\n return findings;\n}\n\nfunction checkSetupSteps(\n file: string,\n location: string,\n steps: unknown,\n bunOnly: boolean,\n): string[] {\n if (!Array.isArray(steps)) return [`${file}: ${location} must be an array`];\n const findings: string[] = [];\n for (const [index, step] of steps.entries()) {\n const stepLocation = `${location}[${index}]`;\n if (!isRecord(step)) {\n findings.push(`${file}: ${stepLocation} must be a mapping`);\n continue;\n }\n findings.push(...checkSetupStep(file, stepLocation, step, bunOnly));\n }\n return findings;\n}\n\nfunction checkWorkflows(root: string, bunOnly: boolean): string[] {\n const findings: string[] = [];\n for (const file of globSync(\".github/workflows/*.{yml,yaml}\", { cwd: root })) {\n let workflow: unknown;\n try {\n workflow = parseYAML(readFileSync(join(root, file), \"utf8\"), { filename: file });\n } catch {\n findings.push(`${file}: invalid YAML configuration`);\n continue;\n }\n if (!isRecord(workflow) || !isRecord(workflow.jobs)) {\n findings.push(`${file}: workflow jobs must be a mapping`);\n continue;\n }\n for (const [name, job] of Object.entries(workflow.jobs)) {\n const location = `jobs.${name}`;\n if (!isRecord(job)) {\n findings.push(`${file}: ${location} must be a mapping`);\n continue;\n }\n if (job.steps === undefined) continue;\n findings.push(...checkSetupSteps(file, `${location}.steps`, job.steps, bunOnly));\n }\n }\n return findings;\n}\n\nfunction checkCompositeActions(root: string, bunOnly: boolean): string[] {\n const findings: string[] = [];\n for (const file of globSync(\".github/actions/**/action.{yml,yaml}\", { cwd: root })) {\n let action: unknown;\n try {\n action = parseYAML(readFileSync(join(root, file), \"utf8\"), { filename: file });\n } catch {\n findings.push(`${file}: invalid YAML configuration`);\n continue;\n }\n if (!isRecord(action) || !isRecord(action.runs)) {\n findings.push(`${file}: action runs must be a mapping`);\n continue;\n }\n if (action.runs.using !== \"composite\") continue;\n findings.push(...checkSetupSteps(file, \"runs.steps\", action.runs.steps, bunOnly));\n }\n return findings;\n}\n\n/** Check duplicated declarations without installing tools or running application code. */\nexport function checkToolchain(\n root: string,\n {\n runtime = \"node-bun\",\n documents = [\"README.md\", \"AGENTS.md\"],\n turboGlobalDependencies = runtime === \"bun\" ? [\"mise.toml\"] : [\".nvmrc\", \"mise.toml\"],\n }: ToolchainOptions = {},\n): string[] {\n const bunOnly = runtime === \"bun\";\n let pins: { bun: string; node?: string };\n try {\n pins = bunOnly ? { bun: readBunPin(root) } : readToolchain(root);\n } catch (error) {\n return [error instanceof Error ? error.message : String(error)];\n }\n const findings: string[] = [];\n const manifest = manifestAt(root);\n const expect = (file: string, label: string, actual: string | undefined, expected: string) => {\n if (actual !== expected)\n findings.push(`${file}: ${label} is ${actual ?? \"missing\"}, expected ${expected}`);\n };\n if (bunOnly) {\n if (manifest.engines?.node !== undefined)\n findings.push(\"package.json: remove engines.node; Bun is the only runtime\");\n } else expect(\"package.json\", \"engines.node\", manifest.engines?.node, pins.node!);\n for (const key of [\"packageManager\", \"runtime\"]) {\n const engine = manifest.devEngines?.[key];\n expect(\n \"package.json\",\n `devEngines.${key}`,\n `${engine?.name}@${engine?.version}`,\n `bun@${pins.bun}`,\n );\n expect(\"package.json\", `devEngines.${key}.onFail`, engine?.onFail, \"error\");\n }\n for (const file of [\n ...(bunOnly ? [\".nvmrc\"] : []),\n \".node-version\",\n \".bun-version\",\n \".tool-versions\",\n ])\n if (existsSync(join(root, file)))\n findings.push(\n bunOnly\n ? `${file}: package.json packageManager is the only runtime pin`\n : `${file}: use .nvmrc and package.json instead of a second version source`,\n );\n findings.push(...checkMise(root, bunOnly));\n\n // Runtime type packages follow the pins: @types/node by major, @types/bun exactly.\n const nodeMajor = pins.node?.split(\".\")[0];\n // Protocol ranges such as catalog: and workspace: are checked where they resolve.\n const version = (range: string | undefined) =>\n range && !/^[a-z]+:/.test(range) ? range.replace(/^[\\^~]/, \"\") : undefined;\n const checkTypes = (file: string, dependencies: Record<string, string> | undefined) => {\n const node = version(dependencies?.[\"@types/node\"]);\n if (nodeMajor && node && node.split(\".\")[0] !== nodeMajor)\n findings.push(`${file}: @types/node must match the Node ${nodeMajor} major`);\n const bun = version(dependencies?.[\"@types/bun\"]);\n if (bun) expect(file, \"@types/bun\", bun, pins.bun);\n };\n for (const catalog of catalogsOf(manifest)) checkTypes(\"package.json catalog\", catalog);\n for (const file of [\n \"package.json\",\n ...globSync(\n workspacePatterns(manifest).map((path) => `${path}/package.json`),\n { cwd: root },\n ),\n ])\n checkTypes(file, manifestAt(root, file).devDependencies);\n\n for (const file of globSync(\"**/Dockerfile\", { cwd: root, exclude: [\"**/node_modules/**\"] })) {\n const source = readFileSync(join(root, file), \"utf8\");\n if (bunOnly) {\n if (/^\\s*FROM\\s+(?:--platform=\\S+\\s+)?node:/im.test(source))\n findings.push(`${file}: build and run on oven/bun images`);\n } else {\n for (const [, version] of source.matchAll(/^FROM\\s+node:(\\d+\\.\\d+\\.\\d+)/gm))\n expect(file, \"node image\", version, pins.node!);\n }\n for (const [, version] of source.matchAll(/^FROM\\s+oven\\/bun:(\\d+\\.\\d+\\.\\d+)/gm))\n expect(file, \"bun image\", version, pins.bun);\n }\n findings.push(...checkWorkflows(root, bunOnly), ...checkCompositeActions(root, bunOnly));\n for (const file of documents) {\n if (!existsSync(join(root, file))) continue;\n const document = readFileSync(join(root, file), \"utf8\").replaceAll(\"**\", \"\");\n for (const [, tool, version] of document.matchAll(/\\b(Node|Bun) v?(\\d+\\.\\d+\\.\\d+)\\b/g)) {\n if (bunOnly && tool === \"Node\")\n findings.push(`${file}: documents a Node release; Bun is the only runtime`);\n else expect(file, `documented ${tool}`, version, tool === \"Node\" ? pins.node! : pins.bun);\n }\n }\n const turboFile = join(root, \"turbo.json\");\n if (existsSync(turboFile)) {\n const turbo = JSON.parse(readFileSync(turboFile, \"utf8\")) as { globalDependencies?: string[] };\n for (const file of turboGlobalDependencies)\n if (!turbo.globalDependencies?.includes(file))\n findings.push(`turbo.json: include ${file} in globalDependencies`);\n }\n return [...new Set(findings)];\n}\n"],"mappings":"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"}
@@ -1,4 +1,3 @@
1
- //#region src/workspaces.d.ts
2
1
  export declare const targets: readonly ["universal", "browser", "native", "node", "bun"];
3
2
  export type Target = (typeof targets)[number];
4
3
  export declare const kinds: readonly ["app", "library", "tooling"];
@@ -11,7 +10,7 @@ export interface Metadata {
11
10
  sourceTargets?: Record<string, Target>;
12
11
  /** Maintained source behind an export whose published target is generated. */
13
12
  sourceEntries?: Record<string, string>;
14
- /** Development dependencies explicitly bundled into the published runtime. */
13
+ /** Development dependencies explicitly bundled into runtime; internal workspaces require app/tooling. */
15
14
  bundledDependencies?: string[];
16
15
  }
17
16
  export interface Manifest {
@@ -67,5 +66,4 @@ export declare function resolveImport(index: WorkspaceIndex, from: Workspace, fi
67
66
  paths: string[];
68
67
  targetPlatform: "browser" | "bun" | "native" | "node" | "universal";
69
68
  };
70
- //#endregion
71
69
  //# sourceMappingURL=workspaces.d.mts.map
@@ -1,6 +1,6 @@
1
1
  import { existsSync, globSync, readFileSync, readdirSync } from "node:fs";
2
2
  import { join, posix } from "node:path";
3
- //#region src/workspaces.ts
3
+ // src/workspaces.ts
4
4
  const targets = [
5
5
  "universal",
6
6
  "browser",
@@ -133,7 +133,6 @@ function resolveImport(index, from, file, request) {
133
133
  targetPlatform: owner ? sourceTarget(owner, path) : "universal"
134
134
  };
135
135
  }
136
- //#endregion
137
136
  export { compatible, discover, exportPaths, kinds, matches, resolveImport, sourceTarget, targets, workspaceFor, workspacePatterns };
138
137
 
139
138
  //# sourceMappingURL=workspaces.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"workspaces.mjs","names":[],"sources":["../src/workspaces.ts"],"sourcesContent":["import { existsSync, globSync, readFileSync, readdirSync } from \"node:fs\";\nimport { join, posix } from \"node:path\";\n\nexport const targets = [\"universal\", \"browser\", \"native\", \"node\", \"bun\"] as const;\nexport type Target = (typeof targets)[number];\nexport const kinds = [\"app\", \"library\", \"tooling\"] as const;\nexport type Kind = (typeof kinds)[number];\n\n/** Per-workspace boundary metadata, supplied explicitly or read from package.json. */\nexport interface Metadata {\n kind: Kind;\n target: Target;\n entryTargets?: Record<string, Target>;\n sourceTargets?: Record<string, Target>;\n /** Maintained source behind an export whose published target is generated. */\n sourceEntries?: Record<string, string>;\n /** Development dependencies explicitly bundled into the published runtime. */\n bundledDependencies?: string[];\n}\nexport interface Manifest {\n name: string;\n private?: boolean;\n type?: string;\n exports?: Record<string, unknown>;\n imports?: Record<string, string>;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n [key: string]: unknown;\n}\nexport interface Workspace {\n path: string;\n manifest: Manifest;\n metadata: Metadata | undefined;\n aliases: Record<string, string[]>;\n}\nexport interface WorkspaceIndex {\n root: string;\n metadataKey: string;\n /** Origin of workspace metadata; omitted legacy indexes use the manifest. */\n metadataSource?: \"manifest\" | \"options\";\n workspaces: Workspace[];\n byName: Map<string, Workspace>;\n findings: string[];\n}\nexport interface DiscoverOptions {\n /** Complete metadata by exact workspace-relative path; never merged with manifests. */\n metadata?: Readonly<Record<string, Metadata>>;\n /** The package.json field holding {@link Metadata}, used only when metadata is omitted. */\n metadataKey?: string;\n /** Directories whose children must each be a workspace. */\n containers?: readonly string[];\n}\n\n/** Matches package export, import-map and tsconfig path patterns, including `**`. */\nexport function matches(pattern: string, value: string): boolean {\n let regex = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]!;\n if (char === \"*\" && pattern[i + 1] === \"*\") {\n if (pattern[i + 2] === \"/\") {\n regex += \"(?:.*/)?\";\n i += 2;\n } else {\n regex += \".*\";\n i++;\n }\n } else if (char === \"*\") regex += \"[^/]*\";\n else regex += char.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n return new RegExp(`${regex}$`).test(value);\n}\n\n/** Reads workspace patterns from the array or Bun/Yarn object form of `workspaces`. */\nexport function workspacePatterns(manifest: { workspaces?: unknown }): string[] {\n const value = manifest.workspaces;\n if (Array.isArray(value)) return value as string[];\n if (\n value &&\n typeof value === \"object\" &&\n Array.isArray((value as { packages?: unknown }).packages)\n )\n return (value as { packages: string[] }).packages;\n return [];\n}\n\nexport function discover(\n root: string,\n { metadata, metadataKey = \"devkit\", containers = [\"apps\", \"packages\"] }: DiscoverOptions = {},\n): WorkspaceIndex {\n const manifest = JSON.parse(readFileSync(join(root, \"package.json\"), \"utf8\"));\n const findings: string[] = [];\n const workspaces: Workspace[] = [];\n for (const file of globSync(\n workspacePatterns(manifest).map((p) => `${p}/package.json`),\n { cwd: root },\n ).sort()) {\n const path = file.replaceAll(\"\\\\\", \"/\").slice(0, -13);\n const entry = JSON.parse(readFileSync(join(root, file), \"utf8\")) as Manifest;\n const workspaceMetadata =\n metadata === undefined\n ? (entry[metadataKey] as Metadata | undefined)\n : Object.hasOwn(metadata, path)\n ? metadata[path]\n : undefined;\n if (metadata !== undefined && workspaceMetadata === undefined)\n findings.push(`${path}: missing supplied workspace metadata`);\n let aliases: Record<string, string[]> = {};\n const config = join(root, path, \"tsconfig.json\");\n if (existsSync(config)) {\n try {\n aliases = JSON.parse(readFileSync(config, \"utf8\")).compilerOptions?.paths ?? {};\n } catch {\n findings.push(`${path}/tsconfig.json: expected valid JSON`);\n }\n }\n workspaces.push({\n path,\n manifest: entry,\n metadata: workspaceMetadata,\n aliases,\n });\n }\n const byName = new Map<string, Workspace>();\n for (const workspace of workspaces) {\n if (byName.has(workspace.manifest.name))\n findings.push(`duplicate workspace: ${workspace.manifest.name}`);\n byName.set(workspace.manifest.name, workspace);\n }\n if (metadata !== undefined) {\n const paths = new Set(workspaces.map((workspace) => workspace.path));\n for (const path of Object.keys(metadata).sort()) {\n if (!paths.has(path)) findings.push(`${path}: metadata supplied for unknown workspace`);\n }\n }\n for (const parent of containers) {\n if (!existsSync(join(root, parent))) continue;\n for (const child of readdirSync(join(root, parent), { withFileTypes: true })) {\n if (!child.isDirectory() || child.name.startsWith(\".\")) continue;\n const path = `${parent}/${child.name}`;\n if (!workspaces.some((w) => w.path === path))\n findings.push(`${path}: missing workspace manifest`);\n }\n }\n return {\n root,\n metadataKey,\n metadataSource: metadata === undefined ? \"manifest\" : \"options\",\n workspaces,\n byName,\n findings,\n };\n}\n\nexport function workspaceFor(index: WorkspaceIndex, file: string) {\n return index.workspaces.find((w) => file.startsWith(`${w.path}/`));\n}\n\nexport function sourceTarget(workspace: Workspace, file: string): Target {\n const relative = file.slice(workspace.path.length + 1);\n const scopes = Object.entries(workspace.metadata?.sourceTargets ?? {})\n .filter(([pattern]) => matches(pattern, relative))\n .sort(([a], [b]) => b.replaceAll(\"*\", \"\").length - a.replaceAll(\"*\", \"\").length);\n return scopes[0]?.[1] ?? workspace.metadata?.target ?? \"universal\";\n}\n\nexport function compatible(from: Target, to: Target): boolean {\n return to === \"universal\" || from === to || (from === \"bun\" && to === \"node\");\n}\n\nexport function exportPaths(value: unknown): string[] {\n if (typeof value === \"string\") return [value];\n if (value && typeof value === \"object\") return Object.values(value).flatMap(exportPaths);\n return [];\n}\n\nexport function resolveImport(\n index: WorkspaceIndex,\n from: Workspace,\n file: string,\n request: string,\n) {\n const name = request.startsWith(\"@\")\n ? request.split(\"/\").slice(0, 2).join(\"/\")\n : request.split(\"/\")[0]!;\n const target = index.byName.get(name);\n if (target) {\n const subpath = request === name ? \".\" : `.${request.slice(name.length)}`;\n const entry = Object.entries(target.manifest.exports ?? {}).find(([pattern]) =>\n matches(pattern, subpath),\n );\n const suffix = entry?.[0].includes(\"*\") ? subpath.slice(entry[0].split(\"*\")[0]!.length) : \"\";\n const source = entry && target.metadata?.sourceEntries?.[entry[0]];\n return {\n target,\n subpath,\n internalPath: !entry,\n paths: entry\n ? (source ? [source] : exportPaths(entry[1])).map((p) =>\n posix.join(target.path, p.replace(\"*\", suffix)),\n )\n : [],\n targetPlatform:\n target.metadata?.entryTargets?.[entry?.[0] ?? subpath] ??\n target.metadata?.target ??\n \"universal\",\n };\n }\n const alias = Object.entries(from.aliases).find(([pattern]) => matches(pattern, request));\n const internal = Object.entries(from.manifest.imports ?? {}).find(([pattern]) =>\n matches(pattern, request),\n );\n const pattern = internal?.[0] ?? alias?.[0];\n const mapped = internal?.[1] ?? alias?.[1][0];\n const suffix = pattern?.includes(\"*\") ? request.slice(pattern.split(\"*\")[0]!.length) : \"\";\n const path = request.startsWith(\".\")\n ? posix.join(posix.dirname(file), request)\n : mapped\n ? posix.join(from.path, mapped.replace(\"*\", suffix))\n : undefined;\n const owner = path ? workspaceFor(index, path) : undefined;\n return {\n target: owner,\n subpath: \".\",\n internalPath: !!owner && owner !== from,\n paths: path ? [path] : [],\n targetPlatform: owner ? sourceTarget(owner, path!) : (\"universal\" as Target),\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"workspaces.mjs","names":[],"sources":["../src/workspaces.ts"],"sourcesContent":["import { existsSync, globSync, readFileSync, readdirSync } from \"node:fs\";\nimport { join, posix } from \"node:path\";\n\nexport const targets = [\"universal\", \"browser\", \"native\", \"node\", \"bun\"] as const;\nexport type Target = (typeof targets)[number];\nexport const kinds = [\"app\", \"library\", \"tooling\"] as const;\nexport type Kind = (typeof kinds)[number];\n\n/** Per-workspace boundary metadata, supplied explicitly or read from package.json. */\nexport interface Metadata {\n kind: Kind;\n target: Target;\n entryTargets?: Record<string, Target>;\n sourceTargets?: Record<string, Target>;\n /** Maintained source behind an export whose published target is generated. */\n sourceEntries?: Record<string, string>;\n /** Development dependencies explicitly bundled into runtime; internal workspaces require app/tooling. */\n bundledDependencies?: string[];\n}\nexport interface Manifest {\n name: string;\n private?: boolean;\n type?: string;\n exports?: Record<string, unknown>;\n imports?: Record<string, string>;\n scripts?: Record<string, string>;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n [key: string]: unknown;\n}\nexport interface Workspace {\n path: string;\n manifest: Manifest;\n metadata: Metadata | undefined;\n aliases: Record<string, string[]>;\n}\nexport interface WorkspaceIndex {\n root: string;\n metadataKey: string;\n /** Origin of workspace metadata; omitted legacy indexes use the manifest. */\n metadataSource?: \"manifest\" | \"options\";\n workspaces: Workspace[];\n byName: Map<string, Workspace>;\n findings: string[];\n}\nexport interface DiscoverOptions {\n /** Complete metadata by exact workspace-relative path; never merged with manifests. */\n metadata?: Readonly<Record<string, Metadata>>;\n /** The package.json field holding {@link Metadata}, used only when metadata is omitted. */\n metadataKey?: string;\n /** Directories whose children must each be a workspace. */\n containers?: readonly string[];\n}\n\n/** Matches package export, import-map and tsconfig path patterns, including `**`. */\nexport function matches(pattern: string, value: string): boolean {\n let regex = \"^\";\n for (let i = 0; i < pattern.length; i++) {\n const char = pattern[i]!;\n if (char === \"*\" && pattern[i + 1] === \"*\") {\n if (pattern[i + 2] === \"/\") {\n regex += \"(?:.*/)?\";\n i += 2;\n } else {\n regex += \".*\";\n i++;\n }\n } else if (char === \"*\") regex += \"[^/]*\";\n else regex += char.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n }\n return new RegExp(`${regex}$`).test(value);\n}\n\n/** Reads workspace patterns from the array or Bun/Yarn object form of `workspaces`. */\nexport function workspacePatterns(manifest: { workspaces?: unknown }): string[] {\n const value = manifest.workspaces;\n if (Array.isArray(value)) return value as string[];\n if (\n value &&\n typeof value === \"object\" &&\n Array.isArray((value as { packages?: unknown }).packages)\n )\n return (value as { packages: string[] }).packages;\n return [];\n}\n\nexport function discover(\n root: string,\n { metadata, metadataKey = \"devkit\", containers = [\"apps\", \"packages\"] }: DiscoverOptions = {},\n): WorkspaceIndex {\n const manifest = JSON.parse(readFileSync(join(root, \"package.json\"), \"utf8\"));\n const findings: string[] = [];\n const workspaces: Workspace[] = [];\n for (const file of globSync(\n workspacePatterns(manifest).map((p) => `${p}/package.json`),\n { cwd: root },\n ).sort()) {\n const path = file.replaceAll(\"\\\\\", \"/\").slice(0, -13);\n const entry = JSON.parse(readFileSync(join(root, file), \"utf8\")) as Manifest;\n const workspaceMetadata =\n metadata === undefined\n ? (entry[metadataKey] as Metadata | undefined)\n : Object.hasOwn(metadata, path)\n ? metadata[path]\n : undefined;\n if (metadata !== undefined && workspaceMetadata === undefined)\n findings.push(`${path}: missing supplied workspace metadata`);\n let aliases: Record<string, string[]> = {};\n const config = join(root, path, \"tsconfig.json\");\n if (existsSync(config)) {\n try {\n aliases = JSON.parse(readFileSync(config, \"utf8\")).compilerOptions?.paths ?? {};\n } catch {\n findings.push(`${path}/tsconfig.json: expected valid JSON`);\n }\n }\n workspaces.push({\n path,\n manifest: entry,\n metadata: workspaceMetadata,\n aliases,\n });\n }\n const byName = new Map<string, Workspace>();\n for (const workspace of workspaces) {\n if (byName.has(workspace.manifest.name))\n findings.push(`duplicate workspace: ${workspace.manifest.name}`);\n byName.set(workspace.manifest.name, workspace);\n }\n if (metadata !== undefined) {\n const paths = new Set(workspaces.map((workspace) => workspace.path));\n for (const path of Object.keys(metadata).sort()) {\n if (!paths.has(path)) findings.push(`${path}: metadata supplied for unknown workspace`);\n }\n }\n for (const parent of containers) {\n if (!existsSync(join(root, parent))) continue;\n for (const child of readdirSync(join(root, parent), { withFileTypes: true })) {\n if (!child.isDirectory() || child.name.startsWith(\".\")) continue;\n const path = `${parent}/${child.name}`;\n if (!workspaces.some((w) => w.path === path))\n findings.push(`${path}: missing workspace manifest`);\n }\n }\n return {\n root,\n metadataKey,\n metadataSource: metadata === undefined ? \"manifest\" : \"options\",\n workspaces,\n byName,\n findings,\n };\n}\n\nexport function workspaceFor(index: WorkspaceIndex, file: string) {\n return index.workspaces.find((w) => file.startsWith(`${w.path}/`));\n}\n\nexport function sourceTarget(workspace: Workspace, file: string): Target {\n const relative = file.slice(workspace.path.length + 1);\n const scopes = Object.entries(workspace.metadata?.sourceTargets ?? {})\n .filter(([pattern]) => matches(pattern, relative))\n .sort(([a], [b]) => b.replaceAll(\"*\", \"\").length - a.replaceAll(\"*\", \"\").length);\n return scopes[0]?.[1] ?? workspace.metadata?.target ?? \"universal\";\n}\n\nexport function compatible(from: Target, to: Target): boolean {\n return to === \"universal\" || from === to || (from === \"bun\" && to === \"node\");\n}\n\nexport function exportPaths(value: unknown): string[] {\n if (typeof value === \"string\") return [value];\n if (value && typeof value === \"object\") return Object.values(value).flatMap(exportPaths);\n return [];\n}\n\nexport function resolveImport(\n index: WorkspaceIndex,\n from: Workspace,\n file: string,\n request: string,\n) {\n const name = request.startsWith(\"@\")\n ? request.split(\"/\").slice(0, 2).join(\"/\")\n : request.split(\"/\")[0]!;\n const target = index.byName.get(name);\n if (target) {\n const subpath = request === name ? \".\" : `.${request.slice(name.length)}`;\n const entry = Object.entries(target.manifest.exports ?? {}).find(([pattern]) =>\n matches(pattern, subpath),\n );\n const suffix = entry?.[0].includes(\"*\") ? subpath.slice(entry[0].split(\"*\")[0]!.length) : \"\";\n const source = entry && target.metadata?.sourceEntries?.[entry[0]];\n return {\n target,\n subpath,\n internalPath: !entry,\n paths: entry\n ? (source ? [source] : exportPaths(entry[1])).map((p) =>\n posix.join(target.path, p.replace(\"*\", suffix)),\n )\n : [],\n targetPlatform:\n target.metadata?.entryTargets?.[entry?.[0] ?? subpath] ??\n target.metadata?.target ??\n \"universal\",\n };\n }\n const alias = Object.entries(from.aliases).find(([pattern]) => matches(pattern, request));\n const internal = Object.entries(from.manifest.imports ?? {}).find(([pattern]) =>\n matches(pattern, request),\n );\n const pattern = internal?.[0] ?? alias?.[0];\n const mapped = internal?.[1] ?? alias?.[1][0];\n const suffix = pattern?.includes(\"*\") ? request.slice(pattern.split(\"*\")[0]!.length) : \"\";\n const path = request.startsWith(\".\")\n ? posix.join(posix.dirname(file), request)\n : mapped\n ? posix.join(from.path, mapped.replace(\"*\", suffix))\n : undefined;\n const owner = path ? workspaceFor(index, path) : undefined;\n return {\n target: owner,\n subpath: \".\",\n internalPath: !!owner && owner !== from,\n paths: path ? [path] : [],\n targetPlatform: owner ? sourceTarget(owner, path!) : (\"universal\" as Target),\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raypx-kit/devkit",
3
- "version": "0.0.0-alpha.0",
3
+ "version": "0.0.0-alpha.2",
4
4
  "description": "Monorepo maintenance checks: workspace boundaries, toolchain pins, static catalogs, artifacts and task helpers.",
5
5
  "homepage": "https://github.com/raypx/raypx-kit/tree/canary/packages/devkit",
6
6
  "license": "Apache-2.0",
@@ -49,6 +49,14 @@
49
49
  "./docs": {
50
50
  "types": "./dist/docs.d.mts",
51
51
  "import": "./dist/docs.mjs"
52
+ },
53
+ "./lint": {
54
+ "types": "./dist/lint.d.mts",
55
+ "import": "./dist/lint.mjs"
56
+ },
57
+ "./testing": {
58
+ "types": "./dist/testing.d.mts",
59
+ "import": "./dist/testing.mjs"
52
60
  }
53
61
  },
54
62
  "publishConfig": {
@@ -63,17 +71,18 @@
63
71
  "test:coverage": "bun --bun vp test --run --passWithNoTests --coverage --coverage.reporter=text --coverage.reporter=json --coverage.reporter=lcov",
64
72
  "build": "tsdown",
65
73
  "dev": "tsdown --watch",
66
- "pack:check": "bun pm pack --dry-run --ignore-scripts"
74
+ "pack:check": "bun pm pack --dry-run"
67
75
  },
68
76
  "dependencies": {
69
- "@dotenvx/dotenvx": "^2.38.0",
77
+ "@dotenvx/dotenvx": "^2.38.2",
70
78
  "confbox": "^0.3.1",
71
79
  "oxc-parser": "^0.155.0"
72
80
  },
73
81
  "devDependencies": {
74
- "@vitest/coverage-v8": "^5.0.5"
82
+ "@vitest/coverage-v8": "^5.0.5",
83
+ "vite-plus": "^1.3.0"
75
84
  },
76
85
  "engines": {
77
- "node": ">=22.14"
86
+ "node": ">=22.21"
78
87
  }
79
88
  }