| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- import { dirname, resolve } from 'node:path'
- import { fileURLToPath } from 'node:url'
- import { loadEcosystemReleaseContext } from './lib/ecosystem-packages.mjs'
- const scriptDir = dirname(fileURLToPath(import.meta.url))
- const sourceRoot = resolve(scriptDir, '..')
- const { rootPackage, wrapperManifest, entries } = await loadEcosystemReleaseContext(sourceRoot)
- const rootVersion = rootPackage.version
- const expectedWorkspaceRange = `workspace:^${rootVersion}`
- const corePackageName = wrapperManifest.corePackage.packageName
- const entryByName = new Map()
- const entryById = new Map()
- const entryByDir = new Map()
- const historicalPackageNames = new Set()
- const standardPackageByHistoricalName = new Map()
- const compatibilityPackageByName = new Map()
- const standardWrapperPackageNames = new Set(wrapperManifest.wrappers.map(wrapper => wrapper.packageName))
- const wrapperByPackageName = new Map(wrapperManifest.wrappers.map(wrapper => [wrapper.packageName, wrapper]))
- function assert(condition, message) {
- if (!condition) {
- throw new Error(message)
- }
- }
- function installDependencies(packageJson) {
- return {
- ...packageJson.dependencies,
- ...packageJson.optionalDependencies
- }
- }
- function allDependencyFields(packageJson) {
- return {
- dependencies: packageJson.dependencies || {},
- optionalDependencies: packageJson.optionalDependencies || {},
- peerDependencies: packageJson.peerDependencies || {},
- devDependencies: packageJson.devDependencies || {}
- }
- }
- function assertUnique(map, key, label, packageName) {
- assert(!map.has(key), `Duplicate ${label} ${key} in ecosystem release packages`)
- map.set(key, packageName)
- }
- function normalizeRepositoryUrl(value) {
- return String(value || '')
- .replace(/^git\+/, '')
- .replace(/\.git$/, '')
- }
- function assertPackageFiles(entry, requiredFiles) {
- const files = new Set(entry.packageJson.files || [])
- for (const requiredFile of requiredFiles) {
- assert(
- files.has(requiredFile),
- `${entry.packageName} package.json files must include ${requiredFile}`
- )
- }
- }
- function assertRepositoryMetadata(entry, expectedUrl, expectedDirectory) {
- assert(entry.packageJson.repository?.type === 'git', `${entry.packageName} repository.type must be git`)
- assert(
- normalizeRepositoryUrl(entry.packageJson.repository?.url) === normalizeRepositoryUrl(expectedUrl),
- `${entry.packageName} repository.url must point to ${expectedUrl}`
- )
- assert(
- entry.packageJson.repository?.directory === expectedDirectory,
- `${entry.packageName} repository.directory must be ${expectedDirectory}`
- )
- }
- for (const wrapper of wrapperManifest.wrappers) {
- for (const packageName of wrapper.historicalPackages) {
- assert(!historicalPackageNames.has(packageName), `Duplicate historical package ${packageName}`)
- historicalPackageNames.add(packageName)
- standardPackageByHistoricalName.set(packageName, wrapper.packageName)
- }
- }
- for (const renderer of wrapperManifest.renderers || []) {
- const entry = entries.find(candidate => candidate.id === `renderer-${renderer.id}`)
- assert(entry, `Missing renderer release entry for ${renderer.id}`)
- assert(entry.kind === 'renderer', `${renderer.packageName} must be a renderer release entry`)
- assert(entry.packageName === renderer.packageName, `${renderer.id} package name drifted from wrappers.json`)
- assert(entry.packageDir === renderer.packageDir, `${renderer.id} packageDir drifted from wrappers.json`)
- assertRepositoryMetadata(entry, renderer.github, renderer.packageDir)
- assert(
- entry.packageJson.bugs?.url === `${renderer.github}/issues`,
- `${entry.packageName} bugs.url must be ${renderer.github}/issues`
- )
- }
- for (const preset of wrapperManifest.presets || []) {
- const entry = entries.find(candidate => candidate.id === `preset-${preset.id}`)
- assert(entry, `Missing preset release entry for ${preset.id}`)
- assert(entry.kind === 'preset', `${preset.packageName} must be a preset release entry`)
- assert(entry.packageName === preset.packageName, `${preset.id} package name drifted from wrappers.json`)
- assert(entry.packageDir === preset.packageDir, `${preset.id} packageDir drifted from wrappers.json`)
- assertRepositoryMetadata(entry, preset.github, preset.packageDir)
- assert(
- entry.packageJson.bugs?.url === `${preset.github}/issues`,
- `${entry.packageName} bugs.url must be ${preset.github}/issues`
- )
- }
- for (const compatibilityPackage of wrapperManifest.compatibilityPackages || []) {
- assert(
- compatibilityPackage.id,
- `Compatibility package ${compatibilityPackage.packageName || '(unnamed)'} must declare id`
- )
- assert(
- compatibilityPackage.packageName,
- `Compatibility package ${compatibilityPackage.id} must declare packageName`
- )
- assert(
- compatibilityPackage.targetPackage,
- `Compatibility package ${compatibilityPackage.packageName} must declare targetPackage`
- )
- assert(
- compatibilityPackage.packageDir,
- `Compatibility package ${compatibilityPackage.packageName} must declare packageDir`
- )
- assert(
- historicalPackageNames.has(compatibilityPackage.packageName),
- `${compatibilityPackage.packageName} compatibility package is not declared in wrappers.json historicalPackages`
- )
- assert(
- standardPackageByHistoricalName.get(compatibilityPackage.packageName) === compatibilityPackage.targetPackage,
- `${compatibilityPackage.packageName} targetPackage must match its wrapper historical package owner`
- )
- assert(
- !compatibilityPackageByName.has(compatibilityPackage.packageName),
- `Duplicate compatibility package manifest entry ${compatibilityPackage.packageName}`
- )
- compatibilityPackageByName.set(compatibilityPackage.packageName, compatibilityPackage)
- }
- for (const historicalPackageName of historicalPackageNames) {
- assert(
- compatibilityPackageByName.has(historicalPackageName),
- `${historicalPackageName} is declared as historical package but has no compatibilityPackages entry`
- )
- }
- for (const entry of entries) {
- assertUnique(entryByName, entry.packageName, 'package name', entry.packageName)
- assertUnique(entryById, entry.id, 'package id', entry.packageName)
- assertUnique(entryByDir, entry.packageDir, 'package directory', entry.packageName)
- assert(
- entry.version === rootVersion,
- `${entry.packageName} version ${entry.version} must match root version ${rootVersion}`
- )
- assert(entry.packageJson.private !== true, `${entry.packageName} must not be private`)
- assert(
- entry.packageJson.publishConfig?.access === 'public',
- `${entry.packageName} publishConfig.access must be public`
- )
- assertPackageFiles(entry, ['README.md', 'README.en.md'])
- }
- const releasePackageNames = new Set(entryByName.keys())
- const coreEntry = entries.find(entry => entry.packageName === corePackageName)
- assert(coreEntry, `Missing core package ${corePackageName} in ecosystem release list`)
- assert(coreEntry.kind === 'core', `${corePackageName} must be marked as the core release package`)
- assert(coreEntry.version === rootVersion, `${corePackageName} version must match ${rootVersion}`)
- assertRepositoryMetadata(coreEntry, wrapperManifest.corePackage.sourceRepository, coreEntry.packageDir)
- for (const wrapper of wrapperManifest.wrappers) {
- const entry = entries.find(candidate => candidate.id === wrapper.id)
- assert(entry, `Missing standard wrapper release entry for ${wrapper.id}`)
- assert(entry.kind === 'standard-wrapper', `${wrapper.packageName} must be a standard-wrapper release entry`)
- assert(entry.packageName === wrapper.packageName, `${wrapper.id} package name drifted from wrappers.json`)
- assert(entry.packageDir === wrapper.packageDir, `${wrapper.id} packageDir drifted from wrappers.json`)
- assertRepositoryMetadata(entry, wrapper.github, wrapper.packageDir)
- assert(
- entry.packageJson.bugs?.url === `${wrapper.github}/issues`,
- `${entry.packageName} bugs.url must be ${wrapper.github}/issues`
- )
- }
- for (const entry of entries) {
- for (const [field, dependencies] of Object.entries(allDependencyFields(entry.packageJson))) {
- for (const [packageName, range] of Object.entries(dependencies)) {
- if (releasePackageNames.has(packageName)) {
- assert(
- range === expectedWorkspaceRange,
- `${entry.packageName} ${field}.${packageName} must be ${expectedWorkspaceRange}, got ${range}`
- )
- }
- }
- }
- if (entry.kind === 'standard-wrapper') {
- const wrapper = wrapperByPackageName.get(entry.packageName)
- const allowedWrapperDependencies = new Set(
- wrapper?.flavor === 'full' && wrapper.basePackage ? [wrapper.basePackage] : []
- )
- const dependencies = installDependencies(entry.packageJson)
- for (const packageName of Object.keys(dependencies)) {
- assert(
- !historicalPackageNames.has(packageName),
- `${entry.packageName} must not depend on historical compatibility package ${packageName}`
- )
- assert(
- !standardWrapperPackageNames.has(packageName)
- || packageName === entry.packageName
- || allowedWrapperDependencies.has(packageName),
- `${entry.packageName} must not depend on another standard wrapper package ${packageName}`
- )
- }
- if (wrapper?.flavor === 'full') {
- assert(
- dependencies['@file-viewer/preset-all'] === expectedWorkspaceRange,
- `${entry.packageName} full package must depend on @file-viewer/preset-all@${expectedWorkspaceRange}`
- )
- assert(
- wrapper.basePackage && dependencies[wrapper.basePackage] === expectedWorkspaceRange,
- `${entry.packageName} full package must depend on ${wrapper.basePackage}@${expectedWorkspaceRange}`
- )
- } else {
- assert(
- dependencies[corePackageName] === expectedWorkspaceRange,
- `${entry.packageName} must depend on ${corePackageName}@${expectedWorkspaceRange}`
- )
- }
- }
- if (entry.kind === 'compatibility') {
- const compatibilityPackage = compatibilityPackageByName.get(entry.packageName)
- assert(
- compatibilityPackage,
- `${entry.packageName} compatibility package is not declared in ecosystem/wrappers.json compatibilityPackages`
- )
- assert(
- entry.id === compatibilityPackage.id,
- `${entry.packageName} release entry id ${entry.id} must match compatibilityPackages id ${compatibilityPackage.id}`
- )
- assert(
- entry.packageDir === compatibilityPackage.packageDir,
- `${entry.packageName} packageDir must match compatibilityPackages.packageDir`
- )
- const standardPackageName = standardPackageByHistoricalName.get(entry.packageName)
- assert(
- standardPackageName === compatibilityPackage.targetPackage,
- `${entry.packageName} target package must be ${standardPackageName}`
- )
- const dependencies = installDependencies(entry.packageJson)
- if (standardPackageName === '@file-viewer/react') {
- assert(
- !dependencies[corePackageName] && !dependencies['@file-viewer/web'],
- `${entry.packageName} must remain a thin React alias instead of depending on core/web directly`
- )
- assert(
- dependencies['@file-viewer/react'] === expectedWorkspaceRange,
- `${entry.packageName} must depend on @file-viewer/react@${expectedWorkspaceRange}`
- )
- }
- if (standardPackageName === '@file-viewer/web') {
- assert(
- dependencies['@file-viewer/web'] === expectedWorkspaceRange,
- `${entry.packageName} must depend on @file-viewer/web@${expectedWorkspaceRange}`
- )
- }
- if (standardPackageName === '@file-viewer/vue2.7') {
- assert(
- dependencies['@file-viewer/vue2.7'] === expectedWorkspaceRange,
- `${entry.packageName} must depend on @file-viewer/vue2.7@${expectedWorkspaceRange}`
- )
- }
- if (standardPackageName === '@file-viewer/vue3') {
- assert(
- dependencies['@file-viewer/vue3'] === expectedWorkspaceRange,
- `${entry.packageName} must depend on @file-viewer/vue3@${expectedWorkspaceRange}`
- )
- }
- if (entry.packageName === 'file-viewer3') {
- assert(
- !dependencies['@flyfish-group/file-viewer3'],
- `${entry.packageName} must alias @file-viewer/vue3 directly instead of chaining through @flyfish-group/file-viewer3`
- )
- }
- }
- }
- console.log(
- `Verified ${entries.length} ecosystem package versions and workspace dependency ranges at ${rootVersion}.`
- )
|