Skip to content

Commit 3b0c79c

Browse files
authored
feat(publish): include all dependencies in package graph by default, allow no-sort (#3263)
1 parent 2f51588 commit 3b0c79c

6 files changed

Lines changed: 145 additions & 23 deletions

File tree

commands/publish/__tests__/publish-canary.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,9 @@ test("publish --canary", async () => {
7979
expect(npmPublish.registry).toMatchInlineSnapshot(`
8080
Map {
8181
"package-1" => "canary",
82-
"package-3" => "canary",
8382
"package-4" => "canary",
8483
"package-2" => "canary",
84+
"package-3" => "canary",
8585
}
8686
`);
8787
expect(writePkg.updatedVersions()).toMatchInlineSnapshot(`

commands/publish/__tests__/publish-command.test.js

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,24 @@ describe("PublishCommand", () => {
101101
expect(packDirectory.registry).toMatchInlineSnapshot(`
102102
Set {
103103
"package-1",
104-
"package-3",
105104
"package-4",
106105
"package-2",
106+
"package-3",
107107
}
108108
`);
109109
expect(npmPublish.registry).toMatchInlineSnapshot(`
110110
Map {
111111
"package-1" => "latest",
112-
"package-3" => "latest",
113112
"package-4" => "latest",
114113
"package-2" => "latest",
114+
"package-3" => "latest",
115115
}
116116
`);
117117
expect(npmPublish.order()).toEqual([
118118
"package-1",
119-
"package-3",
120119
"package-4",
121120
"package-2",
121+
"package-3",
122122
// package-5 is private
123123
]);
124124
expect(npmDistTag.remove).not.toHaveBeenCalled();
@@ -142,9 +142,9 @@ Map {
142142

143143
expect(npmPublish.order()).toEqual([
144144
"package-1",
145-
"package-3",
146145
"package-4",
147146
"package-2",
147+
"package-3",
148148
// package-5 is private
149149
]);
150150
});
@@ -169,6 +169,21 @@ Map {
169169
});
170170

171171
describe("--graph-type", () => {
172+
it("produces a topological ordering that _includes_ devDependencies when value is not set", async () => {
173+
const cwd = await initFixture("normal");
174+
175+
await lernaPublish(cwd)();
176+
177+
expect(npmPublish.order()).toEqual([
178+
"package-1",
179+
"package-4",
180+
"package-2",
181+
// package-3 has a peer/devDependency on package-2
182+
"package-3",
183+
// package-5 is private
184+
]);
185+
});
186+
172187
it("produces a topological ordering that _includes_ devDependencies when value is 'all'", async () => {
173188
const cwd = await initFixture("normal");
174189

@@ -184,6 +199,28 @@ Map {
184199
]);
185200
});
186201

202+
it("produces a topological ordering that _excludes_ devDependencies when value is 'dependencies' (DEPRECATED)", async () => {
203+
const cwd = await initFixture("normal");
204+
205+
await lernaPublish(cwd)("--graph-type", "dependencies");
206+
207+
expect(npmPublish.order()).toEqual([
208+
"package-1",
209+
// package-3 has a peer/devDependency on package-2
210+
"package-3",
211+
"package-4",
212+
"package-2",
213+
// package-5 is private
214+
]);
215+
216+
const logMessages = loggingOutput("warn");
217+
expect(logMessages).toMatchInlineSnapshot(`
218+
Array [
219+
"--graph-type=dependencies is deprecated and will be removed in lerna v6. If you have a use-case you feel requires it please open an issue to discuss: https://github.com/lerna/lerna/issues/new/choose",
220+
]
221+
`);
222+
});
223+
187224
it("throws an error when value is _not_ 'all' or 'dependencies'", async () => {
188225
const testDir = await initFixture("normal");
189226
const command = lernaPublish(testDir)("--graph-type", "poopy-pants");
@@ -192,6 +229,22 @@ Map {
192229
});
193230
});
194231

232+
describe("--no-sort", () => {
233+
it("produces a lexical ordering when --no-sort is set", async () => {
234+
const cwd = await initFixture("normal");
235+
236+
await lernaPublish(cwd)("--no-sort");
237+
238+
expect(npmPublish.order()).toEqual([
239+
"package-1",
240+
"package-2",
241+
"package-3",
242+
"package-4",
243+
// package-5 is private
244+
]);
245+
});
246+
});
247+
195248
describe("--otp", () => {
196249
getOneTimePassword.mockImplementation(() => Promise.resolve("654321"));
197250

@@ -317,24 +370,24 @@ Map {
317370
expect(packDirectory.registry).toMatchInlineSnapshot(`
318371
Set {
319372
"package-1",
320-
"package-3",
321373
"package-4",
322374
"package-2",
375+
"package-3",
323376
}
324377
`);
325378
expect(npmPublish.registry).toMatchInlineSnapshot(`
326379
Map {
327380
"package-1" => "latest",
328-
"package-3" => "latest",
329381
"package-4" => "latest",
330382
"package-2" => "latest",
383+
"package-3" => "latest",
331384
}
332385
`);
333386
expect(npmPublish.order()).toEqual([
334387
"package-1",
335-
"package-3",
336388
"package-4",
337389
"package-2",
390+
"package-3",
338391
// package-5 is private
339392
]);
340393
expect(npmDistTag.remove).not.toHaveBeenCalled();

commands/publish/__tests__/publish-from-git.test.js

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,29 @@ describe("publish from-git", () => {
4343
expect(output.logged()).toMatch("Found 4 packages to publish:");
4444
expect(npmPublish.order()).toEqual([
4545
"package-1",
46-
"package-3",
4746
"package-4",
4847
"package-2",
48+
"package-3",
49+
// package-5 is private
50+
]);
51+
});
52+
53+
it("publishes tagged packages, lexically sorted when --no-sort is present", async () => {
54+
const cwd = await initFixture("normal");
55+
56+
await gitTag(cwd, "v1.0.0");
57+
await lernaPublish(cwd)("from-git", "--no-sort");
58+
59+
// called from chained describeRef()
60+
expect(throwIfUncommitted).toHaveBeenCalled();
61+
62+
expect(promptConfirmation).toHaveBeenLastCalledWith("Are you sure you want to publish these packages?");
63+
expect(output.logged()).toMatch("Found 4 packages to publish:");
64+
expect(npmPublish.order()).toEqual([
65+
"package-1",
66+
"package-2",
67+
"package-3",
68+
"package-4",
4969
// package-5 is private
5070
]);
5171
});
@@ -64,9 +84,9 @@ describe("publish from-git", () => {
6484

6585
expect(npmPublish.order()).toEqual([
6686
"package-1",
67-
"package-3",
6887
"package-4",
6988
"package-2",
89+
"package-3",
7090
// package-5 is private
7191
]);
7292
});
@@ -79,9 +99,9 @@ describe("publish from-git", () => {
7999

80100
expect(npmPublish.order()).toEqual([
81101
"package-1",
82-
"package-3",
83102
"package-4",
84103
"package-2",
104+
"package-3",
85105
// package-5 is private
86106
]);
87107
});

commands/publish/__tests__/publish-from-package.test.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,25 @@ describe("publish from-package", () => {
5555

5656
expect(npmPublish.order()).toEqual([
5757
"package-1",
58-
"package-3",
5958
"package-4",
6059
"package-2",
60+
"package-3",
61+
// package-5 is private
62+
]);
63+
});
64+
65+
it("publishes unpublished independent packages, lexically sorted when --no-sort is present", async () => {
66+
const cwd = await initFixture("independent");
67+
68+
getUnpublishedPackages.mockImplementationOnce((packageGraph) => Array.from(packageGraph.values()));
69+
70+
await lernaPublish(cwd)("from-package", "--no-sort");
71+
72+
expect(npmPublish.order()).toEqual([
73+
"package-1",
74+
"package-2",
75+
"package-3",
76+
"package-4",
6177
// package-5 is private
6278
]);
6379
});

commands/publish/index.js

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ class PublishCommand extends Command {
5858
configureProperties() {
5959
super.configureProperties();
6060

61+
// For publish we want to enable topological sorting by default, but allow users to override with --no-sort
62+
this.toposort = this.options.sort !== false;
63+
6164
// Defaults are necessary here because yargs defaults
6265
// override durable options provided by a config file
6366
const {
@@ -102,6 +105,13 @@ class PublishCommand extends Command {
102105
);
103106
}
104107

108+
if (this.options.graphType === "dependencies") {
109+
this.logger.warn(
110+
"graph-type",
111+
"--graph-type=dependencies is deprecated and will be removed in lerna v6. If you have a use-case you feel requires it please open an issue to discuss: https://github.com/lerna/lerna/issues/new/choose"
112+
);
113+
}
114+
105115
if (this.options.skipNpm) {
106116
// TODO: remove in next major release
107117
this.logger.warn("deprecated", "Instead of --skip-npm, call `lerna version` directly");
@@ -626,15 +636,21 @@ class PublishCommand extends Command {
626636
}
627637

628638
topoMapPackages(mapper) {
629-
// we don't respect --no-sort here, sorry
630639
return runTopologically(this.packagesToPublish, mapper, {
631640
concurrency: this.concurrency,
632641
rejectCycles: this.options.rejectCycles,
633-
// By default, do not include devDependencies in the graph because it would
634-
// increase the chance of dependency cycles, causing less-than-ideal order.
635-
// If the user has opted-in to --graph-type=all (or "graphType": "all" in lerna.json),
636-
// devDependencies _will_ be included in the graph construction.
637-
graphType: this.options.graphType === "all" ? "allDependencies" : "dependencies",
642+
/**
643+
* Previously `publish` had unique default behavior for graph creation vs other commands: it would only consider dependencies when finding
644+
* edges by default (i.e. relationships between packages specified via devDependencies would be ignored). It was documented to be the case
645+
* in order to try and reduce the chance of dependency cycles.
646+
*
647+
* We are removing this behavior altogether in v6 because we do not want to have different ways of constructing the graph,
648+
* only different ways of utilizing it (e.g. --no-sort vs topological sort).
649+
*
650+
* Therefore until we remove graphType altogether in v6, we provide a way for users to opt into the old default behavior
651+
* by setting the `graphType` option to `dependencies`.
652+
*/
653+
graphType: this.options.graphType === "dependencies" ? "dependencies" : "allDependencies",
638654
});
639655
}
640656

@@ -676,7 +692,12 @@ class PublishCommand extends Command {
676692
].filter(Boolean)
677693
);
678694

679-
chain = chain.then(() => this.topoMapPackages(mapper));
695+
chain = chain.then(() => {
696+
if (this.toposort) {
697+
return this.topoMapPackages(mapper);
698+
}
699+
return pMap(this.packagesToPublish, mapper, { concurrency: this.concurrency });
700+
});
680701

681702
chain = chain.then(() => removeTempLicenses(this.packagesToBeLicensed));
682703

@@ -729,7 +750,12 @@ class PublishCommand extends Command {
729750
].filter(Boolean)
730751
);
731752

732-
chain = chain.then(() => this.topoMapPackages(mapper));
753+
chain = chain.then(() => {
754+
if (this.toposort) {
755+
return this.topoMapPackages(mapper);
756+
}
757+
return pMap(this.packagesToPublish, mapper, { concurrency: this.concurrency });
758+
});
733759

734760
if (!this.hasRootedLeaf) {
735761
// cyclical "publish" lifecycles are automatically skipped
@@ -772,7 +798,12 @@ class PublishCommand extends Command {
772798
});
773799
};
774800

775-
chain = chain.then(() => this.topoMapPackages(mapper));
801+
chain = chain.then(() => {
802+
if (this.toposort) {
803+
return this.topoMapPackages(mapper);
804+
}
805+
return pMap(this.packagesToPublish, mapper, { concurrency: this.concurrency });
806+
});
776807

777808
return chain.finally(() => tracker.finish());
778809
}

core/lerna/schemas/lerna-schema.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,8 +1600,10 @@
16001600
},
16011601
"graphType": {
16021602
"type": "string",
1603-
"enum": ["all", "depencencies"],
1604-
"description": "For `lerna publish`, the type of dependency to use when determining package hierarchy."
1603+
"enum": ["all", "dependencies"],
1604+
"description": "DEPRECATED: For `lerna publish`, if you want to ignore devDependencies when considering the package graph you can set this property equal to 'dependencies'. This will be removed in v6 of lerna.",
1605+
"default": "all",
1606+
"deprecated": true
16051607
},
16061608
"otp": {
16071609
"type": "string",

0 commit comments

Comments
 (0)