Skip to content

Commit 0ebadf5

Browse files
nlflukekarrys
authored andcommitted
feat(arborist): add support for installLinks
when set, installLinks instructs arborist to pack and extract a file: dependency rather than creating a symlink to it. this has the effect of also installing the dependencies for the linked dependency, though if local changes are made it also requires the user to reinstall the package
1 parent 3d96494 commit 0ebadf5

File tree

11 files changed

+471
-10
lines changed

11 files changed

+471
-10
lines changed

workspaces/arborist/lib/arborist/build-ideal-tree.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
124124
globalStyle = false,
125125
idealTree = null,
126126
includeWorkspaceRoot = false,
127+
installLinks = false,
127128
legacyPeerDeps = false,
128129
packageLock = true,
129130
strictPeerDeps = false,
@@ -135,6 +136,7 @@ module.exports = cls => class IdealTreeBuilder extends cls {
135136
this[_strictPeerDeps] = !!strictPeerDeps
136137

137138
this.idealTree = idealTree
139+
this.installLinks = installLinks
138140
this.legacyPeerDeps = legacyPeerDeps
139141

140142
this[_usePackageLock] = packageLock
@@ -410,6 +412,7 @@ Try using the package name instead, e.g:
410412
peer: false,
411413
optional: false,
412414
global: this[_global],
415+
installLinks: this.installLinks,
413416
legacyPeerDeps: this.legacyPeerDeps,
414417
loadOverrides: true,
415418
})
@@ -424,6 +427,7 @@ Try using the package name instead, e.g:
424427
peer: false,
425428
optional: false,
426429
global: this[_global],
430+
installLinks: this.installLinks,
427431
legacyPeerDeps: this.legacyPeerDeps,
428432
root,
429433
})
@@ -992,6 +996,7 @@ This is a one-time fix-up, please be patient...
992996
preferDedupe: this[_preferDedupe],
993997
legacyBundling: this[_legacyBundling],
994998
strictPeerDeps: this[_strictPeerDeps],
999+
installLinks: this.installLinks,
9951000
legacyPeerDeps: this.legacyPeerDeps,
9961001
globalStyle: this[_globalStyle],
9971002
}))
@@ -1151,6 +1156,7 @@ This is a one-time fix-up, please be patient...
11511156
const vr = new Node({
11521157
path: node.realpath,
11531158
sourceReference: node,
1159+
installLinks: this.installLinks,
11541160
legacyPeerDeps: this.legacyPeerDeps,
11551161
overrides: node.overrides,
11561162
})
@@ -1268,17 +1274,18 @@ This is a one-time fix-up, please be patient...
12681274
// the object so it doesn't get mutated.
12691275
// Don't bother to load the manifest for link deps, because the target
12701276
// might be within another package that doesn't exist yet.
1271-
const { legacyPeerDeps } = this
1277+
const { installLinks, legacyPeerDeps } = this
1278+
const isWorkspace = this.idealTree.workspaces && this.idealTree.workspaces.has(spec.name)
12721279

