build(release): reference workspace members through the workspace protocol
1504 hand-written ranges pointing at workspace members become workspace:^, so pnpm pack substitutes each member's real version at publication: sibling peerDependencies follow the family version instead of being pinned at ^0.0.1, and a reference to a vendored package follows that package's own line. Without this, publishing 0.0.2 ships peer ranges naming a version that does not exist, and 0.0.1-rc.1 does not satisfy ^0.0.1 either. It also retires ranges that had gone stale against the workspace: ^4.0.0-rc.6 for a 4.0.0-rc.7 checkout, ^3.17.0 for schemastery 3.18.0. workspace:* stays where an exact published version is the point, which is how the Landlock entry pins its platform packages. A workspace constraint now requires the protocol, so a new package cannot reintroduce a hand-written range. The same constraint caught packages/boot/cmdline arriving on master without the publishable trio, which this change completes.
This commit is contained in:
@@ -80,6 +80,8 @@ interface PackageManifest {
|
||||
repository?: { type?: string; url?: string; directory?: string }
|
||||
peerDependencies?: Record<string, string>
|
||||
devDependencies?: Record<string, string>
|
||||
dependencies?: Record<string, string>
|
||||
optionalDependencies?: Record<string, string>
|
||||
}
|
||||
|
||||
/** One workspace manifest and its repo-relative path. */
|
||||
@@ -380,9 +382,38 @@ function checkRepositoryVersion(): string[] {
|
||||
return ['package.json: version must be stable X.Y.Z']
|
||||
}
|
||||
|
||||
/** Dependency sections whose ranges reach a published tarball or a local install. */
|
||||
const dependencySections = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'] as const
|
||||
|
||||
/**
|
||||
* Require the `workspace:` protocol for every reference to a workspace member.
|
||||
*
|
||||
* A hand-written range says nothing about the version the workspace actually
|
||||
* carries, and `pnpm pack` leaves it alone: `^0.0.1` published from version
|
||||
* `0.0.2` names a version that does not exist. The protocol makes pack
|
||||
* substitute the member's real version, so no release step rewrites ranges.
|
||||
* @param manifests - every workspace manifest.
|
||||
* @returns One error per reference that names a workspace member without the protocol.
|
||||
*/
|
||||
function checkWorkspaceProtocol(manifests: readonly WorkspaceManifest[]): string[] {
|
||||
const members = new Set(manifests.map(entry => entry.manifest.name).filter(name => name !== undefined))
|
||||
const errors: string[] = []
|
||||
for (const { dir, manifest } of manifests) {
|
||||
for (const section of dependencySections) {
|
||||
for (const [name, range] of Object.entries(manifest[section] ?? {})) {
|
||||
if (!members.has(name) || range.startsWith('workspace:')) continue
|
||||
errors.push(`${manifest.name ?? dir}: ${section}.${name} must use the workspace: protocol, got ${range}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errors
|
||||
}
|
||||
|
||||
const manifests = workspaceManifests()
|
||||
const errors = [
|
||||
...checkRepositoryVersion(),
|
||||
...workspaceManifests().flatMap(checkWorkspace),
|
||||
...manifests.flatMap(checkWorkspace),
|
||||
...checkWorkspaceProtocol(manifests),
|
||||
...checkHierarchyShape(),
|
||||
...collectProjectReferenceFaceViolations(root),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user