2026-05-21 13:53:38 +00:00
|
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
|
import { join } from 'node:path'
|
|
|
|
|
|
|
|
|
|
const publicPackages = ['packages/core', 'packages/cli', 'packages/mcp', 'packages/vue']
|
|
|
|
|
|
|
|
|
|
interface PackageJson {
|
|
|
|
|
name: string
|
|
|
|
|
main?: string
|
|
|
|
|
types?: string
|
|
|
|
|
files?: string[]
|
|
|
|
|
bin?: Record<string, string> | string
|
|
|
|
|
exports?: unknown
|
|
|
|
|
publishConfig?: Record<string, unknown>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const errors: string[] = []
|
|
|
|
|
|
|
|
|
|
function readPackageJson(packageDir: string): PackageJson {
|
|
|
|
|
return JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8'))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function checkRuntimePath(packageName: string, field: string, value: string): void {
|
|
|
|
|
if (value.endsWith('.ts') && !value.endsWith('.d.ts')) {
|
|
|
|
|
errors.push(`${packageName}: ${field} must not point to runtime TypeScript (${value})`)
|
|
|
|
|
}
|
|
|
|
|
if (value.startsWith('./src/')) {
|
|
|
|
|
errors.push(`${packageName}: ${field} must not point to source files (${value})`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function walkExports(packageName: string, value: unknown, path: string[] = []): void {
|
|
|
|
|
if (typeof value === 'string') {
|
|
|
|
|
const key = path.at(-1)
|
2026-05-24 09:15:29 +00:00
|
|
|
if (key !== 'types' && key !== 'bun')
|
|
|
|
|
checkRuntimePath(packageName, `exports.${path.join('.')}`, value)
|
2026-05-21 13:53:38 +00:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (!value || typeof value !== 'object') return
|
|
|
|
|
for (const [key, child] of Object.entries(value)) {
|
|
|
|
|
walkExports(packageName, child, [...path, key])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const packageDir of publicPackages) {
|
|
|
|
|
const pkg = readPackageJson(packageDir)
|
|
|
|
|
|
|
|
|
|
if (!pkg.files?.includes('dist')) {
|
|
|
|
|
errors.push(`${pkg.name}: files must include dist`)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pkg.main) checkRuntimePath(pkg.name, 'main', pkg.main)
|
|
|
|
|
|
|
|
|
|
if (typeof pkg.bin === 'string') {
|
|
|
|
|
checkRuntimePath(pkg.name, 'bin', pkg.bin)
|
|
|
|
|
} else if (pkg.bin) {
|
|
|
|
|
for (const [name, target] of Object.entries(pkg.bin)) {
|
|
|
|
|
checkRuntimePath(pkg.name, `bin.${name}`, target)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
walkExports(pkg.name, pkg.exports)
|
|
|
|
|
|
2026-05-24 09:15:29 +00:00
|
|
|
if (
|
|
|
|
|
pkg.publishConfig &&
|
|
|
|
|
('exports' in pkg.publishConfig || 'main' in pkg.publishConfig || 'types' in pkg.publishConfig)
|
|
|
|
|
) {
|
2026-05-21 13:53:38 +00:00
|
|
|
errors.push(`${pkg.name}: publishConfig must not rewrite runtime entrypoints`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (errors.length > 0) {
|
|
|
|
|
console.error(errors.join('\n'))
|
|
|
|
|
process.exit(1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('Package metadata is publish-safe.')
|