summaryrefslogtreecommitdiff
path: root/deps/npm/lib/pack.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/lib/pack.js')
-rw-r--r--deps/npm/lib/pack.js170
1 files changed, 151 insertions, 19 deletions
diff --git a/deps/npm/lib/pack.js b/deps/npm/lib/pack.js
index f6a0eff805..93c21ad559 100644
--- a/deps/npm/lib/pack.js
+++ b/deps/npm/lib/pack.js
@@ -6,7 +6,9 @@
const BB = require('bluebird')
+const byteSize = require('byte-size')
const cacache = require('cacache')
+const columnify = require('columnify')
const cp = require('child_process')
const deprCheck = require('./utils/depr-check')
const fpm = require('./fetch-package-metadata')
@@ -28,6 +30,7 @@ const pinflight = require('promise-inflight')
const readJson = BB.promisify(require('read-package-json'))
const tar = require('tar')
const packlist = require('npm-packlist')
+const ssri = require('ssri')
pack.usage = 'npm pack [[<@scope>/]<pkg>...]'
@@ -46,35 +49,64 @@ function pack (args, silent, cb) {
BB.all(
args.map((arg) => pack_(arg, cwd))
- ).then((files) => {
- if (!silent) {
- output(files.map((f) => path.relative(cwd, f)).join('\n'))
+ ).then((tarballs) => {
+ if (!silent && npm.config.get('json')) {
+ output(JSON.stringify(tarballs, null, 2))
+ } else if (!silent) {
+ tarballs.forEach(logContents)
+ output(tarballs.map((f) => path.relative(cwd, f.filename)).join('\n'))
}
- cb(null, files)
- }, cb)
+ return tarballs
+ }).nodeify(cb)
}
-// add to cache, then cp to the cwd
function pack_ (pkg, dir) {
return BB.fromNode((cb) => fpm(pkg, dir, cb)).then((mani) => {
let name = mani.name[0] === '@'
// scoped packages get special treatment
- ? mani.name.substr(1).replace(/\//g, '-')
- : mani.name
+ ? mani.name.substr(1).replace(/\//g, '-')
+ : mani.name
const target = `${name}-${mani.version}.tgz`
return pinflight(target, () => {
if (mani._requested.type === 'directory') {
- return prepareDirectory(mani._resolved).then(() => {
- return packDirectory(mani, mani._resolved, target)
+ return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'packing'}, (tmp) => {
+ const tmpTarget = path.join(tmp, path.basename(target))
+ return prepareDirectory(mani._resolved)
+ .then(() => {
+ return packDirectory(mani, mani._resolved, tmpTarget, target, true)
+ })
+ .tap(() => {
+ if (npm.config.get('dry-run')) {
+ log.verbose('pack', '--dry-run mode enabled. Skipping write.')
+ } else {
+ return move(tmpTarget, target, {Promise: BB, fs})
+ }
+ })
+ })
+ } else if (npm.config.get('dry-run')) {
+ log.verbose('pack', '--dry-run mode enabled. Skipping write.')
+ return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'packing'}, (tmp) => {
+ const tmpTarget = path.join(tmp, path.basename(target))
+ return packFromPackage(pkg, tmpTarget, target)
})
} else {
- return pacote.tarball.toFile(pkg, target, pacoteOpts())
- .then(() => target)
+ return packFromPackage(pkg, target, target)
}
})
})
}
+function packFromPackage (arg, target, filename) {
+ const opts = pacoteOpts()
+ return pacote.tarball.toFile(arg, target, pacoteOpts())
+ .then(() => cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'unpacking'}, (tmp) => {
+ const tmpTarget = path.join(tmp, filename)
+ return pacote.extract(arg, tmpTarget, opts)
+ .then(() => readJson(path.join(tmpTarget, 'package.json')))
+ }))
+ .then((pkg) => getContents(pkg, target, filename))
+}
+
module.exports.prepareDirectory = prepareDirectory
function prepareDirectory (dir) {
return readJson(path.join(dir, 'package.json')).then((pkg) => {
@@ -105,7 +137,7 @@ function prepareDirectory (dir) {
}
module.exports.packDirectory = packDirectory
-function packDirectory (mani, dir, target) {
+function packDirectory (mani, dir, target, filename, logIt) {
deprCheck(mani)
return readJson(path.join(dir, 'package.json')).then((pkg) => {
return lifecycle(pkg, 'prepack', dir)
@@ -120,22 +152,122 @@ function packDirectory (mani, dir, target) {
cwd: dir,
prefix: 'package/',
portable: true,
- noMtime: true,
+ // Provide a specific date in the 1980s for the benefit of zip,
+ // which is confounded by files dated at the Unix epoch 0.
+ mtime: new Date('1985-10-26T08:15:00.000Z'),
gzip: true
}
- return packlist({ path: dir })
+ return BB.resolve(packlist({ path: dir }))
// NOTE: node-tar does some Magic Stuff depending on prefixes for files
// specifically with @ signs, so we just neutralize that one
// and any such future "features" by prepending `./`
.then((files) => tar.create(tarOpt, files.map((f) => `./${f}`)))
- .then(() => move(tmpTarget, target, {Promise: BB, fs}))
- .then(() => lifecycle(pkg, 'postpack', dir))
- .then(() => target)
+ .then(() => getContents(pkg, tmpTarget, filename, logIt))
+ // thread the content info through
+ .tap(() => move(tmpTarget, target, {Promise: BB, fs}))
+ .tap(() => lifecycle(pkg, 'postpack', dir))
})
})
}
+module.exports.logContents = logContents
+function logContents (tarball) {
+ log.notice('')
+ log.notice('', `${npm.config.get('unicode') ? '📦 ' : 'package:'} ${tarball.name}@${tarball.version}`)
+ log.notice('=== Tarball Contents ===')
+ if (tarball.files.length) {
+ log.notice('', columnify(tarball.files.map((f) => {
+ const bytes = byteSize(f.size)
+ return {path: f.path, size: `${bytes.value}${bytes.unit}`}
+ }), {
+ include: ['size', 'path'],
+ showHeaders: false
+ }))
+ }
+ if (tarball.bundled.length) {
+ log.notice('=== Bundled Dependencies ===')
+ tarball.bundled.forEach((name) => log.notice('', name))
+ }
+ log.notice('=== Tarball Details ===')
+ log.notice('', columnify([
+ {name: 'name:', value: tarball.name},
+ {name: 'version:', value: tarball.version},
+ tarball.filename && {name: 'filename:', value: tarball.filename},
+ {name: 'package size:', value: byteSize(tarball.size)},
+ {name: 'unpacked size:', value: byteSize(tarball.unpackedSize)},
+ {name: 'shasum:', value: tarball.shasum},
+ {
+ name: 'integrity:',
+ value: tarball.integrity.toString().substr(0, 20) + '[...]' + tarball.integrity.toString().substr(80)},
+ tarball.bundled.length && {name: 'bundled deps:', value: tarball.bundled.length},
+ tarball.bundled.length && {name: 'bundled files:', value: tarball.entryCount - tarball.files.length},
+ tarball.bundled.length && {name: 'own files:', value: tarball.files.length},
+ {name: 'total files:', value: tarball.entryCount}
+ ].filter((x) => x), {
+ include: ['name', 'value'],
+ showHeaders: false
+ }))
+ log.notice('', '')
+}
+
+module.exports.getContents = getContents
+function getContents (pkg, target, filename, silent) {
+ const bundledWanted = new Set(
+ pkg.bundleDependencies ||
+ pkg.bundledDependencies ||
+ []
+ )
+ const files = []
+ const bundled = new Set()
+ let totalEntries = 0
+ let totalEntrySize = 0
+ return tar.t({
+ file: target,
+ onentry (entry) {
+ totalEntries++
+ totalEntrySize += entry.size
+ const p = entry.path
+ if (p.startsWith('package/node_modules/')) {
+ const name = p.match(/^package\/node_modules\/((?:@[^/]+\/)?[^/]+)/)[1]
+ if (bundledWanted.has(name)) {
+ bundled.add(name)
+ }
+ } else {
+ files.push({
+ path: entry.path.replace(/^package\//, ''),
+ size: entry.size,
+ mode: entry.mode
+ })
+ }
+ },
+ strip: 1
+ })
+ .then(() => BB.all([
+ BB.fromNode((cb) => fs.stat(target, cb)),
+ ssri.fromStream(fs.createReadStream(target), {
+ algorithms: ['sha1', 'sha512']
+ })
+ ]))
+ .then(([stat, integrity]) => {
+ const shasum = integrity['sha1'][0].hexDigest()
+ return {
+ id: pkg._id,
+ name: pkg.name,
+ version: pkg.version,
+ from: pkg._from,
+ size: stat.size,
+ unpackedSize: totalEntrySize,
+ shasum,
+ integrity: ssri.parse(integrity['sha512'][0]),
+ filename,
+ files,
+ entryCount: totalEntries,
+ bundled: Array.from(bundled)
+ }
+ })
+}
+
const PASSTHROUGH_OPTS = [
'always-auth',
'auth-type',
@@ -170,7 +302,7 @@ function packGitDep (manifest, dir) {
return acc
}, [])
const child = cp.spawn(process.env.NODE || process.execPath, [
- require.main.filename,
+ require.resolve('../bin/npm-cli.js'),
'install',
'--dev',
'--prod',