Skip to content

Commit f59bf3c

Browse files
committed
feat(link): Add 'convert' subcommand to aid migration to local file: specifiers
1 parent ec2b8f5 commit f59bf3c

File tree

4 files changed

+69
-12
lines changed

4 files changed

+69
-12
lines changed

commands/link/command.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,27 @@
55
*/
66
exports.command = "link";
77

8-
exports.describe = "Symlink together all packages which are dependencies of each other";
8+
exports.describe = "Symlink together all packages that are dependencies of each other";
99

10-
exports.builder = {
11-
"force-local": {
12-
group: "Command Options:",
13-
describe: "Force local",
14-
type: "boolean",
15-
default: undefined,
16-
},
10+
exports.builder = yargs => {
11+
yargs.options({
12+
"force-local": {
13+
group: "Command Options:",
14+
describe: "Force local sibling links regardless of version range match",
15+
type: "boolean",
16+
default: undefined,
17+
},
18+
});
19+
20+
return yargs.command(
21+
"convert",
22+
"Replace local sibling version ranges with relative file: specifiers",
23+
() => {},
24+
handler
25+
);
1726
};
1827

19-
exports.handler = function handler(argv) {
28+
exports.handler = handler;
29+
function handler(argv) {
2030
return require(".")(argv);
21-
};
31+
}

commands/link/index.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
"use strict";
22

3+
const path = require("path");
4+
const pMap = require("p-map");
5+
const slash = require("slash");
36
const Command = require("@lerna/command");
47
const PackageGraph = require("@lerna/package-graph");
58
const symlinkDependencies = require("@lerna/symlink-dependencies");
@@ -32,8 +35,48 @@ class LinkCommand extends Command {
3235
}
3336

3437
execute() {
38+
if (this.options._.pop() === "convert") {
39+
return this.convertLinksToFileSpecs();
40+
}
41+
3542
return symlinkDependencies(this.packages, this.targetGraph, this.logger);
3643
}
44+
45+
convertLinksToFileSpecs() {
46+
const rootPkg = this.project.manifest;
47+
const rootDependencies = {};
48+
const hoisted = {};
49+
const changed = new Set();
50+
const savePrefix = "file:";
51+
52+
for (const targetNode of this.targetGraph.values()) {
53+
const resolved = { name: targetNode.name, type: "directory" };
54+
55+
// install root file: specifiers to avoid bootstrap
56+
rootDependencies[targetNode.name] = targetNode.pkg.resolved.saveSpec;
57+
58+
for (const depNode of targetNode.localDependents.values()) {
59+
const depVersion = slash(path.relative(depNode.pkg.location, targetNode.pkg.location));
60+
// console.log("\n%s\n %j: %j", depNode.name, name, `${savePrefix}${depVersion}`);
61+
62+
depNode.pkg.updateLocalDependency(resolved, depVersion, savePrefix);
63+
changed.add(depNode);
64+
}
65+
66+
if (targetNode.pkg.devDependencies) {
67+
// hoist _all_ devDependencies to the root
68+
Object.assign(hoisted, targetNode.pkg.devDependencies);
69+
targetNode.pkg.set("devDependencies", {});
70+
changed.add(targetNode);
71+
}
72+
}
73+
74+
// mutate project manifest, completely overwriting existing dependencies
75+
rootPkg.set("dependencies", rootDependencies);
76+
rootPkg.set("devDependencies", Object.assign(rootPkg.get("devDependencies") || {}, hoisted));
77+
78+
return pMap(changed, node => node.pkg.serialize()).then(() => rootPkg.serialize());
79+
}
3780
}
3881

3982
module.exports.LinkCommand = LinkCommand;

commands/link/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
"dependencies": {
3434
"@lerna/command": "file:../../core/command",
3535
"@lerna/package-graph": "file:../../core/package-graph",
36-
"@lerna/symlink-dependencies": "file:../../utils/symlink-dependencies"
36+
"@lerna/symlink-dependencies": "file:../../utils/symlink-dependencies",
37+
"p-map": "^1.2.0",
38+
"slash": "^1.0.0"
3739
}
3840
}

package-lock.json

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)