1273-
// spec is a directory, link it
1274-
if (spec.type === 'directory') {
1280+
// spec is a directory, link it unless installLinks is set or it's a workspace
1281+
if (spec.type === 'directory' && (isWorkspace || !installLinks)) {
12751282
return this[_linkFromSpec](name, spec, parent, edge)
12761283
}
12771284

12781285
// if the spec matches a workspace name, then see if the workspace node will
12791286
// satisfy the edge. if it does, we return the workspace node to make sure it
12801287
// takes priority.
1281-
if (this.idealTree.workspaces && this.idealTree.workspaces.has(spec.name)) {
1288+
if (isWorkspace) {
12821289
const existingNode = this.idealTree.edgesOut.get(spec.name).to
12831290
if (existingNode && existingNode.isWorkspace && existingNode.satisfies(edge)) {
12841291
return edge.to
@@ -1288,7 +1295,7 @@ This is a one-time fix-up, please be patient...
12881295
// spec isn't a directory, and either isn't a workspace or the workspace we have
12891296
// doesn't satisfy the edge. try to fetch a manifest and build a node from that.
12901297
return this[_fetchManifest](spec)
1291-
.then(pkg => new Node({ name, pkg, parent, legacyPeerDeps }), error => {
1298+
.then(pkg => new Node({ name, pkg, parent, installLinks, legacyPeerDeps }), error => {
12921299
error.requiredBy = edge.from.location || '.'
12931300

12941301
// failed to load the spec, either because of enotarget or
@@ -1298,6 +1305,7 @@ This is a one-time fix-up, please be patient...
12981305
name,
12991306
parent,
13001307
error,
1308+
installLinks,
13011309
legacyPeerDeps,
13021310
})
13031311
this[_loadFailures].add(n)
@@ -1307,9 +1315,9 @@ This is a one-time fix-up, please be patient...
13071315

13081316
[_linkFromSpec] (name, spec, parent, edge) {
13091317
const realpath = spec.fetchSpec
1310-
const { legacyPeerDeps } = this
1318+
const { installLinks, legacyPeerDeps } = this
13111319
return rpj(realpath + '/package.json').catch(() => ({})).then(pkg => {
1312-
const link = new Link({ name, parent, realpath, pkg, legacyPeerDeps })
1320+
const link = new Link({ name, parent, realpath, pkg, installLinks, legacyPeerDeps })
13131321
this[_linkNodes].add(link)
13141322
return link
13151323
})

workspaces/arborist/lib/arborist/load-actual.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ module.exports = cls => class ActualLoader extends cls {
283283
.then(pkg => [pkg, null], error => [null, error])
284284
.then(([pkg, error]) => {
285285
return this[normalize(path) === real ? _newNode : _newLink]({
286+
installLinks: this.installLinks,
286287
legacyPeerDeps: this.legacyPeerDeps,
287288
path,
288289
realpath: real,

workspaces/arborist/lib/arborist/load-virtual.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ module.exports = cls => class VirtualLoader extends cls {
278278
const peer = sw.peer
279279

280280
const node = new Node({
281+
installLinks: this.installLinks,
281282
legacyPeerDeps: this.legacyPeerDeps,
282283
root: this.virtualTree,
283284
path,
@@ -304,6 +305,7 @@ module.exports = cls => class VirtualLoader extends cls {
304305
[loadLink] (location, targetLoc, target, meta) {
305306
const path = resolve(this.path, location)
306307
const link = new Link({
308+
installLinks: this.installLinks,
307309
legacyPeerDeps: this.legacyPeerDeps,
308310
path,
309311
realpath: resolve(this.path, targetLoc),

workspaces/arborist/lib/dep-valid.js

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,7 @@ const depValid = (child, requested, requestor) => {
5353
return semver.satisfies(child.version, requested.fetchSpec, true)
5454

5555
case 'directory':
56-
// directory must be a link to the specified folder
57-
return !!child.isLink &&
58-
relative(child.realpath, requested.fetchSpec) === ''
56+
return linkValid(child, requested, requestor)
5957

6058
case 'file':
6159
return tarballValid(child, requested, requestor)
@@ -108,6 +106,18 @@ const depValid = (child, requested, requestor) => {
108106
return false
109107
}
110108

109+
const linkValid = (child, requested, requestor) => {
110+
const isLink = !!child.isLink
111+
// if we're installing links and the node is a link, then it's invalid because we want
112+
// a real node to be there
113+
if (requestor.installLinks) {
114+
return !isLink
115+
}
116+
117+
// directory must be a link to the specified folder
118+
return isLink && relative(child.realpath, requested.fetchSpec) === ''
119+
}
120+
111121
const tarballValid = (child, requested, requestor) => {
112122
if (child.isLink) {
113123
return false

workspaces/arborist/lib/node.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ class Node {
8686
name,
8787
children,
8888
fsChildren,
89+
installLinks = false,
8990
legacyPeerDeps = false,
9091
linksIn,
9192
hasShrinkwrap,
@@ -152,6 +153,7 @@ class Node {
152153
}
153154
this.integrity = integrity || pkg._integrity || null
154155
this.hasShrinkwrap = hasShrinkwrap || pkg._hasShrinkwrap || false
156+
this.installLinks = installLinks
155157
this.legacyPeerDeps = legacyPeerDeps
156158

157159
this.children = new CaseInsensitiveMap()

workspaces/arborist/lib/place-dep.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class PlaceDep {
4545
auditReport,
4646
legacyBundling,
4747
strictPeerDeps,
48+
installLinks,
4849
legacyPeerDeps,
4950
globalStyle,
5051
} = parent || options
@@ -56,6 +57,7 @@ class PlaceDep {
5657
auditReport,
5758
legacyBundling,
5859
strictPeerDeps,
60+
installLinks,
5961
legacyPeerDeps,
6062
globalStyle,
6163
})
@@ -293,6 +295,7 @@ class PlaceDep {
293295
pkg: dep.package,
294296
resolved: dep.resolved,
295297
integrity: dep.integrity,
298+
installLinks: this.installLinks,
296299
legacyPeerDeps: this.legacyPeerDeps,
297300
error: dep.errors[0],
298301
...(dep.overrides ? { overrides: dep.overrides } : {}),

workspaces/arborist/tap-snapshots/test/link.js.test.cjs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Link {
1616
"extraneous": true,
1717
"fsChildren": Set {},
1818
"hasShrinkwrap": false,
19+
"installLinks": false,
1920
"integrity": null,
2021
"inventory": Inventory {},
2122
"legacyPeerDeps": false,
@@ -42,6 +43,7 @@ exports[`test/link.js TAP > instantiate without providing target 1`] = `
4243
"extraneous": true,
4344
"fsChildren": Set {},
4445
"hasShrinkwrap": false,
46+
"installLinks": false,
4547
"integrity": null,
4648
"inventory": Inventory {
4749
"../../../../../some/other/path" => <*ref_1>,
@@ -56,6 +58,7 @@ exports[`test/link.js TAP > instantiate without providing target 1`] = `
5658
"extraneous": true,
5759
"fsChildren": Set {},
5860
"hasShrinkwrap": false,
61+
"installLinks": false,
5962
"integrity": null,
6063
"inventory": Inventory {},
6164
"legacyPeerDeps": false,
@@ -94,6 +97,7 @@ exports[`test/link.js TAP > instantiate without providing target 1`] = `
9497
"extraneous": true,
9598
"fsChildren": Set {},
9699
"hasShrinkwrap": false,
100+
"installLinks": false,
97101
"integrity": null,
98102
"inventory": Inventory {},
99103
"legacyPeerDeps": false,

0 commit comments

Comments
 (0)