Skip to content

Commit 8728d4a

Browse files
authored
feat(node-workspace): add root package-lock.json (#2162)
1 parent deeef9d commit 8728d4a

File tree

8 files changed

+150
-15
lines changed

8 files changed

+150
-15
lines changed

__snapshots__/package-lock-json.js

+16-1
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,24 @@ exports['PackageLockJson updateContent v3 monorepo updates the package version 1
3535
"name": "release-please",
3636
"version": "14.0.0"
3737
},
38+
"node_modules/release-please-foo": {
39+
"resolved": "packages/foo",
40+
"link": true
41+
},
42+
"node_modules/release-please-bar": {
43+
"resolved": "packages/bar",
44+
"link": true
45+
},
3846
"packages/foo": {
3947
"name": "release-please-foo",
40-
"version": "2.0.0"
48+
"version": "2.0.0",
49+
"dependencies": {
50+
"release-please-bar": "3.0.0"
51+
}
52+
},
53+
"packages/bar": {
54+
"name": "release-please-bar",
55+
"version": "3.0.0"
4156
}
4257
}
4358
}

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"scripts": {
88
"test": "cross-env ENVIRONMENT=test LC_ALL=en c8 mocha --node-option no-experimental-fetch --recursive --timeout=5000 build/test",
99
"docs": "echo add docs tests",
10-
"test:snap": "SNAPSHOT_UPDATE=1 LC_ALL=en npm test",
10+
"test:snap": "cross-env SNAPSHOT_UPDATE=1 LC_ALL=en npm test",
1111
"clean": "gts clean",
1212
"prepare": "npm run compile",
1313
"lint": "gts check",

src/plugins/node-workspace.ts

+50-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import {GitHub} from '../github';
1616
import {CandidateReleasePullRequest, RepositoryConfig} from '../manifest';
17+
import {PackageLockJson} from '../updaters/node/package-lock-json';
1718
import {Version, VersionsMap} from '../version';
1819
import {PullRequestTitle} from '../util/pull-request-title';
1920
import {PullRequestBody} from '../util/pull-request-body';
@@ -82,6 +83,7 @@ export class NodeWorkspace extends WorkspacePlugin<Package> {
8283
options: NodeWorkspaceOptions = {}
8384
) {
8485
super(github, targetBranch, repositoryConfig, options);
86+
8587
this.alwaysLinkLocal = options.alwaysLinkLocal === false ? false : true;
8688
this.updatePeerDependencies = options.updatePeerDependencies === true;
8789
}
@@ -201,6 +203,13 @@ export class NodeWorkspace extends WorkspacePlugin<Package> {
201203
existingCandidate.pullRequest.updates.map(update => {
202204
if (update.path === addPath(existingCandidate.path, 'package.json')) {
203205
update.updater = new CompositeUpdater(update.updater, updater);
206+
} else if (
207+
update.path === addPath(existingCandidate.path, 'package-lock.json')
208+
) {
209+
update.updater = new PackageLockJson({
210+
version: newVersion,
211+
versionsMap: updatedVersions,
212+
});
204213
} else if (update.updater instanceof Changelog) {
205214
if (dependencyNotes) {
206215
update.updater.changelogEntry =
@@ -303,6 +312,14 @@ export class NodeWorkspace extends WorkspacePlugin<Package> {
303312
versionsMap: updatedVersions,
304313
}),
305314
},
315+
{
316+
path: addPath(updatedPackage.path, 'package-lock.json'),
317+
createIfMissing: false,
318+
updater: new PackageJson({
319+
version: newVersion,
320+
versionsMap: updatedVersions,
321+
}),
322+
},
306323
{
307324
path: addPath(updatedPackage.path, 'CHANGELOG.md'),
308325
createIfMissing: false,
@@ -334,7 +351,39 @@ export class NodeWorkspace extends WorkspacePlugin<Package> {
334351
candidates: CandidateReleasePullRequest[],
335352
_updatedVersions: VersionsMap
336353
): CandidateReleasePullRequest[] {
337-
// NOP for node workspaces
354+
if (candidates.length === 0) {
355+
return candidates;
356+
}
357+
358+
const [candidate] = candidates;
359+
360+
// check for root lock file in pull request
361+
let hasRootLockFile: boolean | undefined;
362+
for (let i = 0; i < candidate.pullRequest.updates.length; i++) {
363+
if (
364+
candidate.pullRequest.updates[i].path === '.package-lock.json' ||
365+
candidate.pullRequest.updates[i].path === './package-lock.json' ||
366+
candidate.pullRequest.updates[i].path === 'package-lock.json' ||
367+
candidate.pullRequest.updates[i].path === '/package-lock.json'
368+
) {
369+
hasRootLockFile = true;
370+
break;
371+
}
372+
}
373+
374+
// if there is a root lock file, then there is no additional pull request update necessary.
375+
if (hasRootLockFile) {
376+
return candidates;
377+
}
378+
379+
candidate.pullRequest.updates.push({
380+
path: 'package-lock.json',
381+
createIfMissing: false,
382+
updater: new PackageLockJson({
383+
versionsMap: _updatedVersions,
384+
}),
385+
});
386+
338387
return candidates;
339388
}
340389

src/strategies/node.ts

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export class Node extends BaseStrategy {
3131
): Promise<Update[]> {
3232
const updates: Update[] = [];
3333
const version = options.newVersion;
34+
const versionsMap = options.versionsMap;
3435
const packageName = (await this.getPackageName()) ?? '';
3536
const lockFiles = ['package-lock.json', 'npm-shrinkwrap.json'];
3637
lockFiles.forEach(lockFile => {
@@ -39,6 +40,7 @@ export class Node extends BaseStrategy {
3940
createIfMissing: false,
4041
updater: new PackageLockJson({
4142
version,
43+
versionsMap,
4244
}),
4345
});
4446
});

src/updaters/node/package-json.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414

1515
import {jsonStringify} from '../../util/json-stringify';
1616
import {logger as defaultLogger, Logger} from '../../util/logger';
17+
import {Version, VersionsMap} from '../../version';
1718
import {DefaultUpdater} from '../default';
18-
import {VersionsMap, Version} from '../../version';
1919

20-
type LockFile = {
20+
export type PackageJsonDescriptor = {
21+
name?: string;
22+
resolved?: string;
23+
link?: boolean;
2124
version: string;
2225
dependencies?: Record<string, string>;
2326
devDependencies?: Record<string, string>;
@@ -32,10 +35,11 @@ export class PackageJson extends DefaultUpdater {
3235
/**
3336
* Given initial file contents, return updated contents.
3437
* @param {string} content The initial content
38+
* @param logger
3539
* @returns {string} The updated content
3640
*/
3741
updateContent(content: string, logger: Logger = defaultLogger): string {
38-
const parsed = JSON.parse(content) as LockFile;
42+
const parsed = JSON.parse(content) as PackageJsonDescriptor;
3943
logger.info(`updating from ${parsed.version} to ${this.version}`);
4044
parsed.version = this.version.toString();
4145

@@ -98,16 +102,15 @@ export function newVersionWithRange(
98102
* where the key is the dependency name and the value is the dependency range
99103
* @param {VersionsMap} updatedVersions Map of new versions (without dependency range prefixes)
100104
*/
101-
function updateDependencies(
105+
export function updateDependencies(
102106
dependencies: Record<string, string>,
103107
updatedVersions: VersionsMap
104108
) {
105109
for (const depName of Object.keys(dependencies)) {
106110
const newVersion = updatedVersions.get(depName);
107111
if (newVersion) {
108112
const oldVersion = dependencies[depName];
109-
const newVersionString = newVersionWithRange(oldVersion, newVersion);
110-
dependencies[depName] = newVersionString;
113+
dependencies[depName] = newVersionWithRange(oldVersion, newVersion);
111114
}
112115
}
113116
}

src/updaters/node/package-lock-json.ts

+56-6
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,80 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
import {Updater} from '../../update';
1516
import {jsonStringify} from '../../util/json-stringify';
1617
import {logger as defaultLogger, Logger} from '../../util/logger';
17-
import {DefaultUpdater} from '../default';
18+
import {Version, VersionsMap} from '../../version';
19+
import {UpdateOptions} from '../default';
20+
import {PackageJsonDescriptor, updateDependencies} from './package-json';
1821

1922
type LockFileV2 = {
2023
version: string;
2124
lockfileVersion?: number;
22-
packages: Record<string, {version: string; name: string}>;
25+
packages: Record<string, PackageJsonDescriptor>;
2326
};
2427

2528
/**
2629
* Updates a Node.js package-lock.json file's version and '' package
2730
* version (for a v2 lock file).
2831
*/
29-
export class PackageLockJson extends DefaultUpdater {
32+
export class PackageLockJson implements Updater {
33+
version?: Version;
34+
versionsMap?: VersionsMap;
35+
constructor(options: Partial<UpdateOptions>) {
36+
this.version = options.version;
37+
this.versionsMap = options.versionsMap;
38+
}
3039
updateContent(content: string, logger: Logger = defaultLogger): string {
3140
const parsed = JSON.parse(content) as LockFileV2;
32-
logger.info(`updating from ${parsed.version} to ${this.version}`);
33-
parsed.version = this.version.toString();
41+
if (this.version) {
42+
logger.info(`updating from ${parsed.version} to ${this.version}`);
43+
parsed.version = this.version.toString();
44+
}
45+
3446
if (parsed.lockfileVersion === 2 || parsed.lockfileVersion === 3) {
35-
parsed.packages[''].version = this.version.toString();
47+
if (this.version) {
48+
parsed.packages[''].version = this.version.toString();
49+
}
50+
51+
if (this.versionsMap) {
52+
this.versionsMap.forEach((version, name) => {
53+
let pkg = parsed.packages['node_modules/' + name];
54+
if (!pkg) {
55+
return;
56+
}
57+
58+
// @see https://docs.npmjs.com/cli/v10/configuring-npm/package-lock-json#packages
59+
if (pkg.link && pkg.resolved) {
60+
pkg = parsed.packages[pkg.resolved];
61+
if (!pkg) {
62+
return;
63+
}
64+
}
65+
66+
pkg.version = version.toString();
67+
68+
if (pkg.dependencies) {
69+
updateDependencies(pkg.dependencies, this.versionsMap!);
70+
}
71+
if (pkg.devDependencies) {
72+
updateDependencies(pkg.devDependencies, this.versionsMap!);
73+
}
74+
if (pkg.peerDependencies) {
75+
updateDependencies(pkg.peerDependencies, this.versionsMap!);
76+
}
77+
if (pkg.optionalDependencies) {
78+
updateDependencies(pkg.optionalDependencies, this.versionsMap!);
79+
}
80+
});
81+
}
3682
}
3783
if (this.versionsMap) {
3884
for (const [, obj] of Object.entries(parsed.packages)) {
85+
if (!obj.name) {
86+
continue;
87+
}
88+
3989
const ver = this.versionsMap.get(obj.name);
4090
if (ver) {
4191
obj.version = ver.toString();

test/updaters/fixtures/package-lock-v3-workspace.json

+15
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,23 @@
88
"name": "release-please",
99
"version": "11.1.0"
1010
},
11+
"node_modules/release-please-foo": {
12+
"resolved": "packages/foo",
13+
"link": true
14+
},
15+
"node_modules/release-please-bar": {
16+
"resolved": "packages/bar",
17+
"link": true
18+
},
1119
"packages/foo": {
1220
"name": "release-please-foo",
21+
"version": "1.0.0",
22+
"dependencies": {
23+
"release-please-bar": "1.0.0"
24+
}
25+
},
26+
"packages/bar": {
27+
"name": "release-please-bar",
1328
"version": "1.0.0"
1429
}
1530
}

test/updaters/package-lock-json.ts

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ describe('PackageLockJson', () => {
7272
);
7373
const versionsMap = new Map();
7474
versionsMap.set('release-please-foo', new Version(2, 0, 0));
75+
versionsMap.set('release-please-bar', new Version(3, 0, 0));
7576
const packageJson = new PackageLockJson({
7677
version: Version.parse('14.0.0'),
7778
versionsMap,

0 commit comments

Comments
 (0)