-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
3c2f1e3
commit 4d8543b
Showing
1 changed file
with
35 additions
and
242 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,57 @@ | ||
'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 libpack = require('libnpmpack') | ||
|
||
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 { getContents, logTar } = require('./utils/tar') | ||
|
||
pack.usage = 'npm pack [[<@scope>/]<pkg>...] [--dry-run]' | ||
const writeFile = util.promisify(require('fs').writeFile) | ||
const readJson = util.promisify(require('read-package-json')) | ||
|
||
// if it can be installed, it can be packed. | ||
pack.completion = install.completion | ||
pack.usage = 'npm pack [[<@scope>/]<pkg>...] [--dry-run]' | ||
|
||
function pack (args, silent, cb) { | ||
const cwd = process.cwd() | ||
module.exports = pack | ||
async function pack (args, silent, cb) { | ||
if (typeof cb !== 'function') { | ||
cb = silent | ||
silent = false | ||
} | ||
|
||
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 tarballs = await Promise.all(args.map((arg) => pack_(arg))) | ||
if (!silent && npm.config.get('json')) { | ||
output(JSON.stringify(tarballs, null, 2)) | ||
} else if (!silent) { | ||
tarballs.forEach((tar) => { | ||
logTar(tar, { | ||
log, | ||
unicode: npm.config.get('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 Promise.resolve(tarballs) | ||
.then(cb()) | ||
} | ||
|
||
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 | ||
} | ||
|
||
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)) | ||
}) | ||
}) | ||
} | ||
async function pack_ (arg) { | ||
let manifest = await readJson(`${arg}/package.json`) | ||
const filename = `${manifest.name}-${manifest.version}.tgz` | ||
const tarballData = await libpack(`file:${arg}`) | ||
const pkgContents = await getContents(manifest, 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' | ||
] | ||
const dryRun = npm.config.get('dry-run') | ||
|
||
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 | ||
}) | ||
if (!dryRun) { | ||
try { | ||
await writeFile(filename, tarballData) | ||
} catch (e) { | ||
console.error(e) | ||
} | ||
}).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 | ||
} |