diff --git a/__snapshots__/package-lock-json.js b/__snapshots__/package-lock-json.js index 90c5cdabe..0c197f8a6 100644 --- a/__snapshots__/package-lock-json.js +++ b/__snapshots__/package-lock-json.js @@ -35,9 +35,24 @@ exports['PackageLockJson updateContent v3 monorepo updates the package version 1 "name": "release-please", "version": "14.0.0" }, + "node_modules/release-please-foo": { + "resolved": "packages/foo", + "link": true + }, + "node_modules/release-please-bar": { + "resolved": "packages/bar", + "link": true + }, "packages/foo": { "name": "release-please-foo", - "version": "2.0.0" + "version": "2.0.0", + "dependencies": { + "release-please-bar": "3.0.0" + } + }, + "packages/bar": { + "name": "release-please-bar", + "version": "3.0.0" } } } diff --git a/package.json b/package.json index 1910d56c7..fc6972f4a 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "scripts": { "test": "cross-env ENVIRONMENT=test LC_ALL=en c8 mocha --node-option no-experimental-fetch --recursive --timeout=5000 build/test", "docs": "echo add docs tests", - "test:snap": "SNAPSHOT_UPDATE=1 LC_ALL=en npm test", + "test:snap": "cross-env SNAPSHOT_UPDATE=1 LC_ALL=en npm test", "clean": "gts clean", "prepare": "npm run compile", "lint": "gts check", diff --git a/src/plugins/node-workspace.ts b/src/plugins/node-workspace.ts index f3dd8e376..1d309ebd9 100644 --- a/src/plugins/node-workspace.ts +++ b/src/plugins/node-workspace.ts @@ -14,6 +14,7 @@ import {GitHub} from '../github'; import {CandidateReleasePullRequest, RepositoryConfig} from '../manifest'; +import {PackageLockJson} from '../updaters/node/package-lock-json'; import {Version, VersionsMap} from '../version'; import {PullRequestTitle} from '../util/pull-request-title'; import {PullRequestBody} from '../util/pull-request-body'; @@ -82,6 +83,7 @@ export class NodeWorkspace extends WorkspacePlugin { options: NodeWorkspaceOptions = {} ) { super(github, targetBranch, repositoryConfig, options); + this.alwaysLinkLocal = options.alwaysLinkLocal === false ? false : true; this.updatePeerDependencies = options.updatePeerDependencies === true; } @@ -201,6 +203,13 @@ export class NodeWorkspace extends WorkspacePlugin { existingCandidate.pullRequest.updates.map(update => { if (update.path === addPath(existingCandidate.path, 'package.json')) { update.updater = new CompositeUpdater(update.updater, updater); + } else if ( + update.path === addPath(existingCandidate.path, 'package-lock.json') + ) { + update.updater = new PackageLockJson({ + version: newVersion, + versionsMap: updatedVersions, + }); } else if (update.updater instanceof Changelog) { if (dependencyNotes) { update.updater.changelogEntry = @@ -303,6 +312,14 @@ export class NodeWorkspace extends WorkspacePlugin { versionsMap: updatedVersions, }), }, + { + path: addPath(updatedPackage.path, 'package-lock.json'), + createIfMissing: false, + updater: new PackageJson({ + version: newVersion, + versionsMap: updatedVersions, + }), + }, { path: addPath(updatedPackage.path, 'CHANGELOG.md'), createIfMissing: false, @@ -334,7 +351,39 @@ export class NodeWorkspace extends WorkspacePlugin { candidates: CandidateReleasePullRequest[], _updatedVersions: VersionsMap ): CandidateReleasePullRequest[] { - // NOP for node workspaces + if (candidates.length === 0) { + return candidates; + } + + const [candidate] = candidates; + + // check for root lock file in pull request + let hasRootLockFile: boolean | undefined; + for (let i = 0; i < candidate.pullRequest.updates.length; i++) { + if ( + candidate.pullRequest.updates[i].path === '.package-lock.json' || + candidate.pullRequest.updates[i].path === './package-lock.json' || + candidate.pullRequest.updates[i].path === 'package-lock.json' || + candidate.pullRequest.updates[i].path === '/package-lock.json' + ) { + hasRootLockFile = true; + break; + } + } + + // if there is a root lock file, then there is no additional pull request update necessary. + if (hasRootLockFile) { + return candidates; + } + + candidate.pullRequest.updates.push({ + path: 'package-lock.json', + createIfMissing: false, + updater: new PackageLockJson({ + versionsMap: _updatedVersions, + }), + }); + return candidates; } diff --git a/src/strategies/node.ts b/src/strategies/node.ts index 052012d3a..52fc2c176 100644 --- a/src/strategies/node.ts +++ b/src/strategies/node.ts @@ -31,6 +31,7 @@ export class Node extends BaseStrategy { ): Promise { const updates: Update[] = []; const version = options.newVersion; + const versionsMap = options.versionsMap; const packageName = (await this.getPackageName()) ?? ''; const lockFiles = ['package-lock.json', 'npm-shrinkwrap.json']; lockFiles.forEach(lockFile => { @@ -39,6 +40,7 @@ export class Node extends BaseStrategy { createIfMissing: false, updater: new PackageLockJson({ version, + versionsMap, }), }); }); diff --git a/src/updaters/node/package-json.ts b/src/updaters/node/package-json.ts index 46b2e2306..9eb5cf104 100644 --- a/src/updaters/node/package-json.ts +++ b/src/updaters/node/package-json.ts @@ -14,10 +14,13 @@ import {jsonStringify} from '../../util/json-stringify'; import {logger as defaultLogger, Logger} from '../../util/logger'; +import {Version, VersionsMap} from '../../version'; import {DefaultUpdater} from '../default'; -import {VersionsMap, Version} from '../../version'; -type LockFile = { +export type PackageJsonDescriptor = { + name?: string; + resolved?: string; + link?: boolean; version: string; dependencies?: Record; devDependencies?: Record; @@ -32,10 +35,11 @@ export class PackageJson extends DefaultUpdater { /** * Given initial file contents, return updated contents. * @param {string} content The initial content + * @param logger * @returns {string} The updated content */ updateContent(content: string, logger: Logger = defaultLogger): string { - const parsed = JSON.parse(content) as LockFile; + const parsed = JSON.parse(content) as PackageJsonDescriptor; logger.info(`updating from ${parsed.version} to ${this.version}`); parsed.version = this.version.toString(); @@ -98,7 +102,7 @@ export function newVersionWithRange( * where the key is the dependency name and the value is the dependency range * @param {VersionsMap} updatedVersions Map of new versions (without dependency range prefixes) */ -function updateDependencies( +export function updateDependencies( dependencies: Record, updatedVersions: VersionsMap ) { @@ -106,8 +110,7 @@ function updateDependencies( const newVersion = updatedVersions.get(depName); if (newVersion) { const oldVersion = dependencies[depName]; - const newVersionString = newVersionWithRange(oldVersion, newVersion); - dependencies[depName] = newVersionString; + dependencies[depName] = newVersionWithRange(oldVersion, newVersion); } } } diff --git a/src/updaters/node/package-lock-json.ts b/src/updaters/node/package-lock-json.ts index f540fb49d..b2c6cd7a0 100644 --- a/src/updaters/node/package-lock-json.ts +++ b/src/updaters/node/package-lock-json.ts @@ -12,30 +12,80 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {Updater} from '../../update'; import {jsonStringify} from '../../util/json-stringify'; import {logger as defaultLogger, Logger} from '../../util/logger'; -import {DefaultUpdater} from '../default'; +import {Version, VersionsMap} from '../../version'; +import {UpdateOptions} from '../default'; +import {PackageJsonDescriptor, updateDependencies} from './package-json'; type LockFileV2 = { version: string; lockfileVersion?: number; - packages: Record; + packages: Record; }; /** * Updates a Node.js package-lock.json file's version and '' package * version (for a v2 lock file). */ -export class PackageLockJson extends DefaultUpdater { +export class PackageLockJson implements Updater { + version?: Version; + versionsMap?: VersionsMap; + constructor(options: Partial) { + this.version = options.version; + this.versionsMap = options.versionsMap; + } updateContent(content: string, logger: Logger = defaultLogger): string { const parsed = JSON.parse(content) as LockFileV2; - logger.info(`updating from ${parsed.version} to ${this.version}`); - parsed.version = this.version.toString(); + if (this.version) { + logger.info(`updating from ${parsed.version} to ${this.version}`); + parsed.version = this.version.toString(); + } + if (parsed.lockfileVersion === 2 || parsed.lockfileVersion === 3) { - parsed.packages[''].version = this.version.toString(); + if (this.version) { + parsed.packages[''].version = this.version.toString(); + } + + if (this.versionsMap) { + this.versionsMap.forEach((version, name) => { + let pkg = parsed.packages['node_modules/' + name]; + if (!pkg) { + return; + } + + // @see https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#packages + if (pkg.link && pkg.resolved) { + pkg = parsed.packages[pkg.resolved]; + if (!pkg) { + return; + } + } + + pkg.version = version.toString(); + + if (pkg.dependencies) { + updateDependencies(pkg.dependencies, this.versionsMap!); + } + if (pkg.devDependencies) { + updateDependencies(pkg.devDependencies, this.versionsMap!); + } + if (pkg.peerDependencies) { + updateDependencies(pkg.peerDependencies, this.versionsMap!); + } + if (pkg.optionalDependencies) { + updateDependencies(pkg.optionalDependencies, this.versionsMap!); + } + }); + } } if (this.versionsMap) { for (const [, obj] of Object.entries(parsed.packages)) { + if (!obj.name) { + continue; + } + const ver = this.versionsMap.get(obj.name); if (ver) { obj.version = ver.toString(); diff --git a/test/updaters/fixtures/package-lock-v3-workspace.json b/test/updaters/fixtures/package-lock-v3-workspace.json index c8d2d8979..061cac064 100644 --- a/test/updaters/fixtures/package-lock-v3-workspace.json +++ b/test/updaters/fixtures/package-lock-v3-workspace.json @@ -8,8 +8,23 @@ "name": "release-please", "version": "11.1.0" }, + "node_modules/release-please-foo": { + "resolved": "packages/foo", + "link": true + }, + "node_modules/release-please-bar": { + "resolved": "packages/bar", + "link": true + }, "packages/foo": { "name": "release-please-foo", + "version": "1.0.0", + "dependencies": { + "release-please-bar": "1.0.0" + } + }, + "packages/bar": { + "name": "release-please-bar", "version": "1.0.0" } } diff --git a/test/updaters/package-lock-json.ts b/test/updaters/package-lock-json.ts index 873591455..71e087440 100644 --- a/test/updaters/package-lock-json.ts +++ b/test/updaters/package-lock-json.ts @@ -72,6 +72,7 @@ describe('PackageLockJson', () => { ); const versionsMap = new Map(); versionsMap.set('release-please-foo', new Version(2, 0, 0)); + versionsMap.set('release-please-bar', new Version(3, 0, 0)); const packageJson = new PackageLockJson({ version: Version.parse('14.0.0'), versionsMap,