cross-packer 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +151 -0
- package/README.zh-CN.md +151 -0
- package/dist/archive/ar.d.ts +12 -0
- package/dist/archive/seven-za.d.ts +32 -0
- package/dist/archive/tar.d.ts +30 -0
- package/dist/archive/zip-reader.d.ts +17 -0
- package/dist/asar/dependency/collect.d.ts +2 -0
- package/dist/asar/dependency/detector.d.ts +8 -0
- package/dist/asar/dependency/hoister.d.ts +27 -0
- package/dist/asar/dependency/utils.d.ts +59 -0
- package/dist/asar/fileset/matcher.d.ts +12 -0
- package/dist/asar/fileset/platform-fileset.d.ts +36 -0
- package/dist/asar/fileset/transformer.d.ts +2 -0
- package/dist/asar/fileset/unpack-detector.d.ts +10 -0
- package/dist/asar/fileset/walker.d.ts +14 -0
- package/dist/asar/pack.d.ts +23 -0
- package/dist/asar/packer/app-collector.d.ts +2 -0
- package/dist/asar/packer/collector.d.ts +18 -0
- package/dist/asar/packer/concurrency.d.ts +1 -0
- package/dist/asar/packer/integrity.d.ts +8 -0
- package/dist/asar/packer/nm-collector.d.ts +7 -0
- package/dist/asar/packer/stream-builder.d.ts +18 -0
- package/dist/cli.d.ts +2 -0
- package/dist/config/index.d.ts +97 -0
- package/dist/electron/dist-zip.d.ts +18 -0
- package/dist/electron/download.d.ts +21 -0
- package/dist/index.d.ts +11 -0
- package/dist/packer/common/app-update-yml.d.ts +5 -0
- package/dist/packer/common/extra-resources.d.ts +39 -0
- package/dist/packer/common/staging.d.ts +3 -0
- package/dist/packer/common/template.d.ts +2 -0
- package/dist/packer/linux/linux.d.ts +2 -0
- package/dist/packer/mac/mac.d.ts +23 -0
- package/dist/packer/win/nsis.d.ts +24 -0
- package/dist/packer/win/resedit.d.ts +5 -0
- package/dist/packer/win/toolchain.d.ts +8 -0
- package/dist/packer/win/win.d.ts +12 -0
- package/dist/pipeline/pipeline.d.ts +41 -0
- package/dist/pipeline/targets.d.ts +28 -0
- package/dist/shared/hash.d.ts +2 -0
- package/dist/shared/logger.d.ts +39 -0
- package/dist/shared/platform.d.ts +26 -0
- package/dist/shared/types.d.ts +68 -0
- package/dist/update/blockmap.d.ts +13 -0
- package/dist/update/metadata.d.ts +20 -0
- package/dist/update/version.d.ts +7 -0
- package/package.json +71 -0
- package/src/archive/ar.ts +41 -0
- package/src/archive/seven-za.ts +160 -0
- package/src/archive/tar.ts +142 -0
- package/src/archive/zip-reader.ts +61 -0
- package/src/asar/dependency/collect.ts +437 -0
- package/src/asar/dependency/detector.ts +73 -0
- package/src/asar/dependency/hoister.ts +714 -0
- package/src/asar/dependency/utils.ts +225 -0
- package/src/asar/fileset/matcher.ts +101 -0
- package/src/asar/fileset/platform-fileset.ts +416 -0
- package/src/asar/fileset/transformer.ts +154 -0
- package/src/asar/fileset/unpack-detector.ts +38 -0
- package/src/asar/fileset/walker.ts +115 -0
- package/src/asar/pack.ts +186 -0
- package/src/asar/packer/app-collector.ts +74 -0
- package/src/asar/packer/collector.ts +134 -0
- package/src/asar/packer/concurrency.ts +24 -0
- package/src/asar/packer/integrity.ts +53 -0
- package/src/asar/packer/nm-collector.ts +194 -0
- package/src/asar/packer/stream-builder.ts +723 -0
- package/src/cli.ts +182 -0
- package/src/config/index.ts +365 -0
- package/src/electron/dist-zip.ts +65 -0
- package/src/electron/download.ts +121 -0
- package/src/index.ts +14 -0
- package/src/packer/common/app-update-yml.ts +21 -0
- package/src/packer/common/extra-resources.ts +83 -0
- package/src/packer/common/staging.ts +33 -0
- package/src/packer/common/template.ts +24 -0
- package/src/packer/linux/linux.ts +329 -0
- package/src/packer/mac/mac.ts +495 -0
- package/src/packer/win/nsis.ts +244 -0
- package/src/packer/win/resedit.ts +60 -0
- package/src/packer/win/toolchain.ts +63 -0
- package/src/packer/win/win.ts +421 -0
- package/src/pipeline/pipeline.ts +149 -0
- package/src/pipeline/targets.ts +67 -0
- package/src/shared/hash.ts +15 -0
- package/src/shared/logger.ts +91 -0
- package/src/shared/platform.ts +41 -0
- package/src/shared/types.ts +82 -0
- package/src/update/blockmap.ts +247 -0
- package/src/update/metadata.ts +97 -0
- package/src/update/version.ts +59 -0
- package/templates/linux/app.desktop +11 -0
- package/templates/linux/control +9 -0
- package/templates/linux/postinst +10 -0
- package/templates/linux/postrm +8 -0
- package/templates/linux/preinst +49 -0
- package/templates/linux/prerm +43 -0
- package/templates/mac/Info.plist +18 -0
- package/templates/mac/app-update.yml +4 -0
- package/templates/win/nsis/README.md +51 -0
- package/templates/win/nsis/THIRD-PARTY-NOTICES.md +20 -0
- package/templates/win/nsis/assistedInstaller.nsh +154 -0
- package/templates/win/nsis/assistedMessages.yml +368 -0
- package/templates/win/nsis/common.nsh +147 -0
- package/templates/win/nsis/empty-license.txt +1 -0
- package/templates/win/nsis/include/FileAssociation.nsh +123 -0
- package/templates/win/nsis/include/StdUtils.nsh +496 -0
- package/templates/win/nsis/include/StrContains.nsh +48 -0
- package/templates/win/nsis/include/UAC.nsh +299 -0
- package/templates/win/nsis/include/allowOnlyOneInstallerInstance.nsh +164 -0
- package/templates/win/nsis/include/extractAppPackage.nsh +138 -0
- package/templates/win/nsis/include/getProcessInfo.nsh +157 -0
- package/templates/win/nsis/include/installUtil.nsh +248 -0
- package/templates/win/nsis/include/installer.nsh +247 -0
- package/templates/win/nsis/include/nsProcess.nsh +28 -0
- package/templates/win/nsis/include/webPackage.nsh +64 -0
- package/templates/win/nsis/installSection.nsh +110 -0
- package/templates/win/nsis/installer.nsi +132 -0
- package/templates/win/nsis/messages.yml +240 -0
- package/templates/win/nsis/multiUser.nsh +134 -0
- package/templates/win/nsis/multiUserUi.nsh +230 -0
- package/templates/win/nsis/oneClick.nsh +19 -0
- package/templates/win/nsis/portable.nsi +91 -0
- package/templates/win/nsis/uninstaller.nsh +263 -0
- package/templates/win/nsis-header.nsh +57 -0
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
import { parseNameVersion } from './utils.ts'
|
|
2
|
+
|
|
3
|
+
const HoisterDependencyKind = { REGULAR: 0, WORKSPACE: 1, EXTERNAL_SOFT_LINK: 2 } as const
|
|
4
|
+
const Hoistable = { YES: 0, NO: 1, DEPENDS: 2 } as const
|
|
5
|
+
const DebugLevel = { NONE: -1, PERF: 0, CHECK: 1, REASONS: 2, INTENSIVE_CHECK: 9 } as const
|
|
6
|
+
|
|
7
|
+
/** Compose a locator string from name and reference. */
|
|
8
|
+
const makeLocator = (name: string, reference: string): string => `${name}@${reference}`
|
|
9
|
+
|
|
10
|
+
/** Compose an ident string, stripping any hash portion from the reference. */
|
|
11
|
+
const makeIdent = (name: string, reference: string): string => {
|
|
12
|
+
const hashIdx = reference.indexOf('#')
|
|
13
|
+
const realReference = hashIdx >= 0 ? reference.substring(hashIdx + 1) : reference
|
|
14
|
+
return makeLocator(name, realReference)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Input tree produced by transformToHoisterTree. */
|
|
18
|
+
export interface HoisterTreeNode {
|
|
19
|
+
name: string
|
|
20
|
+
identName: string
|
|
21
|
+
reference: string
|
|
22
|
+
peerNames: Set<string>
|
|
23
|
+
dependencies: Set<HoisterTreeNode>
|
|
24
|
+
hoistPriority?: number
|
|
25
|
+
dependencyKind?: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Mutable work node used during hoisting rounds. */
|
|
29
|
+
interface WorkNode {
|
|
30
|
+
name: string
|
|
31
|
+
references: Set<string>
|
|
32
|
+
ident: string
|
|
33
|
+
locator: string
|
|
34
|
+
dependencies: Map<string, WorkNode>
|
|
35
|
+
originalDependencies: Map<string, WorkNode>
|
|
36
|
+
hoistedDependencies: Map<string, WorkNode>
|
|
37
|
+
peerNames: Set<string>
|
|
38
|
+
reasons: Map<string, string | null>
|
|
39
|
+
decoupled: boolean
|
|
40
|
+
isHoistBorder: boolean
|
|
41
|
+
hoistPriority: number
|
|
42
|
+
dependencyKind: number
|
|
43
|
+
hoistedFrom: Map<string, Set<string>>
|
|
44
|
+
hoistedTo: Map<string, Set<string>>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Immutable shrunk tree returned by hoist. */
|
|
48
|
+
export interface ShrunkNode {
|
|
49
|
+
name: string
|
|
50
|
+
identName: string
|
|
51
|
+
references: Set<string>
|
|
52
|
+
dependencies: Set<ShrunkNode>
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface PreferenceEntry {
|
|
56
|
+
dependents: Set<string>
|
|
57
|
+
peerDependents: Set<string>
|
|
58
|
+
hoistPriority: number
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface HoistInfo {
|
|
62
|
+
isHoistable: (typeof Hoistable)[keyof typeof Hoistable]
|
|
63
|
+
dependsOn?: Set<WorkNode> | null
|
|
64
|
+
reason: string | null
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface HoistOptions {
|
|
68
|
+
debugLevel?: number
|
|
69
|
+
check?: boolean
|
|
70
|
+
hoistingLimits?: Map<string, Set<string>>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
interface HoistToOptions {
|
|
74
|
+
check: boolean
|
|
75
|
+
debugLevel: number
|
|
76
|
+
hoistingLimits: Map<string, Set<string>>
|
|
77
|
+
fastLookupPossible: boolean
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
interface RoundResult {
|
|
81
|
+
anotherRoundNeeded: boolean
|
|
82
|
+
isGraphChanged: boolean
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function hoist(tree: HoisterTreeNode, opts: HoistOptions = {}): ShrunkNode {
|
|
86
|
+
const debugLevel = opts.debugLevel || DebugLevel.NONE
|
|
87
|
+
const check = opts.check || debugLevel >= DebugLevel.INTENSIVE_CHECK
|
|
88
|
+
const hoistingLimits = opts.hoistingLimits || new Map<string, Set<string>>()
|
|
89
|
+
const options: HoistToOptions = { check, debugLevel, hoistingLimits, fastLookupPossible: true }
|
|
90
|
+
|
|
91
|
+
const treeCopy = cloneTree(tree, options)
|
|
92
|
+
let anotherRoundNeeded = false
|
|
93
|
+
do {
|
|
94
|
+
const result = hoistTo(treeCopy, [treeCopy], new Set([treeCopy.locator]), new Map(), options)
|
|
95
|
+
anotherRoundNeeded = result.anotherRoundNeeded || result.isGraphChanged
|
|
96
|
+
options.fastLookupPossible = false
|
|
97
|
+
} while (anotherRoundNeeded)
|
|
98
|
+
|
|
99
|
+
return shrinkTree(treeCopy)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function transformToHoisterTree(
|
|
103
|
+
productionGraph: Record<string, { dependencies?: string[] }>,
|
|
104
|
+
key: string,
|
|
105
|
+
nodes: Map<string, HoisterTreeNode> = new Map(),
|
|
106
|
+
): HoisterTreeNode {
|
|
107
|
+
let node = nodes.get(key)
|
|
108
|
+
const { name, version } = parseNameVersion(key)
|
|
109
|
+
if (!node) {
|
|
110
|
+
node = {
|
|
111
|
+
name,
|
|
112
|
+
identName: name,
|
|
113
|
+
reference: version,
|
|
114
|
+
peerNames: new Set(),
|
|
115
|
+
dependencies: new Set(),
|
|
116
|
+
}
|
|
117
|
+
nodes.set(key, node)
|
|
118
|
+
const deps = productionGraph[key]?.dependencies || []
|
|
119
|
+
for (const dep of deps) {
|
|
120
|
+
const child = transformToHoisterTree(productionGraph, dep, nodes)
|
|
121
|
+
node.dependencies.add(child)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return node
|
|
125
|
+
}
|
|
126
|
+
/** Collect dependencies used before the first hoisting round. */
|
|
127
|
+
const getZeroRoundUsedDependencies = (rootNodePath: WorkNode[]): Map<string, WorkNode> => {
|
|
128
|
+
const rootNode = rootNodePath[rootNodePath.length - 1]
|
|
129
|
+
const usedDependencies = new Map<string, WorkNode>()
|
|
130
|
+
const seenNodes = new Set<WorkNode>()
|
|
131
|
+
// Collect the zero-round reachable dependencies of the root node.
|
|
132
|
+
const addUsedDependencies = (node: WorkNode): void => {
|
|
133
|
+
if (seenNodes.has(node)) return
|
|
134
|
+
seenNodes.add(node)
|
|
135
|
+
for (const dep of node.hoistedDependencies.values()) usedDependencies.set(dep.name, dep)
|
|
136
|
+
for (const dep of node.dependencies.values()) {
|
|
137
|
+
if (!node.peerNames.has(dep.name)) addUsedDependencies(dep)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
addUsedDependencies(rootNode)
|
|
141
|
+
return usedDependencies
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Collect the dependencies reachable from the root node path. */
|
|
145
|
+
const getUsedDependencies = (rootNodePath: WorkNode[]): Map<string, WorkNode> => {
|
|
146
|
+
const rootNode = rootNodePath[rootNodePath.length - 1]
|
|
147
|
+
const usedDependencies = new Map<string, WorkNode>()
|
|
148
|
+
const seenNodes = new Set<WorkNode>()
|
|
149
|
+
// Collect reachable dependencies, skipping names in hiddenDependencies.
|
|
150
|
+
const addUsedDependencies = (node: WorkNode, hiddenDependencies: Set<string>): void => {
|
|
151
|
+
if (seenNodes.has(node)) return
|
|
152
|
+
seenNodes.add(node)
|
|
153
|
+
for (const dep of node.hoistedDependencies.values()) {
|
|
154
|
+
if (!hiddenDependencies.has(dep.name)) {
|
|
155
|
+
for (const pathNode of rootNodePath) {
|
|
156
|
+
const reachableDependency = pathNode.dependencies.get(dep.name)
|
|
157
|
+
if (reachableDependency)
|
|
158
|
+
usedDependencies.set(reachableDependency.name, reachableDependency)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const childrenHiddenDependencies = new Set<string>()
|
|
163
|
+
for (const dep of node.dependencies.values()) childrenHiddenDependencies.add(dep.name)
|
|
164
|
+
for (const dep of node.dependencies.values()) {
|
|
165
|
+
if (!node.peerNames.has(dep.name)) addUsedDependencies(dep, childrenHiddenDependencies)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
addUsedDependencies(rootNode, new Set())
|
|
169
|
+
return usedDependencies
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** Clone a graph node so it can be re-parented without side effects. */
|
|
173
|
+
const decoupleGraphNode = (parent: WorkNode, node: WorkNode): WorkNode => {
|
|
174
|
+
if (node.decoupled) return node
|
|
175
|
+
const clone: WorkNode = {
|
|
176
|
+
name: node.name,
|
|
177
|
+
references: new Set(node.references),
|
|
178
|
+
ident: node.ident,
|
|
179
|
+
locator: node.locator,
|
|
180
|
+
dependencies: new Map(node.dependencies),
|
|
181
|
+
originalDependencies: new Map(node.originalDependencies),
|
|
182
|
+
hoistedDependencies: new Map(node.hoistedDependencies),
|
|
183
|
+
peerNames: new Set(node.peerNames),
|
|
184
|
+
reasons: new Map(node.reasons),
|
|
185
|
+
decoupled: true,
|
|
186
|
+
isHoistBorder: node.isHoistBorder,
|
|
187
|
+
hoistPriority: node.hoistPriority,
|
|
188
|
+
dependencyKind: node.dependencyKind,
|
|
189
|
+
hoistedFrom: new Map(node.hoistedFrom),
|
|
190
|
+
hoistedTo: new Map(node.hoistedTo),
|
|
191
|
+
}
|
|
192
|
+
const selfDep = clone.dependencies.get(node.name)
|
|
193
|
+
if (selfDep && selfDep.ident === clone.ident) clone.dependencies.set(node.name, clone)
|
|
194
|
+
parent.dependencies.set(clone.name, clone)
|
|
195
|
+
return clone
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Map each dependency name to its list of acceptable idents, ordered by hoist priority. */
|
|
199
|
+
const getHoistIdentMap = (
|
|
200
|
+
rootNode: WorkNode,
|
|
201
|
+
preferenceMap: Map<string, PreferenceEntry>,
|
|
202
|
+
): Map<string, string[]> => {
|
|
203
|
+
const identMap = new Map<string, string[]>([[rootNode.name, [rootNode.ident]]])
|
|
204
|
+
for (const dep of rootNode.dependencies.values()) {
|
|
205
|
+
if (!rootNode.peerNames.has(dep.name)) identMap.set(dep.name, [dep.ident])
|
|
206
|
+
}
|
|
207
|
+
const keyList = Array.from(preferenceMap.keys())
|
|
208
|
+
keyList.sort((key1, key2) => {
|
|
209
|
+
const entry1 = preferenceMap.get(key1)
|
|
210
|
+
const entry2 = preferenceMap.get(key2)
|
|
211
|
+
if (!entry1 || !entry2) return 0
|
|
212
|
+
if (entry2.hoistPriority !== entry1.hoistPriority)
|
|
213
|
+
return entry2.hoistPriority - entry1.hoistPriority
|
|
214
|
+
const entry1Usages = entry1.dependents.size + entry1.peerDependents.size
|
|
215
|
+
const entry2Usages = entry2.dependents.size + entry2.peerDependents.size
|
|
216
|
+
return entry2Usages - entry1Usages
|
|
217
|
+
})
|
|
218
|
+
for (const key of keyList) {
|
|
219
|
+
const name = key.substring(0, key.indexOf('@', 1))
|
|
220
|
+
const ident = key.substring(name.length + 1)
|
|
221
|
+
if (!rootNode.peerNames.has(name)) {
|
|
222
|
+
let idents = identMap.get(name)
|
|
223
|
+
if (!idents) {
|
|
224
|
+
idents = []
|
|
225
|
+
identMap.set(name, idents)
|
|
226
|
+
}
|
|
227
|
+
if (idents.indexOf(ident) < 0) idents.push(ident)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return identMap
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Sort the regular (non-peer) dependencies of a node into a Set. */
|
|
234
|
+
const getSortedRegularDependencies = (node: WorkNode): Set<WorkNode> => {
|
|
235
|
+
const dependencies = new Set<WorkNode>()
|
|
236
|
+
// Add a dependency and its transitive non-shadowed peers.
|
|
237
|
+
const addDep = (dep: WorkNode, seenDeps: Set<WorkNode> = new Set()): void => {
|
|
238
|
+
if (seenDeps.has(dep)) return
|
|
239
|
+
seenDeps.add(dep)
|
|
240
|
+
for (const peerName of dep.peerNames) {
|
|
241
|
+
if (!node.peerNames.has(peerName)) {
|
|
242
|
+
const peerDep = node.dependencies.get(peerName)
|
|
243
|
+
if (peerDep && !dependencies.has(peerDep)) addDep(peerDep, seenDeps)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
dependencies.add(dep)
|
|
247
|
+
}
|
|
248
|
+
for (const dep of node.dependencies.values()) {
|
|
249
|
+
if (!node.peerNames.has(dep.name)) addDep(dep)
|
|
250
|
+
}
|
|
251
|
+
return dependencies
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const hoistTo = (
|
|
255
|
+
tree: WorkNode,
|
|
256
|
+
rootNodePath: WorkNode[],
|
|
257
|
+
rootNodePathLocators: Set<string>,
|
|
258
|
+
parentShadowedNodes: Map<WorkNode, Set<string>>,
|
|
259
|
+
options: HoistToOptions,
|
|
260
|
+
seenNodes: Set<WorkNode> = new Set(),
|
|
261
|
+
): RoundResult => {
|
|
262
|
+
const rootNode = rootNodePath[rootNodePath.length - 1]
|
|
263
|
+
if (seenNodes.has(rootNode)) return { anotherRoundNeeded: false, isGraphChanged: false }
|
|
264
|
+
seenNodes.add(rootNode)
|
|
265
|
+
const preferenceMap = buildPreferenceMap(rootNode)
|
|
266
|
+
const hoistIdentMap = getHoistIdentMap(rootNode, preferenceMap)
|
|
267
|
+
const usedDependencies =
|
|
268
|
+
tree === rootNode
|
|
269
|
+
? new Map<string, WorkNode>()
|
|
270
|
+
: options.fastLookupPossible
|
|
271
|
+
? getZeroRoundUsedDependencies(rootNodePath)
|
|
272
|
+
: getUsedDependencies(rootNodePath)
|
|
273
|
+
let wasStateChanged: boolean
|
|
274
|
+
let anotherRoundNeeded = false
|
|
275
|
+
let isGraphChanged = false
|
|
276
|
+
const hoistIdents = new Map(
|
|
277
|
+
Array.from(hoistIdentMap.entries()).map(([k, v]) => [k, v[0]] as const),
|
|
278
|
+
)
|
|
279
|
+
const shadowedNodes = new Map<WorkNode, Set<string>>()
|
|
280
|
+
do {
|
|
281
|
+
const result = hoistGraph(
|
|
282
|
+
tree,
|
|
283
|
+
rootNodePath,
|
|
284
|
+
rootNodePathLocators,
|
|
285
|
+
usedDependencies,
|
|
286
|
+
hoistIdents,
|
|
287
|
+
hoistIdentMap,
|
|
288
|
+
parentShadowedNodes,
|
|
289
|
+
shadowedNodes,
|
|
290
|
+
options,
|
|
291
|
+
)
|
|
292
|
+
if (result.isGraphChanged) isGraphChanged = true
|
|
293
|
+
if (result.anotherRoundNeeded) anotherRoundNeeded = true
|
|
294
|
+
wasStateChanged = false
|
|
295
|
+
for (const [name, idents] of hoistIdentMap) {
|
|
296
|
+
if (idents.length > 1 && !rootNode.dependencies.has(name)) {
|
|
297
|
+
hoistIdents.delete(name)
|
|
298
|
+
idents.shift()
|
|
299
|
+
hoistIdents.set(name, idents[0])
|
|
300
|
+
wasStateChanged = true
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} while (wasStateChanged)
|
|
304
|
+
for (const dependency of rootNode.dependencies.values()) {
|
|
305
|
+
if (!rootNode.peerNames.has(dependency.name) && !rootNodePathLocators.has(dependency.locator)) {
|
|
306
|
+
rootNodePathLocators.add(dependency.locator)
|
|
307
|
+
const result = hoistTo(
|
|
308
|
+
tree,
|
|
309
|
+
[...rootNodePath, dependency],
|
|
310
|
+
rootNodePathLocators,
|
|
311
|
+
shadowedNodes,
|
|
312
|
+
options,
|
|
313
|
+
)
|
|
314
|
+
if (result.isGraphChanged) isGraphChanged = true
|
|
315
|
+
if (result.anotherRoundNeeded) anotherRoundNeeded = true
|
|
316
|
+
rootNodePathLocators.delete(dependency.locator)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return { anotherRoundNeeded, isGraphChanged }
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/** Check whether a node still has unhoisted non-peer dependencies. */
|
|
323
|
+
const hasUnhoistedDependencies = (node: WorkNode): boolean => {
|
|
324
|
+
for (const [subName, subDependency] of node.dependencies) {
|
|
325
|
+
if (!node.peerNames.has(subName) && subDependency.ident !== node.ident) return true
|
|
326
|
+
}
|
|
327
|
+
return false
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const getNodeHoistInfo = (
|
|
331
|
+
rootNode: WorkNode,
|
|
332
|
+
_rootNodePathLocators: Set<string>,
|
|
333
|
+
nodePath: WorkNode[],
|
|
334
|
+
node: WorkNode,
|
|
335
|
+
usedDependencies: Map<string, WorkNode>,
|
|
336
|
+
hoistIdents: Map<string, string>,
|
|
337
|
+
_hoistIdentMap: Map<string, string[]>,
|
|
338
|
+
shadowedNodes: Map<WorkNode, Set<string>>,
|
|
339
|
+
{ fastLookupPossible }: { fastLookupPossible: boolean },
|
|
340
|
+
): HoistInfo => {
|
|
341
|
+
const reason: string | null = null
|
|
342
|
+
let dependsOn: Set<WorkNode> | null = new Set()
|
|
343
|
+
const parentNode = nodePath[nodePath.length - 1]
|
|
344
|
+
const isSelfReference = node.ident === parentNode.ident
|
|
345
|
+
let isHoistable = !isSelfReference
|
|
346
|
+
if (isHoistable) isHoistable = node.dependencyKind !== HoisterDependencyKind.WORKSPACE
|
|
347
|
+
if (isHoistable && node.dependencyKind === HoisterDependencyKind.EXTERNAL_SOFT_LINK) {
|
|
348
|
+
isHoistable = !hasUnhoistedDependencies(node)
|
|
349
|
+
}
|
|
350
|
+
if (isHoistable) isHoistable = !rootNode.peerNames.has(node.name)
|
|
351
|
+
if (isHoistable) {
|
|
352
|
+
const usedDep = usedDependencies.get(node.name)
|
|
353
|
+
const isNameAvailable = !usedDep || usedDep.ident === node.ident
|
|
354
|
+
isHoistable = isNameAvailable
|
|
355
|
+
}
|
|
356
|
+
if (isHoistable && isNameShadowed(node, nodePath, shadowedNodes)) {
|
|
357
|
+
isHoistable = false
|
|
358
|
+
}
|
|
359
|
+
if (isHoistable) {
|
|
360
|
+
const hoistedIdent = hoistIdents.get(node.name)
|
|
361
|
+
isHoistable = hoistedIdent === node.ident
|
|
362
|
+
}
|
|
363
|
+
if (isHoistable) {
|
|
364
|
+
const peerCheck = checkPeerDeps(node, rootNode, nodePath)
|
|
365
|
+
dependsOn = peerCheck.dependsOn
|
|
366
|
+
isHoistable = peerCheck.arePeerDepsSatisfied
|
|
367
|
+
}
|
|
368
|
+
if (isHoistable && !fastLookupPossible) {
|
|
369
|
+
for (const origDep of node.hoistedDependencies.values()) {
|
|
370
|
+
const usedDep = usedDependencies.get(origDep.name) || rootNode.dependencies.get(origDep.name)
|
|
371
|
+
if (!usedDep || origDep.ident !== usedDep.ident) {
|
|
372
|
+
isHoistable = false
|
|
373
|
+
break
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (dependsOn !== null && dependsOn.size > 0)
|
|
378
|
+
return { isHoistable: Hoistable.DEPENDS, dependsOn, reason }
|
|
379
|
+
return { isHoistable: isHoistable ? Hoistable.YES : Hoistable.NO, reason }
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Compose the alias locator for a node (name@locator). */
|
|
383
|
+
const getAliasedLocator = (node: WorkNode): string => `${node.name}@${node.locator}`
|
|
384
|
+
|
|
385
|
+
const hoistGraph = (
|
|
386
|
+
_tree: WorkNode,
|
|
387
|
+
rootNodePath: WorkNode[],
|
|
388
|
+
rootNodePathLocators: Set<string>,
|
|
389
|
+
usedDependencies: Map<string, WorkNode>,
|
|
390
|
+
hoistIdents: Map<string, string>,
|
|
391
|
+
hoistIdentMap: Map<string, string[]>,
|
|
392
|
+
parentShadowedNodes: Map<WorkNode, Set<string>>,
|
|
393
|
+
shadowedNodes: Map<WorkNode, Set<string>>,
|
|
394
|
+
options: HoistToOptions,
|
|
395
|
+
): RoundResult => {
|
|
396
|
+
const rootNode = rootNodePath[rootNodePath.length - 1]
|
|
397
|
+
const seenNodes = new Set<WorkNode>()
|
|
398
|
+
let anotherRoundNeeded = false
|
|
399
|
+
let isGraphChanged = false
|
|
400
|
+
const hoistNodeDependencies = (
|
|
401
|
+
nodePath: WorkNode[],
|
|
402
|
+
locatorPath: string[],
|
|
403
|
+
aliasedLocatorPath: string[],
|
|
404
|
+
parentNode: WorkNode,
|
|
405
|
+
newNodes: Set<WorkNode>,
|
|
406
|
+
): void => {
|
|
407
|
+
if (seenNodes.has(parentNode)) return
|
|
408
|
+
const nextLocatorPath = [...locatorPath, getAliasedLocator(parentNode)]
|
|
409
|
+
const nextAliasedLocatorPath = [...aliasedLocatorPath, getAliasedLocator(parentNode)]
|
|
410
|
+
const dependantTree = new Map<string, Set<string>>()
|
|
411
|
+
const hoistInfos = new Map<WorkNode, HoistInfo>()
|
|
412
|
+
for (const subDependency of getSortedRegularDependencies(parentNode)) {
|
|
413
|
+
const hoistInfo = getNodeHoistInfo(
|
|
414
|
+
rootNode,
|
|
415
|
+
rootNodePathLocators,
|
|
416
|
+
[rootNode, ...nodePath, parentNode],
|
|
417
|
+
subDependency,
|
|
418
|
+
usedDependencies,
|
|
419
|
+
hoistIdents,
|
|
420
|
+
hoistIdentMap,
|
|
421
|
+
shadowedNodes,
|
|
422
|
+
{ fastLookupPossible: options.fastLookupPossible },
|
|
423
|
+
)
|
|
424
|
+
hoistInfos.set(subDependency, hoistInfo)
|
|
425
|
+
if (hoistInfo.isHoistable === Hoistable.DEPENDS) {
|
|
426
|
+
for (const node of hoistInfo.dependsOn || []) {
|
|
427
|
+
const nodeDependants = dependantTree.get(node.name) || new Set<string>()
|
|
428
|
+
nodeDependants.add(subDependency.name)
|
|
429
|
+
dependantTree.set(node.name, nodeDependants)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
const unhoistableNodes = new Set<WorkNode>()
|
|
434
|
+
// Mark a node unhoistable and cascade to its dependants.
|
|
435
|
+
const addUnhoistableNode = (node: WorkNode | undefined, hoistInfo: HoistInfo): void => {
|
|
436
|
+
if (!node || unhoistableNodes.has(node)) return
|
|
437
|
+
unhoistableNodes.add(node)
|
|
438
|
+
hoistInfos.set(node, { isHoistable: Hoistable.NO, reason: hoistInfo.reason })
|
|
439
|
+
for (const dependantName of dependantTree.get(node.name) || []) {
|
|
440
|
+
addUnhoistableNode(parentNode.dependencies.get(dependantName), hoistInfo)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
for (const [node, hoistInfo] of hoistInfos)
|
|
444
|
+
if (hoistInfo.isHoistable === Hoistable.NO) addUnhoistableNode(node, hoistInfo)
|
|
445
|
+
let wereNodesHoisted = false
|
|
446
|
+
for (const node of hoistInfos.keys()) {
|
|
447
|
+
if (!unhoistableNodes.has(node)) {
|
|
448
|
+
isGraphChanged = true
|
|
449
|
+
const shadowedNames = parentShadowedNodes.get(parentNode)
|
|
450
|
+
if (shadowedNames?.has(node.name)) anotherRoundNeeded = true
|
|
451
|
+
wereNodesHoisted = true
|
|
452
|
+
parentNode.dependencies.delete(node.name)
|
|
453
|
+
parentNode.hoistedDependencies.set(node.name, node)
|
|
454
|
+
parentNode.reasons.delete(node.name)
|
|
455
|
+
const hoistedNode = rootNode.dependencies.get(node.name)
|
|
456
|
+
if (!hoistedNode) {
|
|
457
|
+
if (rootNode.ident !== node.ident) {
|
|
458
|
+
rootNode.dependencies.set(node.name, node)
|
|
459
|
+
newNodes.add(node)
|
|
460
|
+
}
|
|
461
|
+
} else {
|
|
462
|
+
for (const reference of node.references) hoistedNode.references.add(reference)
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (parentNode.dependencyKind === HoisterDependencyKind.EXTERNAL_SOFT_LINK && wereNodesHoisted)
|
|
467
|
+
anotherRoundNeeded = true
|
|
468
|
+
const children = getSortedRegularDependencies(parentNode)
|
|
469
|
+
for (const node of children) {
|
|
470
|
+
if (unhoistableNodes.has(node)) {
|
|
471
|
+
const hoistInfo = hoistInfos.get(node)
|
|
472
|
+
if (!hoistInfo) continue
|
|
473
|
+
const hoistableIdent = hoistIdents.get(node.name)
|
|
474
|
+
if (
|
|
475
|
+
(hoistableIdent === node.ident || !parentNode.reasons.has(node.name)) &&
|
|
476
|
+
hoistInfo.isHoistable !== Hoistable.YES
|
|
477
|
+
)
|
|
478
|
+
parentNode.reasons.set(node.name, hoistInfo.reason)
|
|
479
|
+
if (!node.isHoistBorder && nextAliasedLocatorPath.indexOf(getAliasedLocator(node)) < 0) {
|
|
480
|
+
seenNodes.add(parentNode)
|
|
481
|
+
const decoupledNode = decoupleGraphNode(parentNode, node)
|
|
482
|
+
hoistNodeDependencies(
|
|
483
|
+
[...nodePath, parentNode],
|
|
484
|
+
nextLocatorPath,
|
|
485
|
+
nextAliasedLocatorPath,
|
|
486
|
+
decoupledNode,
|
|
487
|
+
nextNewNodes,
|
|
488
|
+
)
|
|
489
|
+
seenNodes.delete(parentNode)
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
let newNodes: Set<WorkNode>
|
|
495
|
+
let nextNewNodes = new Set<WorkNode>(getSortedRegularDependencies(rootNode))
|
|
496
|
+
const aliasedRootNodePathLocators = Array.from(rootNodePath).map((x) => getAliasedLocator(x))
|
|
497
|
+
do {
|
|
498
|
+
newNodes = nextNewNodes
|
|
499
|
+
nextNewNodes = new Set<WorkNode>()
|
|
500
|
+
for (const dep of newNodes) {
|
|
501
|
+
if (dep.locator === rootNode.locator || dep.isHoistBorder) continue
|
|
502
|
+
const decoupledDependency = decoupleGraphNode(rootNode, dep)
|
|
503
|
+
hoistNodeDependencies(
|
|
504
|
+
[],
|
|
505
|
+
Array.from(rootNodePathLocators),
|
|
506
|
+
aliasedRootNodePathLocators,
|
|
507
|
+
decoupledDependency,
|
|
508
|
+
nextNewNodes,
|
|
509
|
+
)
|
|
510
|
+
}
|
|
511
|
+
} while (nextNewNodes.size > 0)
|
|
512
|
+
return { anotherRoundNeeded, isGraphChanged }
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const cloneTree = (tree: HoisterTreeNode, options: HoistToOptions): WorkNode => {
|
|
516
|
+
const treeCopy: WorkNode = {
|
|
517
|
+
name: tree.name,
|
|
518
|
+
references: new Set([tree.reference]),
|
|
519
|
+
locator: makeLocator(tree.identName, tree.reference),
|
|
520
|
+
ident: makeIdent(tree.identName, tree.reference),
|
|
521
|
+
dependencies: new Map(),
|
|
522
|
+
originalDependencies: new Map(),
|
|
523
|
+
hoistedDependencies: new Map(),
|
|
524
|
+
peerNames: new Set(tree.peerNames),
|
|
525
|
+
reasons: new Map(),
|
|
526
|
+
decoupled: true,
|
|
527
|
+
isHoistBorder: true,
|
|
528
|
+
hoistPriority: 0,
|
|
529
|
+
dependencyKind: HoisterDependencyKind.WORKSPACE,
|
|
530
|
+
hoistedFrom: new Map(),
|
|
531
|
+
hoistedTo: new Map(),
|
|
532
|
+
}
|
|
533
|
+
const seenNodes = new Map<HoisterTreeNode, WorkNode>([[tree, treeCopy]])
|
|
534
|
+
// Recursively copy a node and its dependencies into work nodes.
|
|
535
|
+
const addNode = (node: HoisterTreeNode, parentNode: WorkNode): void => {
|
|
536
|
+
const existing = seenNodes.get(node)
|
|
537
|
+
const isSeen = existing !== undefined
|
|
538
|
+
let workNode: WorkNode
|
|
539
|
+
if (!isSeen) {
|
|
540
|
+
const dependenciesNmHoistingLimits = options.hoistingLimits.get(parentNode.locator)
|
|
541
|
+
workNode = {
|
|
542
|
+
name: node.name,
|
|
543
|
+
references: new Set([node.reference]),
|
|
544
|
+
locator: makeLocator(node.identName, node.reference),
|
|
545
|
+
ident: makeIdent(node.identName, node.reference),
|
|
546
|
+
dependencies: new Map(),
|
|
547
|
+
originalDependencies: new Map(),
|
|
548
|
+
hoistedDependencies: new Map(),
|
|
549
|
+
peerNames: new Set(node.peerNames),
|
|
550
|
+
reasons: new Map(),
|
|
551
|
+
decoupled: true,
|
|
552
|
+
isHoistBorder: dependenciesNmHoistingLimits
|
|
553
|
+
? dependenciesNmHoistingLimits.has(node.name)
|
|
554
|
+
: false,
|
|
555
|
+
hoistPriority: node.hoistPriority || 0,
|
|
556
|
+
dependencyKind: node.dependencyKind || HoisterDependencyKind.REGULAR,
|
|
557
|
+
hoistedFrom: new Map(),
|
|
558
|
+
hoistedTo: new Map(),
|
|
559
|
+
}
|
|
560
|
+
seenNodes.set(node, workNode)
|
|
561
|
+
} else {
|
|
562
|
+
workNode = existing
|
|
563
|
+
}
|
|
564
|
+
parentNode.dependencies.set(node.name, workNode)
|
|
565
|
+
parentNode.originalDependencies.set(node.name, workNode)
|
|
566
|
+
if (!isSeen) {
|
|
567
|
+
for (const dep of node.dependencies) addNode(dep, workNode)
|
|
568
|
+
} else {
|
|
569
|
+
const seenCoupledNodes = new Set<WorkNode>()
|
|
570
|
+
// Mark a work node and its dependencies as coupled (not decoupled).
|
|
571
|
+
const markNodeCoupled = (node: WorkNode): void => {
|
|
572
|
+
if (seenCoupledNodes.has(node)) return
|
|
573
|
+
seenCoupledNodes.add(node)
|
|
574
|
+
node.decoupled = false
|
|
575
|
+
for (const dep of node.dependencies.values()) {
|
|
576
|
+
if (!node.peerNames.has(dep.name)) markNodeCoupled(dep)
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
markNodeCoupled(workNode)
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
for (const dep of tree.dependencies) addNode(dep, treeCopy)
|
|
583
|
+
return treeCopy
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/** Extract the ident name from a locator string. */
|
|
587
|
+
const getIdentName = (locator: string): string => locator.substring(0, locator.indexOf('@', 1))
|
|
588
|
+
|
|
589
|
+
/** Build a shrunk copy of the dependency tree for hoisting. */
|
|
590
|
+
const shrinkTree = (tree: WorkNode): ShrunkNode => {
|
|
591
|
+
const treeCopy: ShrunkNode = {
|
|
592
|
+
name: tree.name,
|
|
593
|
+
identName: getIdentName(tree.locator),
|
|
594
|
+
references: new Set(tree.references),
|
|
595
|
+
dependencies: new Set(),
|
|
596
|
+
}
|
|
597
|
+
const seenNodes = new Set<WorkNode>([tree])
|
|
598
|
+
// Recursively add a node to the shrunk tree, reusing parent on self-reference.
|
|
599
|
+
const addNode = (node: WorkNode, parentWorkNode: WorkNode, parentNode: ShrunkNode): void => {
|
|
600
|
+
const isSeen = seenNodes.has(node)
|
|
601
|
+
let resultNode: ShrunkNode
|
|
602
|
+
if (parentWorkNode === node) {
|
|
603
|
+
resultNode = parentNode
|
|
604
|
+
} else {
|
|
605
|
+
resultNode = {
|
|
606
|
+
name: node.name,
|
|
607
|
+
identName: getIdentName(node.locator),
|
|
608
|
+
references: node.references,
|
|
609
|
+
dependencies: new Set(),
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
parentNode.dependencies.add(resultNode)
|
|
613
|
+
if (!isSeen) {
|
|
614
|
+
seenNodes.add(node)
|
|
615
|
+
for (const dep of node.dependencies.values()) {
|
|
616
|
+
if (!node.peerNames.has(dep.name)) addNode(dep, node, resultNode)
|
|
617
|
+
}
|
|
618
|
+
seenNodes.delete(node)
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
for (const dep of tree.dependencies.values()) addNode(dep, tree, treeCopy)
|
|
622
|
+
return treeCopy
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/** Build a preference map (dependents / peer dependents / hoist priority). */
|
|
626
|
+
const buildPreferenceMap = (rootNode: WorkNode): Map<string, PreferenceEntry> => {
|
|
627
|
+
const preferenceMap = new Map<string, PreferenceEntry>()
|
|
628
|
+
const seenNodes = new Set<WorkNode>([rootNode])
|
|
629
|
+
// Derive the preference map key for a node.
|
|
630
|
+
const getPreferenceKey = (node: WorkNode): string => `${node.name}@${node.ident}`
|
|
631
|
+
// Get or create the preference entry for a node.
|
|
632
|
+
const getOrCreatePreferenceEntry = (node: WorkNode): PreferenceEntry => {
|
|
633
|
+
const key = getPreferenceKey(node)
|
|
634
|
+
let entry = preferenceMap.get(key)
|
|
635
|
+
if (!entry) {
|
|
636
|
+
entry = { dependents: new Set(), peerDependents: new Set(), hoistPriority: 0 }
|
|
637
|
+
preferenceMap.set(key, entry)
|
|
638
|
+
}
|
|
639
|
+
return entry
|
|
640
|
+
}
|
|
641
|
+
// Register a dependent and recurse into the node's dependencies.
|
|
642
|
+
const addDependent = (dependent: WorkNode, node: WorkNode): void => {
|
|
643
|
+
const isSeen = !!seenNodes.has(node)
|
|
644
|
+
const entry = getOrCreatePreferenceEntry(node)
|
|
645
|
+
entry.dependents.add(dependent.ident)
|
|
646
|
+
if (!isSeen) {
|
|
647
|
+
seenNodes.add(node)
|
|
648
|
+
for (const dep of node.dependencies.values()) {
|
|
649
|
+
const depEntry = getOrCreatePreferenceEntry(dep)
|
|
650
|
+
depEntry.hoistPriority = Math.max(depEntry.hoistPriority, dep.hoistPriority)
|
|
651
|
+
if (node.peerNames.has(dep.name)) {
|
|
652
|
+
depEntry.peerDependents.add(node.ident)
|
|
653
|
+
} else {
|
|
654
|
+
addDependent(node, dep)
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
for (const dep of rootNode.dependencies.values())
|
|
660
|
+
if (!rootNode.peerNames.has(dep.name)) addDependent(rootNode, dep)
|
|
661
|
+
return preferenceMap
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/** Check whether the node name is shadowed by an ancestor's different-ident dependency. */
|
|
665
|
+
function isNameShadowed(
|
|
666
|
+
node: WorkNode,
|
|
667
|
+
nodePath: WorkNode[],
|
|
668
|
+
shadowedNodes: Map<WorkNode, Set<string>>,
|
|
669
|
+
): boolean {
|
|
670
|
+
for (let idx = nodePath.length - 1; idx >= 1; idx--) {
|
|
671
|
+
const parent = nodePath[idx]
|
|
672
|
+
const parentDep = parent.dependencies.get(node.name)
|
|
673
|
+
if (parentDep && parentDep.ident !== node.ident) {
|
|
674
|
+
const parentNode = nodePath[nodePath.length - 1]
|
|
675
|
+
let shadowedNames = shadowedNodes.get(parentNode)
|
|
676
|
+
if (!shadowedNames) {
|
|
677
|
+
shadowedNames = new Set<string>()
|
|
678
|
+
shadowedNodes.set(parentNode, shadowedNames)
|
|
679
|
+
}
|
|
680
|
+
shadowedNames.add(node.name)
|
|
681
|
+
return true
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return false
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/** Check peer-dependency satisfaction along the node path. */
|
|
688
|
+
function checkPeerDeps(
|
|
689
|
+
node: WorkNode,
|
|
690
|
+
rootNode: WorkNode,
|
|
691
|
+
nodePath: WorkNode[],
|
|
692
|
+
): { dependsOn: Set<WorkNode> | null; arePeerDepsSatisfied: boolean } {
|
|
693
|
+
let dependsOn: Set<WorkNode> | null = new Set()
|
|
694
|
+
let arePeerDepsSatisfied = true
|
|
695
|
+
const checkList = new Set(node.peerNames)
|
|
696
|
+
for (let idx = nodePath.length - 1; idx >= 1; idx--) {
|
|
697
|
+
const parent = nodePath[idx]
|
|
698
|
+
for (const name of checkList) {
|
|
699
|
+
if (parent.peerNames.has(name) && parent.originalDependencies.has(name)) continue
|
|
700
|
+
const parentDepNode = parent.dependencies.get(name)
|
|
701
|
+
if (parentDepNode && rootNode.dependencies.get(name) !== parentDepNode) {
|
|
702
|
+
if (idx === nodePath.length - 1) {
|
|
703
|
+
if (dependsOn) dependsOn.add(parentDepNode)
|
|
704
|
+
} else {
|
|
705
|
+
dependsOn = null
|
|
706
|
+
arePeerDepsSatisfied = false
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
checkList.delete(name)
|
|
710
|
+
}
|
|
711
|
+
if (!arePeerDepsSatisfied) break
|
|
712
|
+
}
|
|
713
|
+
return { dependsOn, arePeerDepsSatisfied }
|
|
714
|
+
}
|