-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2cec6a6
commit bc12db1
Showing
1 changed file
with
37 additions
and
243 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,264 +1,58 @@ | ||
'use strict' | ||
|
||
// npm pack <pkg> | ||
// Packs the specified package into a .tgz file, which can then | ||
// be installed. | ||
|
||
// Set this early to avoid issues with circular dependencies. | ||
module.exports = pack | ||
|
||
const BB = require('bluebird') | ||
|
||
const cacache = require('cacache') | ||
const cp = require('child_process') | ||
const deprCheck = require('./utils/depr-check') | ||
const fpm = require('./fetch-package-metadata') | ||
const fs = require('graceful-fs') | ||
const install = require('./install') | ||
const lifecycle = BB.promisify(require('./utils/lifecycle')) | ||
const { getTarContents, logTarContents } = ('/.utils/tar-contents') | ||
const util = require('util') | ||
const log = require('npmlog') | ||
const move = require('move-concurrently') | ||
const npm = require('./npm') | ||
const npmConfig = require('./config/figgy-config.js') | ||
const output = require('./utils/output') | ||
const pacote = require('pacote') | ||
const path = require('path') | ||
const PassThrough = require('stream').PassThrough | ||
const pathIsInside = require('path-is-inside') | ||
const pipe = BB.promisify(require('mississippi').pipe) | ||
const prepublishWarning = require('./utils/warn-deprecated')('prepublish-on-install') | ||
const pinflight = require('promise-inflight') | ||
const readJson = BB.promisify(require('read-package-json')) | ||
const tar = require('tar') | ||
const packlist = require('npm-packlist') | ||
const libpack = require('libnpmpack') | ||
const npa = require('npm-package-arg') | ||
|
||
const npm = require('./npm.js') | ||
const output = require('./utils/output.js') | ||
const { getContents, logTar } = require('./utils/tar.js') | ||
|
||
pack.usage = 'npm pack [[<@scope>/]<pkg>...] [--dry-run]' | ||
const writeFile = util.promisify(require('fs').writeFile) | ||
|
||
// if it can be installed, it can be packed. | ||
pack.completion = install.completion | ||
cmd.usage = 'npm pack [[<@scope>/]<pkg>...] [--dry-run]' | ||
|
||
function pack (args, silent, cb) { | ||
const cwd = process.cwd() | ||
module.exports = cmd | ||
function cmd(args, silent, cb) { | ||
if (typeof cb !== 'function') { | ||
cb = silent | ||
silent = false | ||
} | ||
pack(args, silent) | ||
.then(() => cb()) | ||
.catch(cb) | ||
} | ||
|
||
async function pack (args, silent) { | ||
if (args.length === 0) args = ['.'] | ||
|
||
BB.all( | ||
args.map((arg) => pack_(arg, cwd)) | ||
).then((tarballs) => { | ||
if (!silent && npm.config.get('json')) { | ||
output(JSON.stringify(tarballs, null, 2)) | ||
} else if (!silent) { | ||
tarballs.forEach(logTarContents) | ||
output(tarballs.map((f) => path.relative(cwd, f.filename)).join('\n')) | ||
} | ||
return tarballs | ||
}).nodeify(cb) | ||
} | ||
|
||
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 | ||
const target = `${name}-${mani.version}.tgz` | ||
return pinflight(target, () => { | ||
const dryRun = npm.config.get('dry-run') | ||
if (mani._requested.type === 'directory') { | ||
return prepareDirectory(mani._resolved) | ||
.then(() => { | ||
return packDirectory(mani, mani._resolved, target, target, true, dryRun) | ||
}) | ||
} else if (dryRun) { | ||
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 packFromPackage(pkg, target, target) | ||
} | ||
const opts = { ...npm.flatOptions } | ||
const { unicode, json } = opts | ||
const tarballs = await Promise.all(args.map((arg) => pack_(arg, opts))) | ||
if (!silent && json) { | ||
output(JSON.stringify(tarballs, null, 2)) | ||
} else if (!silent) { | ||
tarballs.forEach((tar) => { | ||
logTar(tar, { log, unicode }) | ||
}) | ||
}) | ||
} | ||
|
||
function packFromPackage (arg, target, filename) { | ||
const opts = npmConfig() | ||
return pacote.tarball.toFile(arg, target, opts) | ||
.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) => getTarContents(pkg, target, filename)) | ||
} | ||
} | ||
|
||
module.exports.prepareDirectory = prepareDirectory | ||
function prepareDirectory (dir) { | ||
return readJson(path.join(dir, 'package.json')).then((pkg) => { | ||
if (!pkg.name) { | ||
throw new Error('package.json requires a "name" field') | ||
} | ||
if (!pkg.version) { | ||
throw new Error('package.json requires a valid "version" field') | ||
} | ||
if (!pathIsInside(dir, npm.tmp)) { | ||
if (pkg.scripts && pkg.scripts.prepublish) { | ||
prepublishWarning([ | ||
'As of npm@5, `prepublish` scripts are deprecated.', | ||
'Use `prepare` for build steps and `prepublishOnly` for upload-only.', | ||
'See the deprecation note in `npm help scripts` for more information.' | ||
]) | ||
} | ||
if (npm.config.get('ignore-prepublish')) { | ||
return lifecycle(pkg, 'prepare', dir).then(() => pkg) | ||
} else { | ||
return lifecycle(pkg, 'prepublish', dir).then(() => { | ||
return lifecycle(pkg, 'prepare', dir) | ||
}).then(() => pkg) | ||
} | ||
} | ||
return pkg | ||
}) | ||
return tarballs | ||
} | ||
|
||
module.exports.packDirectory = packDirectory | ||
function packDirectory (mani, dir, target, filename, logIt, dryRun) { | ||
deprCheck(mani) | ||
return readJson(path.join(dir, 'package.json')).then((pkg) => { | ||
return lifecycle(pkg, 'prepack', dir) | ||
}).then(() => { | ||
return readJson(path.join(dir, 'package.json')) | ||
}).then((pkg) => { | ||
return cacache.tmp.withTmp(npm.tmp, {tmpPrefix: 'packing'}, (tmp) => { | ||
const tmpTarget = path.join(tmp, path.basename(target)) | ||
|
||
const tarOpt = { | ||
file: tmpTarget, | ||
cwd: dir, | ||
prefix: 'package/', | ||
portable: 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 | ||
} | ||
async function pack_ (arg, opts) { | ||
const spec = npa(arg) | ||
const manifest = await pacote.manifest(spec) | ||
const filename = `${manifest.name}-${manifest.version}.tgz` | ||
const tarballData = await libpack(arg) | ||
const pkgContents = await getContents(manifest, tarballData) | ||
const { dryRun } = opts | ||
|
||
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(() => getTarContents(pkg, tmpTarget, filename, logIt)) | ||
// thread the content info through | ||
.tap(() => { | ||
if (dryRun) { | ||
log.verbose('pack', '--dry-run mode enabled. Skipping write.') | ||
} else { | ||
return move(tmpTarget, target, {Promise: BB, fs}) | ||
} | ||
}) | ||
.tap(() => lifecycle(pkg, 'postpack', dir)) | ||
}) | ||
}) | ||
} | ||
if (!dryRun) { | ||
await writeFile(filename, tarballData) | ||
} | ||
|
||
const PASSTHROUGH_OPTS = [ | ||
'always-auth', | ||
'auth-type', | ||
'ca', | ||
'cafile', | ||
'cert', | ||
'git', | ||
'local-address', | ||
'maxsockets', | ||
'offline', | ||
'prefer-offline', | ||
'prefer-online', | ||
'proxy', | ||
'https-proxy', | ||
'registry', | ||
'send-metrics', | ||
'sso-poll-frequency', | ||
'sso-type', | ||
'strict-ssl' | ||
] | ||
|
||
module.exports.packGitDep = packGitDep | ||
function packGitDep (manifest, dir) { | ||
const stream = new PassThrough() | ||
readJson(path.join(dir, 'package.json')).then((pkg) => { | ||
if (pkg.scripts && pkg.scripts.prepare) { | ||
log.verbose('prepareGitDep', `${manifest._spec}: installing devDeps and running prepare script.`) | ||
const cliArgs = PASSTHROUGH_OPTS.reduce((acc, opt) => { | ||
if (npm.config.get(opt, 'cli') != null) { | ||
acc.push(`--${opt}=${npm.config.get(opt)}`) | ||
} | ||
return acc | ||
}, []) | ||
const child = cp.spawn(process.env.NODE || process.execPath, [ | ||
require.resolve('../bin/npm-cli.js'), | ||
'install', | ||
'--dev', | ||
'--prod', | ||
'--ignore-prepublish', | ||
'--no-progress', | ||
'--no-save' | ||
].concat(cliArgs), { | ||
cwd: dir, | ||
env: process.env | ||
}) | ||
let errData = [] | ||
let errDataLen = 0 | ||
let outData = [] | ||
let outDataLen = 0 | ||
child.stdout.on('data', (data) => { | ||
outData.push(data) | ||
outDataLen += data.length | ||
log.gauge.pulse('preparing git package') | ||
}) | ||
child.stderr.on('data', (data) => { | ||
errData.push(data) | ||
errDataLen += data.length | ||
log.gauge.pulse('preparing git package') | ||
}) | ||
return BB.fromNode((cb) => { | ||
child.on('error', cb) | ||
child.on('exit', (code, signal) => { | ||
if (code > 0) { | ||
const err = new Error(`${signal}: npm exited with code ${code} while attempting to build ${manifest._requested}. Clone the repository manually and run 'npm install' in it for more information.`) | ||
err.code = code | ||
err.signal = signal | ||
cb(err) | ||
} else { | ||
cb() | ||
} | ||
}) | ||
}).then(() => { | ||
if (outDataLen > 0) log.silly('prepareGitDep', '1>', Buffer.concat(outData, outDataLen).toString()) | ||
if (errDataLen > 0) log.silly('prepareGitDep', '2>', Buffer.concat(errData, errDataLen).toString()) | ||
}, (err) => { | ||
if (outDataLen > 0) log.error('prepareGitDep', '1>', Buffer.concat(outData, outDataLen).toString()) | ||
if (errDataLen > 0) log.error('prepareGitDep', '2>', Buffer.concat(errData, errDataLen).toString()) | ||
throw err | ||
}) | ||
} | ||
}).then(() => { | ||
return readJson(path.join(dir, 'package.json')) | ||
}).then((pkg) => { | ||
return cacache.tmp.withTmp(npm.tmp, { | ||
tmpPrefix: 'pacote-packing' | ||
}, (tmp) => { | ||
const tmpTar = path.join(tmp, 'package.tgz') | ||
return packDirectory(manifest, dir, tmpTar).then(() => { | ||
return pipe(fs.createReadStream(tmpTar), stream) | ||
}) | ||
}) | ||
}).catch((err) => stream.emit('error', err)) | ||
return stream | ||
return pkgContents | ||
} |