@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 +23 -0
- package/README.md +74 -5
- package/dist/architecture.d.mts +0 -2
- package/dist/architecture.mjs +6 -4
- package/dist/architecture.mjs.map +1 -1
- package/dist/artifact.d.mts +0 -2
- package/dist/artifact.mjs +1 -2
- package/dist/catalogs.d.mts +0 -2
- package/dist/catalogs.mjs +1 -2
- package/dist/docs.d.mts +0 -2
- package/dist/docs.mjs +1 -2
- package/dist/lint.d.mts +68 -0
- package/dist/lint.mjs +71 -0
- package/dist/lint.mjs.map +1 -0
- package/dist/process.d.mts +0 -2
- package/dist/process.mjs +1 -2
- package/dist/testing.d.mts +13 -0
- package/dist/testing.mjs +18 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/toolchain.d.mts +9 -3
- package/dist/toolchain.mjs +48 -24
- package/dist/toolchain.mjs.map +1 -1
- package/dist/workspaces.d.mts +1 -3
- package/dist/workspaces.mjs +1 -2
- package/dist/workspaces.mjs.map +1 -1
- package/package.json +14 -5
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.
|
|
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
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
package/dist/architecture.d.mts
CHANGED
|
@@ -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
|
package/dist/architecture.mjs
CHANGED
|
@@ -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
|
-
|
|
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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"}
|
package/dist/artifact.d.mts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/catalogs.d.mts
CHANGED
|
@@ -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
|
-
|
|
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
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
|
-
|
|
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
|
package/dist/lint.d.mts
ADDED
|
@@ -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"}
|
package/dist/process.d.mts
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/testing.mjs
ADDED
|
@@ -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"}
|
package/dist/toolchain.d.mts
CHANGED
|
@@ -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
|
package/dist/toolchain.mjs
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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))
|
|
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
|
-
|
|
211
|
-
export { assertToolchainRuntime, checkToolchain, pinnedNode, readToolchain };
|
|
235
|
+
export { assertBunRuntime, assertToolchainRuntime, checkToolchain, pinnedNode, readBunPin, readToolchain };
|
|
212
236
|
|
|
213
237
|
//# sourceMappingURL=toolchain.mjs.map
|
package/dist/toolchain.mjs.map
CHANGED
|
@@ -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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"}
|
package/dist/workspaces.d.mts
CHANGED
|
@@ -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
|
|
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
|
package/dist/workspaces.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, globSync, readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join, posix } from "node:path";
|
|
3
|
-
|
|
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
|
package/dist/workspaces.mjs.map
CHANGED
|
@@ -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":";;;AAGA,MAAa,UAAU;CAAC;CAAa;CAAW;CAAU;CAAQ;AAAK;AAEvE,MAAa,QAAQ;CAAC;CAAO;CAAW;AAAS;;AAmDjD,SAAgB,QAAQ,SAAiB,OAAwB;CAC/D,IAAI,QAAQ;CACZ,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;EACvC,MAAM,OAAO,QAAQ;EACrB,IAAI,SAAS,OAAO,QAAQ,IAAI,OAAO,KAAK;GAC1C,IAAI,QAAQ,IAAI,OAAO,KAAK;IAC1B,SAAS;IACT,KAAK;GACP,OAAO;IACL,SAAS;IACT;GACF;EACF,OAAO,IAAI,SAAS,KAAK,SAAS;OAC7B,SAAS,KAAK,QAAQ,uBAAuB,MAAM;CAC1D;CACA,OAAO,IAAI,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC,KAAK,KAAK;AAC3C;;AAGA,SAAgB,kBAAkB,UAA8C;CAC9E,MAAM,QAAQ,SAAS;CACvB,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO;CACjC,IACE,SACA,OAAO,UAAU,YACjB,MAAM,QAAS,MAAiC,QAAQ,GAExD,OAAQ,MAAiC;CAC3C,OAAO,CAAC;AACV;AAEA,SAAgB,SACd,MACA,EAAE,UAAU,cAAc,UAAU,aAAa,CAAC,QAAQ,UAAU,MAAuB,CAAC,GAC5E;CAChB,MAAM,WAAW,KAAK,MAAM,aAAa,KAAK,MAAM,cAAc,GAAG,MAAM,CAAC;CAC5E,MAAM,WAAqB,CAAC;CAC5B,MAAM,aAA0B,CAAC;CACjC,KAAK,MAAM,QAAQ,SACjB,kBAAkB,QAAQ,CAAC,CAAC,KAAK,MAAM,GAAG,EAAE,cAAc,GAC1D,EAAE,KAAK,KAAK,CACd,CAAC,CAAC,KAAK,GAAG;EACR,MAAM,OAAO,KAAK,WAAW,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,GAAG;EACpD,MAAM,QAAQ,KAAK,MAAM,aAAa,KAAK,MAAM,IAAI,GAAG,MAAM,CAAC;EAC/D,MAAM,oBACJ,aAAa,KAAA,IACR,MAAM,eACP,OAAO,OAAO,UAAU,IAAI,IAC1B,SAAS,QACT,KAAA;EACR,IAAI,aAAa,KAAA,KAAa,sBAAsB,KAAA,GAClD,SAAS,KAAK,GAAG,KAAK,sCAAsC;EAC9D,IAAI,UAAoC,CAAC;EACzC,MAAM,SAAS,KAAK,MAAM,MAAM,eAAe;EAC/C,IAAI,WAAW,MAAM,GACnB,IAAI;GACF,UAAU,KAAK,MAAM,aAAa,QAAQ,MAAM,CAAC,CAAC,CAAC,iBAAiB,SAAS,CAAC;EAChF,QAAQ;GACN,SAAS,KAAK,GAAG,KAAK,oCAAoC;EAC5D;EAEF,WAAW,KAAK;GACd;GACA,UAAU;GACV,UAAU;GACV;EACF,CAAC;CACH;CACA,MAAM,yBAAS,IAAI,IAAuB;CAC1C,KAAK,MAAM,aAAa,YAAY;EAClC,IAAI,OAAO,IAAI,UAAU,SAAS,IAAI,GACpC,SAAS,KAAK,wBAAwB,UAAU,SAAS,MAAM;EACjE,OAAO,IAAI,UAAU,SAAS,MAAM,SAAS;CAC/C;CACA,IAAI,aAAa,KAAA,GAAW;EAC1B,MAAM,QAAQ,IAAI,IAAI,WAAW,KAAK,cAAc,UAAU,IAAI,CAAC;EACnE,KAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,CAAC,CAAC,KAAK,GAC5C,IAAI,CAAC,MAAM,IAAI,IAAI,GAAG,SAAS,KAAK,GAAG,KAAK,0CAA0C;CAE1F;CACA,KAAK,MAAM,UAAU,YAAY;EAC/B,IAAI,CAAC,WAAW,KAAK,MAAM,MAAM,CAAC,GAAG;EACrC,KAAK,MAAM,SAAS,YAAY,KAAK,MAAM,MAAM,GAAG,EAAE,eAAe,KAAK,CAAC,GAAG;GAC5E,IAAI,CAAC,MAAM,YAAY,KAAK,MAAM,KAAK,WAAW,GAAG,GAAG;GACxD,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM;GAChC,IAAI,CAAC,WAAW,MAAM,MAAM,EAAE,SAAS,IAAI,GACzC,SAAS,KAAK,GAAG,KAAK,6BAA6B;EACvD;CACF;CACA,OAAO;EACL;EACA;EACA,gBAAgB,aAAa,KAAA,IAAY,aAAa;EACtD;EACA;EACA;CACF;AACF;AAEA,SAAgB,aAAa,OAAuB,MAAc;CAChE,OAAO,MAAM,WAAW,MAAM,MAAM,KAAK,WAAW,GAAG,EAAE,KAAK,EAAE,CAAC;AACnE;AAEA,SAAgB,aAAa,WAAsB,MAAsB;CACvE,MAAM,WAAW,KAAK,MAAM,UAAU,KAAK,SAAS,CAAC;CAIrD,OAHe,OAAO,QAAQ,UAAU,UAAU,iBAAiB,CAAC,CAAC,CAAC,CACnE,QAAQ,CAAC,aAAa,QAAQ,SAAS,QAAQ,CAAC,CAAC,CACjD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,KAAK,EAAE,CAAC,CAAC,SAAS,EAAE,WAAW,KAAK,EAAE,CAAC,CAAC,MAC/D,CAAC,CAAC,EAAE,GAAG,MAAM,UAAU,UAAU,UAAU;AACzD;AAEA,SAAgB,WAAW,MAAc,IAAqB;CAC5D,OAAO,OAAO,eAAe,SAAS,MAAO,SAAS,SAAS,OAAO;AACxE;AAEA,SAAgB,YAAY,OAA0B;CACpD,IAAI,OAAO,UAAU,UAAU,OAAO,CAAC,KAAK;CAC5C,IAAI,SAAS,OAAO,UAAU,UAAU,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,QAAQ,WAAW;CACvF,OAAO,CAAC;AACV;AAEA,SAAgB,cACd,OACA,MACA,MACA,SACA;CACA,MAAM,OAAO,QAAQ,WAAW,GAAG,IAC/B,QAAQ,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,IACvC,QAAQ,MAAM,GAAG,CAAC,CAAC;CACvB,MAAM,SAAS,MAAM,OAAO,IAAI,IAAI;CACpC,IAAI,QAAQ;EACV,MAAM,UAAU,YAAY,OAAO,MAAM,IAAI,QAAQ,MAAM,KAAK,MAAM;EACtE,MAAM,QAAQ,OAAO,QAAQ,OAAO,SAAS,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aACjE,QAAQ,SAAS,OAAO,CAC1B;EACA,MAAM,SAAS,QAAQ,EAAE,CAAC,SAAS,GAAG,IAAI,QAAQ,MAAM,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAE,MAAM,IAAI;EAC1F,MAAM,SAAS,SAAS,OAAO,UAAU,gBAAgB,MAAM;EAC/D,OAAO;GACL;GACA;GACA,cAAc,CAAC;GACf,OAAO,SACF,SAAS,CAAC,MAAM,IAAI,YAAY,MAAM,EAAE,EAAA,CAAG,KAAK,MAC/C,MAAM,KAAK,OAAO,MAAM,EAAE,QAAQ,KAAK,MAAM,CAAC,CAChD,IACA,CAAC;GACL,gBACE,OAAO,UAAU,eAAe,QAAQ,MAAM,YAC9C,OAAO,UAAU,UACjB;EACJ;CACF;CACA,MAAM,QAAQ,OAAO,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC,aAAa,QAAQ,SAAS,OAAO,CAAC;CACxF,MAAM,WAAW,OAAO,QAAQ,KAAK,SAAS,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,aAClE,QAAQ,SAAS,OAAO,CAC1B;CACA,MAAM,UAAU,WAAW,MAAM,QAAQ;CACzC,MAAM,SAAS,WAAW,MAAM,QAAQ,EAAE,CAAC;CAC3C,MAAM,SAAS,SAAS,SAAS,GAAG,IAAI,QAAQ,MAAM,QAAQ,MAAM,GAAG,CAAC,CAAC,EAAE,CAAE,MAAM,IAAI;CACvF,MAAM,OAAO,QAAQ,WAAW,GAAG,IAC/B,MAAM,KAAK,MAAM,QAAQ,IAAI,GAAG,OAAO,IACvC,SACE,MAAM,KAAK,KAAK,MAAM,OAAO,QAAQ,KAAK,MAAM,CAAC,IACjD,KAAA;CACN,MAAM,QAAQ,OAAO,aAAa,OAAO,IAAI,IAAI,KAAA;CACjD,OAAO;EACL,QAAQ;EACR,SAAS;EACT,cAAc,CAAC,CAAC,SAAS,UAAU;EACnC,OAAO,OAAO,CAAC,IAAI,IAAI,CAAC;EACxB,gBAAgB,QAAQ,aAAa,OAAO,IAAK,IAAK;CACxD;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@raypx-kit/devkit",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
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
|
|
74
|
+
"pack:check": "bun pm pack --dry-run"
|
|
67
75
|
},
|
|
68
76
|
"dependencies": {
|
|
69
|
-
"@dotenvx/dotenvx": "^2.38.
|
|
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.
|
|
86
|
+
"node": ">=22.21"
|
|
78
87
|
}
|
|
79
88
|
}
|