/** * Workspace layout test — locks in the [E00-S01-T11] apps/packages/extensions * separation for the workspace. * * Acceptance criteria covered (each test fails without the committed layout): * - "apps, packages and extensions are separated in the workspace layout" → * the repo root contains exactly the three top-level package group * directories apps/, packages/, extensions/; every workspace package.json * lives exactly one level below one of them (never at the root, never in * another directory, never nested deeper); `pnpm-workspace.yaml` declares * one glob per group and `dependency-boundaries.json` maps the same three * groups to the same three directories. * - "each package sits in the correct top-level directory" → the bootstrap * packages are found where their group says: `@personal-blog/server` in * apps/, `@personal-blog/core` in packages/, `@personal-blog/example- * extension` in extensions/; the discovered workspace package set is * exactly the expected one, and every discovered package has a lockfile * importer (layout ↔ lockfile parity). * * Run: `node --test tests/workspace-layout.test.mjs` * (node:test — built into Node >= 18; no dependencies, lockfile untouched.) */ import test from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync, readdirSync, statSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const read = (relPath) => readFileSync(path.join(REPO_ROOT, relPath), 'utf8'); /** The three top-level package group directories of the separated layout. */ const TOP_LEVEL_GROUPS = ['apps', 'packages', 'extensions']; /** Expected bootstrap packages: package name -> top-level directory it must sit in. */ const EXPECTED_PACKAGES = { '@personal-blog/server': 'apps/server', '@personal-blog/core': 'packages/core', '@personal-blog/database-postgres': 'packages/database-postgres', '@personal-blog/example-extension': 'extensions/example', }; /** Directories never scanned as package sources. */ const IGNORED_DIRS = new Set(['node_modules', 'dist', 'coverage', '.git', '.pnpm-store']); // --------------------------------------------------------------------------- // Layout helpers // --------------------------------------------------------------------------- function isDirectory(p) { try { return statSync(p).isDirectory(); } catch { return false; } } /** * Recursively collects every package.json path under a directory, as a * repo-root-relative posix path (the root workspace manifest itself is * included as "package.json"). */ function collectPackageJsonPaths(dir) { const results = []; for (const entry of readdirSync(dir, { withFileTypes: true })) { if (entry.isDirectory()) { if (!IGNORED_DIRS.has(entry.name)) { results.push(...collectPackageJsonPaths(path.join(dir, entry.name))); } } else if (entry.name === 'package.json') { results.push(path.relative(REPO_ROOT, path.join(dir, entry.name)).split(path.sep).join('/')); } } return results; } /** * Validates the placement of a repo-root-relative package.json path. A * workspace package must be exactly `//package.json` under one of * the three top-level groups; the root workspace manifest ("package.json") is * the one allowed exception. Returns an error message, or null when the * placement is correct. */ function placementError(relPath) { if (relPath === 'package.json') return null; // workspace root manifest, not a package const parts = relPath.split('/'); if (parts.length !== 3 || parts[2] !== 'package.json') { return `"${relPath}" must be //package.json (exactly one level below a top-level group directory)`; } if (!TOP_LEVEL_GROUPS.includes(parts[0])) { return `"${relPath}" must sit under apps/, packages/ or extensions/ (got "${parts[0]}")`; } return null; } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- test('the workspace root contains exactly the three separated top-level group directories', () => { for (const group of TOP_LEVEL_GROUPS) { assert.ok( isDirectory(path.join(REPO_ROOT, group)), `top-level directory "${group}/" must exist (apps/packages/extensions separated)`, ); } }); test('every workspace package.json sits exactly one level below its top-level group directory', () => { const manifests = collectPackageJsonPaths(REPO_ROOT); const packageManifests = manifests.filter((rel) => rel !== 'package.json'); assert.ok(packageManifests.length > 0, 'the workspace must contain at least one package manifest'); for (const rel of packageManifests) { const error = placementError(rel); assert.equal(error, null, error); } }); test('each expected package sits in the correct top-level directory', () => { for (const [name, dir] of Object.entries(EXPECTED_PACKAGES)) { const manifest = JSON.parse(read(path.join(dir, 'package.json'))); assert.equal( manifest.name, name, `"${dir}/package.json" must declare name "${name}" (each package sits in its correct top-level directory)`, ); } }); test('the discovered workspace package set is exactly the expected bootstrap set', () => { const manifests = collectPackageJsonPaths(REPO_ROOT).filter((rel) => rel !== 'package.json'); const discoveredDirs = new Set(manifests.map((rel) => rel.replace(/\/package\.json$/, ''))); assert.deepEqual(discoveredDirs, new Set(Object.values(EXPECTED_PACKAGES))); }); test('pnpm-workspace.yaml declares one group glob per top-level group directory', () => { const workspace = read('pnpm-workspace.yaml'); const packagesSection = workspace.slice(workspace.indexOf('packages:')); const globs = [...packagesSection.matchAll(/^\s*-\s*'([^']+)'\s*$/gm)].map((m) => m[1]); assert.deepEqual( globs, TOP_LEVEL_GROUPS.map((g) => `${g}/*`), 'pnpm-workspace.yaml must declare exactly apps/*, packages/* and extensions/*', ); }); test('dependency-boundaries.json maps the three groups to the same top-level directories', () => { const rule = JSON.parse(read('dependency-boundaries.json')); for (const group of TOP_LEVEL_GROUPS) { assert.equal( rule.groups?.[group]?.path, `${group}/`, `dependency-boundaries.json must map group "${group}" to path "${group}/"`, ); } }); test('every discovered workspace package has a lockfile importer (layout ↔ lockfile parity)', () => { const lockfile = read('pnpm-lock.yaml'); const manifests = collectPackageJsonPaths(REPO_ROOT).filter((rel) => rel !== 'package.json'); for (const rel of manifests) { const importer = rel.replace(/\/package\.json$/, ''); assert.ok( lockfile.includes(` ${importer}:`), `pnpm-lock.yaml must contain an importer for "${importer}" (every package in the layout must be a workspace member)`, ); } }); test('the placement validation flags mis-placed packages (non-vacuous probe)', () => { const valid = [ 'apps/server/package.json', 'packages/core/package.json', 'extensions/example/package.json', 'package.json', // workspace root manifest ]; for (const rel of valid) { assert.equal(placementError(rel), null, `"${rel}" must be a valid placement`); } const invalid = [ 'lib/shared/package.json', // no top-level group at all 'apps/package.json', // manifest directly in a group dir (no package name level) 'packages/a/b/package.json', // nested deeper than one level 'docs/package.json', // outside the three groups ]; for (const rel of invalid) { assert.notEqual(placementError(rel), null, `"${rel}" must be flagged as mis-placed`); } });