(pkgDir: string)
| 20 | } |
| 21 | |
| 22 | export function validatePackageExports(pkgDir: string): string[] { |
| 23 | const errors: string[] = [] |
| 24 | const pkgJsonPath = join(pkgDir, 'package.json') |
| 25 | |
| 26 | if (!existsSync(pkgJsonPath)) { |
| 27 | return [`package.json not found in ${pkgDir}`] |
| 28 | } |
| 29 | |
| 30 | const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf8')) |
| 31 | const exports: Record<string, unknown> = pkg.exports ?? {} |
| 32 | |
| 33 | // Check static (non-wildcard) export paths |
| 34 | collectPaths(exports, value => { |
| 35 | if (value.includes('*')) return |
| 36 | const resolved = join(pkgDir, value) |
| 37 | if (!existsSync(resolved)) { |
| 38 | errors.push(`exported path '${value}' not found at '${resolved}'`) |
| 39 | } |
| 40 | // Check that .js exports have a corresponding .d.ts file |
| 41 | if (value.endsWith('.js')) { |
| 42 | const dtsPath = join(pkgDir, value.replace(/\.js$/, '.d.ts')) |
| 43 | if (!existsSync(dtsPath)) { |
| 44 | errors.push( |
| 45 | `missing declaration file '${value.replace(/\.js$/, '.d.ts')}' for export '${value}'` |
| 46 | ) |
| 47 | } |
| 48 | } |
| 49 | }) |
| 50 | |
| 51 | // Check wildcard export directories |
| 52 | collectPaths(exports, value => { |
| 53 | if (!value.includes('*')) return |
| 54 | const dir = join(pkgDir, value.replace('/*', '')) |
| 55 | if (!existsSync(dir) || !statSync(dir).isDirectory()) { |
| 56 | errors.push(`exported directory '${value}' not found at '${dir}'`) |
| 57 | } else if (readdirSync(dir).length === 0) { |
| 58 | errors.push(`exported directory '${value}' is empty at '${dir}'`) |
| 59 | } else { |
| 60 | // Check that .js files in wildcard dirs have corresponding .d.ts |
| 61 | const jsFiles = readdirSync(dir).filter(f => f.endsWith('.js')) |
| 62 | for (const jsFile of jsFiles) { |
| 63 | const dtsFile = jsFile.replace(/\.js$/, '.d.ts') |
| 64 | if (!existsSync(join(dir, dtsFile))) { |
| 65 | errors.push( |
| 66 | `missing declaration file '${dtsFile}' in wildcard directory '${value}'` |
| 67 | ) |
| 68 | } |
| 69 | } |
| 70 | } |
| 71 | }) |
| 72 | |
| 73 | // Check "types" field |
| 74 | if (pkg.types) { |
| 75 | const resolved = join(pkgDir, pkg.types) |
| 76 | if (!existsSync(resolved)) { |
| 77 | errors.push(`types file '${pkg.types}' not found at '${resolved}'`) |
| 78 | } |
| 79 | } |
no test coverage detected