Merge remote-tracking branch 'origin/master' into feat/adr0016-type-build-check

This commit is contained in:
imccyu
2026-06-22 00:35:51 +08:00
365 changed files with 13601 additions and 7241 deletions
+52 -7
View File
@@ -5,11 +5,16 @@
* Run: `tsx scripts/check-workspace-constraints.ts`.
*/
import { readdirSync, readFileSync } from 'node:fs'
import { existsSync, readdirSync, readFileSync } from 'node:fs'
import { join, relative, resolve } from 'node:path'
const root = resolve(import.meta.dirname, '..')
const workspaceGlobs = ['vendor', 'packages'] as const
// vendor/* is single-level; packages/<group>/<pkg> nests one level deeper
// (the group dirs — core/llm/bash/… — are pure containers with no manifest).
const workspaceGlobs = [
{ dir: 'vendor', depth: 1 },
{ dir: 'packages', depth: 2 },
] as const
const vendoredPackages = new Set([
'cordis',
'cosmokit',
@@ -51,15 +56,25 @@ function readJson(path: string): PackageManifest {
return JSON.parse(readFileSync(path, 'utf8')) as PackageManifest
}
/** Repo-relative dirs holding a package.json, walked to the configured depth. */
function packageDirs(base: string, depth: number): string[] {
if (depth === 1) {
return readdirSync(join(root, base), { withFileTypes: true })
.filter(entry => entry.isDirectory())
.map(entry => join(base, entry.name))
}
return readdirSync(join(root, base), { withFileTypes: true })
.filter(entry => entry.isDirectory())
.flatMap(group => packageDirs(join(base, group.name), depth - 1))
}
function workspaceManifests(): WorkspaceManifest[] {
const manifests: WorkspaceManifest[] = [
{ dir: '.', manifest: readJson(join(root, 'package.json')) },
]
for (const workspaceDir of workspaceGlobs) {
for (const entry of readdirSync(join(root, workspaceDir), { withFileTypes: true })) {
if (!entry.isDirectory()) continue
const dir = join(workspaceDir, entry.name)
for (const { dir: base, depth } of workspaceGlobs) {
for (const dir of packageDirs(base, depth)) {
manifests.push({ dir, manifest: readJson(join(root, dir, 'package.json')) })
}
}
@@ -125,7 +140,37 @@ function checkWorkspace({ dir, manifest }: WorkspaceManifest): string[] {
return errors.map(error => `${relative(root, join(root, dir, 'package.json'))}: ${error}`)
}
const errors = workspaceManifests().flatMap(checkWorkspace)
/**
* Enforce the packages/ hierarchy SHAPE: every package lives at exactly
* `packages/<group>/<pkg>`. A group dir is a pure container — it holds packages,
* never sources of its own — so it must NOT carry a package.json, and a package
* must NOT sit directly at the `packages/` root (the old flat layout) nor nest a
* level deeper. The group NAMES are open on purpose: a new group may be added
* without touching this gate, but the depth-2 shape is fixed. This is what keeps
* a stray flat package or an over-nested one from regressing the hierarchy.
*/
function checkHierarchyShape(): string[] {
const errors: string[] = []
const packagesRoot = join(root, 'packages')
for (const group of readdirSync(packagesRoot, { withFileTypes: true })) {
if (!group.isDirectory()) continue
const groupRel = join('packages', group.name)
if (existsSync(join(packagesRoot, group.name, 'package.json'))) {
errors.push(`${groupRel}: a group dir must not contain a package.json — packages live at packages/<group>/<pkg>, not directly under packages/`)
continue
}
for (const pkg of readdirSync(join(packagesRoot, group.name), { withFileTypes: true })) {
if (!pkg.isDirectory()) continue
const pkgRel = join(groupRel, pkg.name)
if (!existsSync(join(packagesRoot, group.name, pkg.name, 'package.json'))) {
errors.push(`${pkgRel}: expected a package here (no package.json found) — the hierarchy is exactly packages/<group>/<pkg>, no deeper nesting`)
}
}
}
return errors
}
const errors = [...workspaceManifests().flatMap(checkWorkspace), ...checkHierarchyShape()]
if (errors.length > 0) {
console.error(errors.join('\n'))
process.exitCode = 1