1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
|
'use strict'
const BB = require('bluebird')
let addBundled
const childPath = require('../utils/child-path.js')
const createChild = require('./node.js').create
let fetchPackageMetadata
const inflateBundled = require('./inflate-bundled.js')
const moduleName = require('../utils/module-name.js')
const normalizePackageData = require('normalize-package-data')
const npm = require('../npm.js')
const realizeShrinkwrapSpecifier = require('./realize-shrinkwrap-specifier.js')
const validate = require('aproba')
const path = require('path')
module.exports = function (tree, swdeps, opts, finishInflating) {
if (!fetchPackageMetadata) {
fetchPackageMetadata = BB.promisify(require('../fetch-package-metadata.js'))
addBundled = BB.promisify(fetchPackageMetadata.addBundled)
}
if (!npm.config.get('shrinkwrap') || !npm.config.get('package-lock')) {
return finishInflating()
}
if (arguments.length === 3) {
finishInflating = opts
opts = {}
}
tree.loaded = true
return inflateShrinkwrap(tree.path, tree, swdeps, opts).then(
() => finishInflating(),
finishInflating
)
}
function inflateShrinkwrap (topPath, tree, swdeps, opts) {
validate('SOO|SOOO', arguments)
if (!opts) opts = {}
const onDisk = {}
tree.children.forEach((child) => {
onDisk[moduleName(child)] = child
})
const dev = npm.config.get('dev') || (!/^prod(uction)?$/.test(npm.config.get('only')) && !npm.config.get('production')) || /^dev(elopment)?$/.test(npm.config.get('only'))
const prod = !/^dev(elopment)?$/.test(npm.config.get('only'))
tree.children = []
return BB.each(Object.keys(swdeps), (name) => {
const sw = swdeps[name]
if (
(!prod && !sw.dev) ||
(!dev && sw.dev)
) { return null }
const dependencies = sw.dependencies || {}
const requested = realizeShrinkwrapSpecifier(name, sw, topPath)
return inflatableChild(
onDisk[name], name, topPath, tree, sw, requested, opts
).then((child) => {
return inflateShrinkwrap(topPath, child, dependencies)
})
})
}
function normalizePackageDataNoErrors (pkg) {
try {
normalizePackageData(pkg)
} catch (ex) {
// don't care
}
}
function inflatableChild (onDiskChild, name, topPath, tree, sw, requested, opts) {
validate('OSSOOOO|ZSSOOOO', arguments)
if (onDiskChild && childIsEquivalent(sw, requested, onDiskChild)) {
// The version on disk matches the shrinkwrap entry.
if (!onDiskChild.fromShrinkwrap) onDiskChild.fromShrinkwrap = true
if (sw.dev) onDiskChild.shrinkwrapDev = true
onDiskChild.package._requested = requested
onDiskChild.package._spec = requested.rawSpec
onDiskChild.package._where = topPath
onDiskChild.fromBundle = sw.bundled ? tree.fromBundle || tree : null
if (!onDiskChild.package._args) onDiskChild.package._args = []
onDiskChild.package._args.push([String(requested), topPath])
// non-npm registries can and will return unnormalized data, plus
// even the npm registry may have package data normalized with older
// normalization rules. This ensures we get package data in a consistent,
// stable format.
normalizePackageDataNoErrors(onDiskChild.package)
tree.children.push(onDiskChild)
return BB.resolve(onDiskChild)
} else if (opts.fakeChildren !== false && sw.version && sw.integrity) {
// The shrinkwrap entry has an integrity field. We can fake a pkg to get
// the installer to do a content-address fetch from the cache, if possible.
return BB.resolve(makeFakeChild(name, topPath, tree, sw, requested))
} else {
// It's not on disk, and we can't just look it up by address -- do a full
// fpm/inflate bundle pass. For registry deps, this will go straight to the
// tarball URL, as if it were a remote tarball dep.
return fetchChild(topPath, tree, sw, requested)
}
}
function makeFakeChild (name, topPath, tree, sw, requested) {
const from = sw.from || requested.raw
const pkg = {
name: name,
version: sw.version,
_resolved: adaptResolved(requested, sw.resolved),
_requested: requested,
_optional: sw.optional,
_integrity: sw.integrity,
_from: from,
_spec: requested.rawSpec,
_where: topPath,
_args: [[requested.toString(), topPath]]
}
let bundleAdded = BB.resolve()
if (Object.keys(sw.dependencies || {}).some((d) => {
return sw.dependencies[d].bundled
})) {
pkg.bundleDependencies = []
bundleAdded = addBundled(pkg)
}
return bundleAdded.then(() => {
const child = createChild({
package: pkg,
loaded: true,
parent: tree,
children: pkg._bundled || [],
fromShrinkwrap: true,
fakeChild: sw,
fromBundle: sw.bundled ? tree.fromBundle || tree : null,
path: childPath(tree.path, pkg),
realpath: childPath(tree.realpath, pkg),
location: tree.location + '/' + pkg.name,
isInLink: tree.isLink
})
tree.children.push(child)
if (pkg._bundled) {
delete pkg._bundled
inflateBundled(child, child, child.children)
}
return child
})
}
function adaptResolved (requested, resolved) {
const registry = requested.scope
? npm.config.get(`${requested.scope}:registry`) || npm.config.get('registry')
: npm.config.get('registry')
if (!requested.registry || (resolved && resolved.indexOf(registry) === 0)) {
// Nothing to worry about here. Pass it through.
return resolved
} else {
// We could fast-path for registry.npmjs.org here, but if we do, it
// would end up getting written back to the `resolved` field. By always
// returning `null` for other registries, `pacote.extract()` will take
// care of any required metadata fetches internally, without altering
// the tree we're going to write out to shrinkwrap/lockfile.
return null
}
}
function fetchChild (topPath, tree, sw, requested) {
const from = sw.from || requested.raw
const optional = sw.optional
return fetchPackageMetadata(requested, topPath).then((pkg) => {
pkg._from = from
pkg._optional = optional
return addBundled(pkg).then(() => pkg)
}).then((pkg) => {
var isLink = pkg._requested.type === 'directory'
const child = createChild({
package: pkg,
loaded: true,
parent: tree,
fromShrinkwrap: requested,
path: childPath(tree.path, pkg),
realpath: isLink ? requested.fetchSpec : childPath(tree.realpath, pkg),
children: pkg._bundled || [],
location: tree.location + '/' + pkg.name,
isLink: isLink,
isInLink: tree.isLink
})
tree.children.push(child)
if (pkg._bundled) {
delete pkg._bundled
inflateBundled(child, child, child.children)
}
return child
})
}
function childIsEquivalent (sw, requested, child) {
if (!child) return false
if (child.fromShrinkwrap) return true
if (sw.integrity && child.package._integrity === sw.integrity) return true
if (child.isLink && requested.type === 'directory') return path.relative(child.realpath, requested.fetchSpec) === ''
if (sw.resolved) return child.package._resolved === sw.resolved
if (!requested.registry && sw.from) return child.package._from === sw.from
if (!requested.registry && child.package._resolved) return sw.version === child.package._resolved
return child.package.version === sw.version
}
|