import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { dirname, join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { RepositoryCleaner } from './clean.ts' const roots: string[] = [] function fixture(): string { const root = mkdtempSync(join(tmpdir(), 'dsh-clean-')) roots.push(root) return root } function write(path: string, content = ''): void { mkdirSync(dirname(path), { recursive: true }) writeFileSync(path, content) } function addProject(root: string, path: string): void { write(join(root, 'tsconfig.json'), JSON.stringify({ files: [], references: [{ path }] })) write(join(root, path, 'tsconfig.json'), JSON.stringify({ compilerOptions: { composite: true, outDir: 'lib/types' }, include: ['src'], })) write(join(root, path, 'src/index.ts'), 'export {}\n') } afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }) }) describe('RepositoryCleaner', () => { it('derives live build outputs from project references and removes safe stale package residue', async () => { const root = fixture() addProject(root, 'products/shell') write(join(root, 'products/shell/lib/types/index.js')) write(join(root, 'products/shell/lib/index.js')) write(join(root, '.typecheck/legacy.tsbuildinfo')) write(join(root, 'root.tsbuildinfo')) write(join(root, 'packages/removed/ghost/node_modules/.bin/tool')) await new RepositoryCleaner(root).clean() expect(existsSync(join(root, 'products/shell/lib'))).toBe(false) expect(existsSync(join(root, 'products/shell/src/index.ts'))).toBe(true) expect(existsSync(join(root, '.typecheck'))).toBe(false) expect(existsSync(join(root, 'root.tsbuildinfo'))).toBe(false) expect(existsSync(join(root, 'packages/removed/ghost'))).toBe(false) }) it('does not delete any target when a manifest-less package contains an unknown file', async () => { const root = fixture() addProject(root, 'products/shell') write(join(root, 'products/shell/lib/types/index.js')) write(join(root, 'packages/removed/ghost/notes.txt')) await expect(new RepositoryCleaner(root).clean()).rejects.toThrow('packages/removed/ghost/notes.txt') expect(existsSync(join(root, 'products/shell/lib'))).toBe(true) }) })