mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-28 03:51:40 -04:00
@@ -60,18 +60,17 @@ export type BasicOptions = {
|
||||
ignoreScripts: boolean
|
||||
}
|
||||
|
||||
export default (opts: BasicOptions): Promise<CommandNamespace> => {
|
||||
export default async function (opts: BasicOptions): Promise<CommandNamespace> {
|
||||
const cwd = opts.cwd || process.cwd()
|
||||
const cmd: CommandNamespace = {
|
||||
ctx: {}
|
||||
}
|
||||
let lockfile: string
|
||||
return (opts.global ? readGlobalPkg(opts.globalPath) : readPkgUp({ cwd }))
|
||||
.then((_: PackageAndPath) => { cmd.pkg = _ })
|
||||
.then(() => updateContext())
|
||||
.then(() => mkdirp(cmd.ctx.store))
|
||||
.then(() => lock(lockfile))
|
||||
.then(() => cmd)
|
||||
cmd.pkg = await (opts.global ? readGlobalPkg(opts.globalPath) : readPkgUp({ cwd }))
|
||||
updateContext()
|
||||
await mkdirp(cmd.ctx.store)
|
||||
await lock(lockfile)
|
||||
return cmd
|
||||
|
||||
function updateContext () {
|
||||
const root = cmd.pkg.path ? path.dirname(cmd.pkg.path) : cwd
|
||||
@@ -109,25 +108,25 @@ function failIfNotCompatible (storeVersion: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function readGlobalPkg (globalPath: string) {
|
||||
async function readGlobalPkg (globalPath: string) {
|
||||
if (!globalPath) throw new Error('globalPath is required')
|
||||
const globalPnpm = resolveGlobalPkgPath(globalPath)
|
||||
const globalPkgPath = path.resolve(globalPnpm, 'package.json')
|
||||
return readGlobalPkgJson(globalPkgPath)
|
||||
.then(globalPkgJson => ({
|
||||
pkg: globalPkgJson,
|
||||
path: globalPkgPath
|
||||
}))
|
||||
}
|
||||
|
||||
function readGlobalPkgJson (globalPkgPath: string) {
|
||||
try {
|
||||
const globalPkgJson = requireJson(globalPkgPath)
|
||||
return Promise.resolve(globalPkgJson)
|
||||
} catch (err) {
|
||||
const pkgJson = {}
|
||||
return mkdirp(path.dirname(globalPkgPath))
|
||||
.then(_ => writeJson(globalPkgPath, pkgJson))
|
||||
.then(_ => pkgJson)
|
||||
const globalPkgJson = await readGlobalPkgJson(globalPkgPath)
|
||||
return {
|
||||
pkg: globalPkgJson,
|
||||
path: globalPkgPath
|
||||
}
|
||||
}
|
||||
|
||||
async function readGlobalPkgJson (globalPkgPath: string) {
|
||||
try {
|
||||
const globalPkgJson = requireJson(globalPkgPath)
|
||||
return globalPkgJson
|
||||
} catch (err) {
|
||||
const pkgJson = {}
|
||||
await mkdirp(path.dirname(globalPkgPath))
|
||||
await writeJson(globalPkgPath, pkgJson)
|
||||
return pkgJson
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,44 +63,42 @@ export type PublicInstallationOptions = BasicOptions & {
|
||||
* install({'lodash': '1.0.0', 'foo': '^2.1.0' }, { quiet: true })
|
||||
*/
|
||||
|
||||
export default function install (fuzzyDeps: string[] | Dependencies, opts: PublicInstallationOptions) {
|
||||
export default async function install (fuzzyDeps: string[] | Dependencies, opts: PublicInstallationOptions) {
|
||||
let packagesToInstall = mapify(fuzzyDeps)
|
||||
const installType = packagesToInstall && Object.keys(packagesToInstall).length ? 'named' : 'general'
|
||||
opts = Object.assign({}, defaults, opts)
|
||||
|
||||
let cmd: InstallNamespace
|
||||
const isProductionInstall = opts.production || process.env.NODE_ENV === 'production'
|
||||
let cmd: InstallNamespace
|
||||
|
||||
return initCmd(opts)
|
||||
.then(_ => { cmd = _ })
|
||||
.then(() => install())
|
||||
.then(() => linkPeers(cmd.ctx.store, cmd.ctx.installs))
|
||||
try {
|
||||
cmd = await initCmd(opts)
|
||||
await install()
|
||||
await linkPeers(cmd.ctx.store, cmd.ctx.installs)
|
||||
// postinstall hooks
|
||||
.then(() => {
|
||||
if (opts.ignoreScripts || !cmd.ctx.piq || !cmd.ctx.piq.length) return
|
||||
return seq(
|
||||
cmd.ctx.piq.map(pkg => () => linkBins(path.join(pkg.path, '_', 'node_modules'))
|
||||
.then(() => postInstall(pkg.path, installLogger(pkg.pkgFullname)))
|
||||
.catch(err => {
|
||||
if (cmd.ctx.installs[pkg.pkgFullname].optional) {
|
||||
console.log('Skipping failed optional dependency ' + pkg.pkgFullname + ':')
|
||||
console.log(err.message || err)
|
||||
return
|
||||
}
|
||||
throw err
|
||||
})
|
||||
)
|
||||
)
|
||||
})
|
||||
.then(() => linkBins(path.join(cmd.ctx.root, 'node_modules')))
|
||||
.then(() => mainPostInstall())
|
||||
.then(() => cmd.unlock())
|
||||
.catch((err: Error) => {
|
||||
if (!(opts.ignoreScripts || !cmd.ctx.piq || !cmd.ctx.piq.length)) {
|
||||
await seq(
|
||||
cmd.ctx.piq.map(pkg => () => linkBins(path.join(pkg.path, '_', 'node_modules'))
|
||||
.then(() => postInstall(pkg.path, installLogger(pkg.pkgFullname)))
|
||||
.catch(err => {
|
||||
if (cmd.ctx.installs[pkg.pkgFullname].optional) {
|
||||
console.log('Skipping failed optional dependency ' + pkg.pkgFullname + ':')
|
||||
console.log(err.message || err)
|
||||
return
|
||||
}
|
||||
throw err
|
||||
})
|
||||
))
|
||||
}
|
||||
await linkBins(path.join(cmd.ctx.root, 'node_modules'))
|
||||
await mainPostInstall()
|
||||
await cmd.unlock()
|
||||
} catch (err) {
|
||||
if (cmd && cmd.unlock) cmd.unlock()
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
function install () {
|
||||
async function install () {
|
||||
if (installType !== 'named') {
|
||||
if (!cmd.pkg.pkg) throw runtimeError('No package.json found')
|
||||
packagesToInstall = Object.assign({}, cmd.pkg.pkg.dependencies || {})
|
||||
@@ -114,18 +112,21 @@ export default function install (fuzzyDeps: string[] | Dependencies, opts: Publi
|
||||
fetchRetryMintimeout: opts.fetchRetryMintimeout,
|
||||
fetchRetryMaxtimeout: opts.fetchRetryMaxtimeout
|
||||
})
|
||||
return installMultiple(cmd.ctx,
|
||||
packagesToInstall,
|
||||
cmd.pkg.pkg && cmd.pkg.pkg.optionalDependencies,
|
||||
path.join(cmd.ctx.root, 'node_modules'),
|
||||
Object.assign({}, opts, { dependent: cmd.pkg && cmd.pkg.path })
|
||||
)
|
||||
.then(savePkgs)
|
||||
.then(_ => cmd.storeJsonCtrl.save({
|
||||
pnpm: pnpmPkgJson.version,
|
||||
dependents: cmd.ctx.dependents,
|
||||
dependencies: cmd.ctx.dependencies
|
||||
}))
|
||||
|
||||
const pkgs = await installMultiple(cmd.ctx,
|
||||
packagesToInstall,
|
||||
cmd.pkg.pkg && cmd.pkg.pkg.optionalDependencies,
|
||||
path.join(cmd.ctx.root, 'node_modules'),
|
||||
Object.assign({}, opts, { dependent: cmd.pkg && cmd.pkg.path })
|
||||
)
|
||||
|
||||
await savePkgs(pkgs)
|
||||
|
||||
cmd.storeJsonCtrl.save({
|
||||
pnpm: pnpmPkgJson.version,
|
||||
dependents: cmd.ctx.dependents,
|
||||
dependencies: cmd.ctx.dependencies
|
||||
})
|
||||
}
|
||||
|
||||
function savePkgs (packages: PackageContext[]) {
|
||||
|
||||
@@ -7,15 +7,15 @@ import {linkPkgBins} from '../install/link_bins'
|
||||
import mkdirp from '../fs/mkdirp'
|
||||
import {PublicInstallationOptions} from './install'
|
||||
|
||||
export function linkFromRelative (linkTo: string, opts: PublicInstallationOptions) {
|
||||
export async function linkFromRelative (linkTo: string, opts: PublicInstallationOptions) {
|
||||
const cwd = opts && opts.cwd || process.cwd()
|
||||
const linkedPkgPath = path.resolve(cwd, linkTo)
|
||||
const currentModules = path.resolve(cwd, 'node_modules')
|
||||
return installPkgDeps(Object.assign({}, opts, { cwd: linkedPkgPath }))
|
||||
.then(() => mkdirp(currentModules))
|
||||
.then(() => readPkgUp({ cwd: linkedPkgPath }))
|
||||
.then(pkg => relSymlink(linkedPkgPath, path.resolve(currentModules, pkg.pkg.name)))
|
||||
.then(() => linkPkgBins(currentModules, linkedPkgPath))
|
||||
await installPkgDeps(Object.assign({}, opts, { cwd: linkedPkgPath }))
|
||||
await mkdirp(currentModules)
|
||||
const pkg = await readPkgUp({ cwd: linkedPkgPath })
|
||||
await relSymlink(linkedPkgPath, path.resolve(currentModules, pkg.pkg.name))
|
||||
return linkPkgBins(currentModules, linkedPkgPath)
|
||||
}
|
||||
|
||||
export function linkFromGlobal (pkgName: string, opts: PublicInstallationOptions) {
|
||||
|
||||
@@ -9,41 +9,42 @@ import defaults from '../defaults'
|
||||
import requireJson from '../fs/require_json'
|
||||
import {PublicInstallationOptions} from './install'
|
||||
|
||||
export default function uninstallCmd (pkgsToUninstall: string[], opts: PublicInstallationOptions) {
|
||||
export default async function uninstallCmd (pkgsToUninstall: string[], opts: PublicInstallationOptions) {
|
||||
opts = Object.assign({}, defaults, opts)
|
||||
|
||||
let cmd: CommandNamespace
|
||||
const uninstalledPkgs: string[] = []
|
||||
const saveType = getSaveType(opts)
|
||||
let cmd: CommandNamespace
|
||||
|
||||
return initCmd(opts)
|
||||
.then(_ => { cmd = _ })
|
||||
.then(() => {
|
||||
cmd.pkg.pkg.dependencies = cmd.pkg.pkg.dependencies || {}
|
||||
const pkgFullNames = pkgsToUninstall.map(dep => cmd.ctx.dependencies[cmd.pkg.path].find(_ => _.indexOf(dep + '@') === 0))
|
||||
tryUninstall(pkgFullNames.slice())
|
||||
if (cmd.ctx.dependencies[cmd.pkg.path]) {
|
||||
pkgFullNames.forEach(dep => {
|
||||
cmd.ctx.dependencies[cmd.pkg.path].splice(cmd.ctx.dependencies[cmd.pkg.path].indexOf(dep), 1)
|
||||
})
|
||||
if (!cmd.ctx.dependencies[cmd.pkg.path].length) {
|
||||
delete cmd.ctx.dependencies[cmd.pkg.path]
|
||||
}
|
||||
try {
|
||||
cmd = await initCmd(opts)
|
||||
cmd.pkg.pkg.dependencies = cmd.pkg.pkg.dependencies || {}
|
||||
const pkgFullNames = pkgsToUninstall.map(dep => cmd.ctx.dependencies[cmd.pkg.path].find(_ => _.indexOf(dep + '@') === 0))
|
||||
tryUninstall(pkgFullNames.slice())
|
||||
if (cmd.ctx.dependencies[cmd.pkg.path]) {
|
||||
pkgFullNames.forEach(dep => {
|
||||
cmd.ctx.dependencies[cmd.pkg.path].splice(cmd.ctx.dependencies[cmd.pkg.path].indexOf(dep), 1)
|
||||
})
|
||||
if (!cmd.ctx.dependencies[cmd.pkg.path].length) {
|
||||
delete cmd.ctx.dependencies[cmd.pkg.path]
|
||||
}
|
||||
return Promise.all(uninstalledPkgs.map(removePkgFromStore))
|
||||
})
|
||||
.then(() => cmd.storeJsonCtrl.save({
|
||||
}
|
||||
await Promise.all(uninstalledPkgs.map(removePkgFromStore))
|
||||
|
||||
cmd.storeJsonCtrl.save({
|
||||
pnpm: cmd.ctx.pnpm,
|
||||
dependents: cmd.ctx.dependents,
|
||||
dependencies: cmd.ctx.dependencies
|
||||
}))
|
||||
.then(() => Promise.all(pkgsToUninstall.map(dep => rimraf(path.join(cmd.ctx.root, 'node_modules', dep)))))
|
||||
.then(() => saveType && removeDeps(cmd.pkg.path, pkgsToUninstall, saveType))
|
||||
.then(() => cmd.unlock())
|
||||
.catch((err: Error) => {
|
||||
if (cmd && cmd.unlock) cmd.unlock()
|
||||
throw err
|
||||
})
|
||||
await Promise.all(pkgsToUninstall.map(dep => rimraf(path.join(cmd.ctx.root, 'node_modules', dep))))
|
||||
if (saveType) {
|
||||
await removeDeps(cmd.pkg.path, pkgsToUninstall, saveType)
|
||||
}
|
||||
await cmd.unlock()
|
||||
} catch (err) {
|
||||
if (cmd && cmd.unlock) await cmd.unlock()
|
||||
throw err
|
||||
}
|
||||
|
||||
function canBeUninstalled (pkgFullName: string) {
|
||||
return !cmd.ctx.dependents[pkgFullName] || !cmd.ctx.dependents[pkgFullName].length ||
|
||||
|
||||
@@ -9,21 +9,19 @@ export type SymlinkType = 'junction' | 'dir'
|
||||
* srcPath. API compatible with [`fs#symlink`](https://nodejs.org/api/fs.html#fs_fs_symlink_srcpath_dstpath_type_callback).
|
||||
*/
|
||||
|
||||
export default function forceSymlink (srcPath: string, dstPath: string, type: SymlinkType) {
|
||||
export default async function forceSymlink (srcPath: string, dstPath: string, type: SymlinkType) {
|
||||
debug(`${srcPath} -> ${dstPath}`)
|
||||
try {
|
||||
fs.symlinkSync(srcPath, dstPath, type)
|
||||
return Promise.resolve()
|
||||
return
|
||||
} catch (err) {
|
||||
if ((<NodeJS.ErrnoException>err).code !== 'EEXIST') return Promise.reject(err)
|
||||
if ((<NodeJS.ErrnoException>err).code !== 'EEXIST') throw err
|
||||
|
||||
return fs.readlink(dstPath)
|
||||
.then((linkString: string) => {
|
||||
if (srcPath === linkString) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return fs.unlink(dstPath)
|
||||
.then(() => forceSymlink(srcPath, dstPath, type))
|
||||
})
|
||||
const linkString = await fs.readlink(dstPath)
|
||||
if (srcPath === linkString) {
|
||||
return
|
||||
}
|
||||
await fs.unlink(dstPath)
|
||||
await forceSymlink(srcPath, dstPath, type)
|
||||
}
|
||||
}
|
||||
|
||||
193
src/install.ts
193
src/install.ts
@@ -2,6 +2,7 @@ import createDebug from './debug'
|
||||
const debug = createDebug('pnpm:install')
|
||||
import npa = require('npm-package-arg')
|
||||
import fs = require('mz/fs')
|
||||
import {Stats} from 'fs'
|
||||
import logger = require('@zkochan/logger')
|
||||
|
||||
import path = require('path')
|
||||
@@ -88,7 +89,7 @@ export type InstallLog = (msg: string, data?: any) => void
|
||||
* - symlink bins
|
||||
*/
|
||||
|
||||
export default function install (ctx: InstallContext, pkgMeta: PackageMeta, modules: string, options: InstallationOptions): Promise<PackageContext> {
|
||||
export default async function install (ctx: InstallContext, pkgMeta: PackageMeta, modules: string, options: InstallationOptions): Promise<PackageContext> {
|
||||
debug('installing ' + pkgMeta.rawSpec)
|
||||
if (!ctx.builds) ctx.builds = {}
|
||||
if (!ctx.fetches) ctx.fetches = {}
|
||||
@@ -135,33 +136,35 @@ export default function install (ctx: InstallContext, pkgMeta: PackageMeta, modu
|
||||
|
||||
const log: InstallLog = logger.fork(pkg.spec).log.bind(null, 'progress', pkgMeta.rawSpec)
|
||||
|
||||
// it might be a bundleDependency, in which case, don't bother
|
||||
return isAvailable(pkg.spec, modules)
|
||||
.then(_ => _
|
||||
? saveCachedResolution()
|
||||
.then(data => log('package.json', data))
|
||||
: resolve(Object.assign({}, pkg.spec, {root: options.parentRoot || ctx.root}), {log, got: ctx.got})
|
||||
.then(saveResolution)
|
||||
.then(() => log('resolved', pkg))
|
||||
.then(() => buildToStoreCached(ctx, paths, pkg, log))
|
||||
.then(() => mkdirp(paths.modules))
|
||||
.then(() => symlinkToModules(join(paths.target, '_'), paths.modules))
|
||||
.then(() => log('package.json', requireJson(join(paths.target, '_', 'package.json')))))
|
||||
// done
|
||||
.then(_ => {
|
||||
if (!ctx.installs) ctx.installs = {}
|
||||
if (!ctx.installs[pkg.fullname]) {
|
||||
ctx.installs[pkg.fullname] = pkg
|
||||
return
|
||||
}
|
||||
try {
|
||||
// it might be a bundleDependency, in which case, don't bother
|
||||
const available = await isAvailable(pkg.spec, modules)
|
||||
if (available) {
|
||||
const data = await saveCachedResolution()
|
||||
log('package.json', data)
|
||||
} else {
|
||||
const res = await resolve(Object.assign({}, pkg.spec, {root: options.parentRoot || ctx.root}), {log, got: ctx.got})
|
||||
saveResolution(res)
|
||||
log('resolved', pkg)
|
||||
await buildToStoreCached(ctx, paths, pkg, log)
|
||||
await mkdirp(paths.modules)
|
||||
await symlinkToModules(join(paths.target, '_'), paths.modules)
|
||||
log('package.json', requireJson(join(paths.target, '_', 'package.json')))
|
||||
}
|
||||
|
||||
if (!ctx.installs) ctx.installs = {}
|
||||
if (!ctx.installs[pkg.fullname]) {
|
||||
ctx.installs[pkg.fullname] = pkg
|
||||
} else {
|
||||
ctx.installs[pkg.fullname].optional = ctx.installs[pkg.fullname].optional && pkg.optional
|
||||
})
|
||||
.then(_ => log('done'))
|
||||
.then(_ => pkg)
|
||||
.catch(err => {
|
||||
log('error', err)
|
||||
throw err
|
||||
})
|
||||
}
|
||||
|
||||
log('done')
|
||||
return pkg
|
||||
} catch (err) {
|
||||
log('error', err)
|
||||
throw err
|
||||
}
|
||||
|
||||
// set metadata as fetched from resolve()
|
||||
function saveResolution (res: ResolveResult) {
|
||||
@@ -173,17 +176,14 @@ export default function install (ctx: InstallContext, pkgMeta: PackageMeta, modu
|
||||
paths.target = join(ctx.store, res.fullname)
|
||||
}
|
||||
|
||||
function saveCachedResolution (): Promise<Package> {
|
||||
async function saveCachedResolution (): Promise<Package> {
|
||||
const target = join(modules, pkg.spec.name)
|
||||
return fs.lstat(target)
|
||||
.then((stat: any) => {
|
||||
if (stat.isSymbolicLink()) {
|
||||
return fs.readlink(target)
|
||||
.then((path: string) => save(abspath(path, target)))
|
||||
} else {
|
||||
return save(target)
|
||||
}
|
||||
})
|
||||
const stat: Stats = await fs.lstat(target)
|
||||
if (stat.isSymbolicLink()) {
|
||||
const path = await fs.readlink(target)
|
||||
return save(abspath(path, target))
|
||||
}
|
||||
return save(target)
|
||||
|
||||
function save (fullpath: string): Package {
|
||||
const data = requireJson(join(fullpath, 'package.json'))
|
||||
@@ -209,12 +209,11 @@ function buildToStoreCached (ctx: InstallContext, paths: InstallationPaths, pkg:
|
||||
if (ctx.fetches[pkg.fullname]) return ctx.fetches[pkg.fullname]
|
||||
|
||||
return make(paths.target, () =>
|
||||
memoize(ctx.builds, pkg.fullname, () =>
|
||||
Promise.resolve()
|
||||
.then(_ => memoize(ctx.fetches, pkg.fullname, () =>
|
||||
fetchToStore(ctx, paths, pkg, log)))
|
||||
.then(_ => buildInStore(ctx, paths, pkg, log))
|
||||
))
|
||||
memoize(ctx.builds, pkg.fullname, async function () {
|
||||
await memoize(ctx.fetches, pkg.fullname, () => fetchToStore(ctx, paths, pkg, log))
|
||||
return buildInStore(ctx, paths, pkg, log)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -222,53 +221,51 @@ function buildToStoreCached (ctx: InstallContext, paths: InstallationPaths, pkg:
|
||||
* Fetches from npm, recurses to dependencies, runs lifecycle scripts, etc
|
||||
*/
|
||||
|
||||
function fetchToStore (ctx: InstallContext, paths: InstallationPaths, pkg: PackageContext, log: InstallLog) {
|
||||
return Promise.resolve()
|
||||
// download and untar
|
||||
.then(_ => log('download-queued'))
|
||||
.then(_ => mkdirp(join(paths.target, '_')))
|
||||
.then(_ => fetch(join(paths.target, '_'), pkg.dist, {log, got: ctx.got}))
|
||||
.then(_ => pkg.dist.local && pkg.dist.remove ? fs.unlink(pkg.dist.tarball) : Promise.resolve())
|
||||
async function fetchToStore (ctx: InstallContext, paths: InstallationPaths, pkg: PackageContext, log: InstallLog) {
|
||||
// download and untar
|
||||
log('download-queued')
|
||||
await mkdirp(join(paths.target, '_'))
|
||||
await fetch(join(paths.target, '_'), pkg.dist, {log, got: ctx.got})
|
||||
if (pkg.dist.local && pkg.dist.remove) {
|
||||
await fs.unlink(pkg.dist.tarball)
|
||||
}
|
||||
|
||||
// TODO: this is the point it becomes partially useable.
|
||||
// ie, it can now be symlinked into .store/foo@1.0.0.
|
||||
// it is only here that it should be available for ciruclar dependencies.
|
||||
// TODO: this is the point it becomes partially useable.
|
||||
// ie, it can now be symlinked into .store/foo@1.0.0.
|
||||
// it is only here that it should be available for ciruclar dependencies.
|
||||
}
|
||||
|
||||
function buildInStore (ctx: InstallContext, paths: InstallationPaths, pkg: PackageContext, log: InstallLog) {
|
||||
async function buildInStore (ctx: InstallContext, paths: InstallationPaths, pkg: PackageContext, log: InstallLog) {
|
||||
let fulldata: Package
|
||||
|
||||
return Promise.resolve()
|
||||
.then(_ => { fulldata = requireJson(abspath(join(paths.target, '_', 'package.json'))) })
|
||||
.then(_ => log('package.json', fulldata))
|
||||
fulldata = requireJson(abspath(join(paths.target, '_', 'package.json')))
|
||||
log('package.json', fulldata)
|
||||
|
||||
.then(_ => linkBundledDeps(join(paths.target, '_')))
|
||||
await linkBundledDeps(join(paths.target, '_'))
|
||||
|
||||
// recurse down to dependencies
|
||||
.then(_ => log('dependencies'))
|
||||
.then(_ => installAll(ctx,
|
||||
fulldata.dependencies,
|
||||
fulldata.optionalDependencies,
|
||||
join(paths.target, '_', 'node_modules'),
|
||||
{
|
||||
keypath: pkg.keypath.concat([ pkg.fullname ]),
|
||||
dependent: pkg.fullname,
|
||||
parentRoot: pkg.root,
|
||||
optional: pkg.optional,
|
||||
ignoreScripts: pkg.ignoreScripts
|
||||
}))
|
||||
|
||||
// symlink itself; . -> node_modules/lodash@4.0.0
|
||||
// this way it can require itself
|
||||
.then(_ => symlinkSelf(paths.target, fulldata, pkg.keypath.length))
|
||||
|
||||
.then(_ => {
|
||||
ctx.piq = ctx.piq || []
|
||||
ctx.piq.push({
|
||||
path: paths.target,
|
||||
pkgFullname: pkg.fullname
|
||||
})
|
||||
// recurse down to dependencies
|
||||
log('dependencies')
|
||||
await installAll(ctx,
|
||||
fulldata.dependencies,
|
||||
fulldata.optionalDependencies,
|
||||
join(paths.target, '_', 'node_modules'),
|
||||
{
|
||||
keypath: pkg.keypath.concat([ pkg.fullname ]),
|
||||
dependent: pkg.fullname,
|
||||
parentRoot: pkg.root,
|
||||
optional: pkg.optional,
|
||||
ignoreScripts: pkg.ignoreScripts
|
||||
})
|
||||
|
||||
// symlink itself; . -> node_modules/lodash@4.0.0
|
||||
// this way it can require itself
|
||||
await symlinkSelf(paths.target, fulldata, pkg.keypath.length)
|
||||
|
||||
ctx.piq = ctx.piq || []
|
||||
ctx.piq.push({
|
||||
path: paths.target,
|
||||
pkgFullname: pkg.fullname
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -276,16 +273,15 @@ function buildInStore (ctx: InstallContext, paths: InstallationPaths, pkg: Packa
|
||||
* require('babel-runtime') within itself.
|
||||
*/
|
||||
|
||||
function symlinkSelf (target: string, pkg: Package, depth: number) {
|
||||
async function symlinkSelf (target: string, pkg: Package, depth: number) {
|
||||
debug(`symlinkSelf ${pkg.name}`)
|
||||
if (depth === 0) {
|
||||
return Promise.resolve()
|
||||
} else {
|
||||
return mkdirp(join(target, 'node_modules'))
|
||||
.then(_ => relSymlink(
|
||||
join('..', '_'),
|
||||
join(target, 'node_modules', escapeName(pkg.name))))
|
||||
return
|
||||
}
|
||||
await mkdirp(join(target, 'node_modules'))
|
||||
await relSymlink(
|
||||
join('..', '_'),
|
||||
join(target, 'node_modules', escapeName(pkg.name)))
|
||||
}
|
||||
|
||||
function escapeName (name: string) {
|
||||
@@ -300,7 +296,7 @@ function escapeName (name: string) {
|
||||
* symlinkToModules(fullname, modules)
|
||||
*/
|
||||
|
||||
function symlinkToModules (target: string, modules: string) {
|
||||
async function symlinkToModules (target: string, modules: string) {
|
||||
// TODO: uncomment to make things fail
|
||||
const pkgData = requireJson(join(target, 'package.json'))
|
||||
if (!pkgData.name) { throw new Error('Invalid package.json for ' + target) }
|
||||
@@ -308,8 +304,8 @@ function symlinkToModules (target: string, modules: string) {
|
||||
// lodash -> .store/lodash@4.0.0
|
||||
// .store/foo@1.0.0/node_modules/lodash -> ../../../.store/lodash@4.0.0
|
||||
const out = join(modules, pkgData.name)
|
||||
return mkdirp(dirname(out))
|
||||
.then(_ => relSymlink(target, out))
|
||||
await mkdirp(dirname(out))
|
||||
await relSymlink(target, out)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -317,12 +313,13 @@ function symlinkToModules (target: string, modules: string) {
|
||||
* If it exists, don't do anything.
|
||||
*/
|
||||
|
||||
function make (path: string, fn: Function) {
|
||||
return fs.stat(path)
|
||||
.catch((err: NodeJS.ErrnoException) => {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
return fn()
|
||||
})
|
||||
async function make (path: string, fn: Function) {
|
||||
try {
|
||||
await fs.stat(path)
|
||||
} catch (err) {
|
||||
if ((<NodeJS.ErrnoException>err).code !== 'ENOENT') throw err
|
||||
return fn()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -15,20 +15,20 @@ import {Package} from '../api/init_cmd'
|
||||
* isAvailable(spec, 'path/to/node_modules')
|
||||
*/
|
||||
|
||||
export default function isAvailable (spec: PackageSpec, modules: string) {
|
||||
export default async function isAvailable (spec: PackageSpec, modules: string) {
|
||||
const name = spec && spec.name
|
||||
if (!name) return Promise.resolve(false)
|
||||
if (!name) return false
|
||||
|
||||
const packageJsonPath = path.join(modules, name, 'package.json')
|
||||
|
||||
return Promise.resolve()
|
||||
.then(_ => fs.readFile(packageJsonPath))
|
||||
.then(_ => JSON.parse(_))
|
||||
.then(_ => verify(spec, _))
|
||||
.catch((err: NodeJS.ErrnoException) => {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
return false
|
||||
})
|
||||
try {
|
||||
const content = await fs.readFile(packageJsonPath)
|
||||
const pkgJson = JSON.parse(content)
|
||||
return verify(spec, pkgJson)
|
||||
} catch (err) {
|
||||
if ((<NodeJS.ErrnoException>err).code !== 'ENOENT') throw err
|
||||
return false
|
||||
}
|
||||
|
||||
function verify (spec: PackageSpec, packageJson: Package) {
|
||||
return packageJson.name === spec.name &&
|
||||
|
||||
@@ -52,38 +52,38 @@ function isScopedPkgsDir (dirPath: string) {
|
||||
* // node_modules/.bin/rimraf -> ../.store/rimraf@2.5.1/cmd.js
|
||||
*/
|
||||
|
||||
export function linkPkgBins (modules: string, target: string) {
|
||||
export async function linkPkgBins (modules: string, target: string) {
|
||||
const pkg = tryRequire(path.join(target, 'package.json'))
|
||||
|
||||
if (!pkg || !pkg.bin) return Promise.resolve()
|
||||
if (!pkg || !pkg.bin) return
|
||||
|
||||
const bins = binify(pkg)
|
||||
const binDir = path.join(modules, '.bin')
|
||||
|
||||
return mkdirp(binDir)
|
||||
.then(() => Promise.all(Object.keys(bins).map(bin => {
|
||||
const actualBin = bins[bin]
|
||||
const externalBinPath = path.join(binDir, bin)
|
||||
|
||||
const targetPath = normalizePath(path.join(pkg.name, actualBin))
|
||||
if (isWindows) {
|
||||
if (!preserveSymlinks) {
|
||||
return cmdShim(externalBinPath, '../' + targetPath)
|
||||
}
|
||||
const proxyFilePath = path.join(binDir, bin + '.proxy')
|
||||
fs.writeFileSync(proxyFilePath, 'require("../' + targetPath + '")', 'utf8')
|
||||
return cmdShim(externalBinPath, path.relative(binDir, proxyFilePath))
|
||||
}
|
||||
await mkdirp(binDir)
|
||||
await Promise.all(Object.keys(bins).map(async function (bin) {
|
||||
const actualBin = bins[bin]
|
||||
const externalBinPath = path.join(binDir, bin)
|
||||
|
||||
const targetPath = normalizePath(path.join(pkg.name, actualBin))
|
||||
if (isWindows) {
|
||||
if (!preserveSymlinks) {
|
||||
return makeExecutable(path.join(target, actualBin))
|
||||
.then(() => relSymlink(
|
||||
path.join(target, actualBin),
|
||||
externalBinPath))
|
||||
return cmdShim(externalBinPath, '../' + targetPath)
|
||||
}
|
||||
const proxyFilePath = path.join(binDir, bin + '.proxy')
|
||||
fs.writeFileSync(proxyFilePath, 'require("../' + targetPath + '")', 'utf8')
|
||||
return cmdShim(externalBinPath, path.relative(binDir, proxyFilePath))
|
||||
}
|
||||
|
||||
return proxy(externalBinPath, targetPath)
|
||||
})))
|
||||
if (!preserveSymlinks) {
|
||||
await makeExecutable(path.join(target, actualBin))
|
||||
return relSymlink(
|
||||
path.join(target, actualBin),
|
||||
externalBinPath)
|
||||
}
|
||||
|
||||
return proxy(externalBinPath, targetPath)
|
||||
}))
|
||||
}
|
||||
|
||||
function makeExecutable (filePath: string) {
|
||||
|
||||
@@ -18,11 +18,12 @@ function symlinkBundledDep (nodeModules: string, submod: string) {
|
||||
return linkBins(nodeModules)
|
||||
}
|
||||
|
||||
function isDir (path: string, fn: () => Promise<any>) {
|
||||
return fs.stat(path)
|
||||
.then((stat: Stats) => {
|
||||
if (!stat.isDirectory()) return Promise.resolve()
|
||||
async function isDir (path: string, fn: () => Promise<any>) {
|
||||
try {
|
||||
const stat = await fs.stat(path)
|
||||
if (!stat.isDirectory()) return
|
||||
return fn()
|
||||
})
|
||||
.catch((err: NodeJS.ErrnoException) => { if (err.code !== 'ENOENT') throw err })
|
||||
} catch (err) {
|
||||
if ((<NodeJS.ErrnoException>err).code !== 'ENOENT') throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ import {InstalledPackages} from '../api/install'
|
||||
* Links into `.store/node_modules`
|
||||
*/
|
||||
|
||||
export default function linkPeers (store: string, installs: InstalledPackages) {
|
||||
if (!installs) return Promise.resolve()
|
||||
export default async function linkPeers (store: string, installs: InstalledPackages) {
|
||||
if (!installs) return
|
||||
const peers = {}
|
||||
const roots = {}
|
||||
|
||||
@@ -32,15 +32,15 @@ export default function linkPeers (store: string, installs: InstalledPackages) {
|
||||
})
|
||||
|
||||
const modules = path.join(store, 'node_modules')
|
||||
return mkdirp(modules)
|
||||
.then(_ => Promise.all(Object.keys(roots).map(name => {
|
||||
return unsymlink(path.join(modules, roots[name].name))
|
||||
})))
|
||||
.then(_ => Promise.all(Object.keys(peers).map(name =>
|
||||
unsymlink(path.join(modules, peers[name].spec.escapedName))
|
||||
.then(() =>
|
||||
relSymlink(
|
||||
path.join(store, peers[name].fullname, '_'),
|
||||
path.join(modules, peers[name].spec.escapedName)))
|
||||
)))
|
||||
await mkdirp(modules)
|
||||
await Promise.all(Object.keys(roots).map(name => {
|
||||
return unsymlink(path.join(modules, roots[name].name))
|
||||
}))
|
||||
|
||||
await Promise.all(Object.keys(peers).map(async function (name) {
|
||||
await unsymlink(path.join(modules, peers[name].spec.escapedName))
|
||||
return relSymlink(
|
||||
path.join(store, peers[name].fullname, '_'),
|
||||
path.join(modules, peers[name].spec.escapedName))
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -5,23 +5,26 @@ import fs = require('mz/fs')
|
||||
import runScript from '../run_script'
|
||||
import requireJson from '../fs/require_json'
|
||||
|
||||
export default function postInstall (root_: string, log: Function) {
|
||||
export default async function postInstall (root_: string, log: Function) {
|
||||
const root = path.join(root_, '_')
|
||||
const pkg = requireJson(path.join(root, 'package.json'))
|
||||
debug('postinstall', pkg.name + '@' + pkg.version)
|
||||
const scripts = pkg && pkg.scripts || {}
|
||||
return Promise.resolve()
|
||||
.then(_ => !scripts['install'] && checkBindingGyp(root, log))
|
||||
.then(_ => {
|
||||
if (scripts['install']) {
|
||||
return npmRunScript('install')
|
||||
}
|
||||
return npmRunScript('preinstall')
|
||||
.then(_ => npmRunScript('postinstall'))
|
||||
})
|
||||
|
||||
function npmRunScript (scriptName: string) {
|
||||
if (!scripts[scriptName]) return Promise.resolve()
|
||||
if (!scripts['install']) {
|
||||
await checkBindingGyp(root, log)
|
||||
}
|
||||
|
||||
if (scripts['install']) {
|
||||
await npmRunScript('install')
|
||||
return
|
||||
}
|
||||
await npmRunScript('preinstall')
|
||||
await npmRunScript('postinstall')
|
||||
return
|
||||
|
||||
async function npmRunScript (scriptName: string) {
|
||||
if (!scripts[scriptName]) return
|
||||
return runScript('npm', ['run', scriptName], { cwd: root, log })
|
||||
}
|
||||
}
|
||||
@@ -31,10 +34,11 @@ export default function postInstall (root_: string, log: Function) {
|
||||
* `install` script (see `npm help scripts`).
|
||||
*/
|
||||
|
||||
function checkBindingGyp (root: string, log: Function) {
|
||||
return fs.stat(path.join(root, 'binding.gyp'))
|
||||
.then(() => runScript('node-gyp', ['rebuild'], { cwd: root, log }))
|
||||
.catch((err: NodeJS.ErrnoException) => {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
})
|
||||
async function checkBindingGyp (root: string, log: Function) {
|
||||
try {
|
||||
await fs.stat(path.join(root, 'binding.gyp'))
|
||||
await runScript('node-gyp', ['rebuild'], { cwd: root, log })
|
||||
} catch (err) {
|
||||
if ((<NodeJS.ErrnoException>err).code !== 'ENOENT') throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,8 +31,9 @@ export default function installMultiple (ctx: InstallContext, requiredPkgsMap: D
|
||||
ctx.dependents = ctx.dependents || {}
|
||||
ctx.dependencies = ctx.dependencies || {}
|
||||
|
||||
return Promise.all(optionalPkgs.concat(requiredPkgs).map(pkg => install(ctx, pkg, modules, options)
|
||||
.then((dependency: PackageContext) => {
|
||||
return Promise.all(optionalPkgs.concat(requiredPkgs).map(async function (pkg) {
|
||||
try {
|
||||
const dependency = await install(ctx, pkg, modules, options)
|
||||
const depFullName = pkgFullName(dependency)
|
||||
ctx.dependents[depFullName] = ctx.dependents[depFullName] || []
|
||||
if (ctx.dependents[depFullName].indexOf(options.dependent) === -1) {
|
||||
@@ -43,15 +44,15 @@ export default function installMultiple (ctx: InstallContext, requiredPkgsMap: D
|
||||
ctx.dependencies[options.dependent].push(depFullName)
|
||||
}
|
||||
return dependency
|
||||
})
|
||||
.catch(err => {
|
||||
} catch (err) {
|
||||
if (pkg.optional) {
|
||||
console.log('Skipping failed optional dependency ' + pkg.rawSpec + ':')
|
||||
console.log(err.message || err)
|
||||
return
|
||||
return null // is it OK to return null?
|
||||
}
|
||||
throw err
|
||||
})))
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
function pkgMeta (name: string, version: string, optional: boolean) {
|
||||
|
||||
@@ -29,7 +29,7 @@ export type GetFunc = (url: string, options?: GotOptions) => Promise<HttpRespons
|
||||
export type Got = {
|
||||
get: GetFunc,
|
||||
getStream: (url: string, options?: GotOptions) => Promise<NodeJS.ReadableStream>,
|
||||
getJSON: (url: string) => Promise<string>
|
||||
getJSON<T>(url: string): Promise<T>
|
||||
}
|
||||
|
||||
export default (opts: GotOptions): Got => {
|
||||
|
||||
@@ -2,10 +2,10 @@ import requireJson from './fs/require_json'
|
||||
import writeJson from './fs/write_json'
|
||||
import {DependenciesType} from './get_save_type'
|
||||
|
||||
export default (pkgJsonPath: string, removedPackages: string[], saveType: DependenciesType) => {
|
||||
export default async function (pkgJsonPath: string, removedPackages: string[], saveType: DependenciesType) {
|
||||
const packageJson = requireJson(pkgJsonPath)
|
||||
packageJson[saveType] = packageJson[saveType]
|
||||
if (!packageJson[saveType]) return Promise.resolve()
|
||||
if (!packageJson[saveType]) return
|
||||
|
||||
removedPackages.forEach(dependency => {
|
||||
delete packageJson[saveType][dependency]
|
||||
|
||||
@@ -3,6 +3,7 @@ import resolveTarball from './resolve/tarball'
|
||||
import resolveGithub from './resolve/github'
|
||||
import resolveLocal from './resolve/local'
|
||||
import {PackageSpec} from './install'
|
||||
import {Got} from './network/got'
|
||||
|
||||
export type PackageDist = {
|
||||
local: boolean,
|
||||
@@ -16,7 +17,7 @@ export type ResolveResult = {
|
||||
fullname: string,
|
||||
version: string,
|
||||
dist: PackageDist,
|
||||
root: string
|
||||
root?: string
|
||||
}
|
||||
|
||||
export type PackageToResolve = PackageSpec & {
|
||||
@@ -25,7 +26,7 @@ export type PackageToResolve = PackageSpec & {
|
||||
|
||||
export type ResolveOptions = {
|
||||
log(msg: string): void,
|
||||
got: any
|
||||
got: Got
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,52 +8,50 @@ import {Package} from '../api/init_cmd'
|
||||
|
||||
const PARSE_GITHUB_RE = /^github:([^\/]+)\/([^#]+)(#(.+))?$/
|
||||
|
||||
export default function resolveGithub (pkg: PackageToResolve, opts: ResolveOptions) {
|
||||
export default async function resolveGithub (pkg: PackageToResolve, opts: ResolveOptions) {
|
||||
const getJSON = opts.got.getJSON
|
||||
const spec = parseGithubSpec(pkg)
|
||||
return resolveRef(spec).then((ref: string) => {
|
||||
spec.ref = ref
|
||||
return resolvePackageJson(spec).then((pkg: Package) => ({
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
fullname: pkgFullName({
|
||||
name: pkg.name,
|
||||
version: ['github', spec.owner, spec.repo, spec.ref].join(delimiter)
|
||||
}),
|
||||
dist: {
|
||||
tarball: [
|
||||
'https://api.github.com/repos',
|
||||
spec.owner,
|
||||
spec.repo,
|
||||
'tarball',
|
||||
spec.ref
|
||||
].join('/')
|
||||
}
|
||||
}))
|
||||
})
|
||||
spec.ref = await resolveRef(spec)
|
||||
const resPkg: Package = await resolvePackageJson(spec)
|
||||
return {
|
||||
name: resPkg.name,
|
||||
version: resPkg.version,
|
||||
fullname: pkgFullName({
|
||||
name: resPkg.name,
|
||||
version: ['github', spec.owner, spec.repo, spec.ref].join(delimiter)
|
||||
}),
|
||||
dist: {
|
||||
tarball: [
|
||||
'https://api.github.com/repos',
|
||||
spec.owner,
|
||||
spec.repo,
|
||||
'tarball',
|
||||
spec.ref
|
||||
].join('/')
|
||||
}
|
||||
}
|
||||
|
||||
type GitHubContentResponse = {
|
||||
content: string
|
||||
}
|
||||
|
||||
function resolvePackageJson (spec: GitHubSpec) {
|
||||
async function resolvePackageJson (spec: GitHubSpec) {
|
||||
const url = [
|
||||
'https://api.github.com/repos',
|
||||
spec.owner,
|
||||
spec.repo,
|
||||
'contents/package.json?ref=' + spec.ref
|
||||
].join('/')
|
||||
return getJSON(url).then((body: GitHubContentResponse) => {
|
||||
const content = new Buffer(body.content, 'base64').toString('utf8')
|
||||
return JSON.parse(content)
|
||||
})
|
||||
const body = await getJSON<GitHubContentResponse>(url)
|
||||
const content = new Buffer(body.content, 'base64').toString('utf8')
|
||||
return JSON.parse(content)
|
||||
}
|
||||
|
||||
type GitHubRepoResponse = {
|
||||
sha: string
|
||||
}
|
||||
|
||||
function resolveRef (spec: GitHubSpec) {
|
||||
async function resolveRef (spec: GitHubSpec) {
|
||||
const url = [
|
||||
'https://api.github.com/repos',
|
||||
spec.owner,
|
||||
@@ -61,7 +59,8 @@ export default function resolveGithub (pkg: PackageToResolve, opts: ResolveOptio
|
||||
'commits',
|
||||
spec.ref
|
||||
].join('/')
|
||||
return getJSON(url).then((body: GitHubRepoResponse) => body.sha)
|
||||
const body = await getJSON<GitHubRepoResponse>(url)
|
||||
return body.sha
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ import {PackageToResolve} from '../resolve'
|
||||
* Resolves a package hosted on the local filesystem
|
||||
*/
|
||||
|
||||
export default function resolveLocal (pkg: PackageToResolve) {
|
||||
export default async function resolveLocal (pkg: PackageToResolve) {
|
||||
const dependencyPath = resolve(pkg.root, pkg.spec)
|
||||
|
||||
if (dependencyPath.slice(-4) === '.tgz' || dependencyPath.slice(-7) === '.tar.gz') {
|
||||
const name = getTarballName(dependencyPath)
|
||||
return Promise.resolve({
|
||||
return {
|
||||
name,
|
||||
fullname: pkgFullName({
|
||||
name,
|
||||
@@ -29,7 +29,7 @@ export default function resolveLocal (pkg: PackageToResolve) {
|
||||
local: true,
|
||||
tarball: dependencyPath
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return resolveFolder(dependencyPath)
|
||||
|
||||
@@ -3,7 +3,7 @@ const enc = encodeURIComponent
|
||||
import pkgFullName from '../pkg_full_name'
|
||||
import registryUrl = require('registry-url')
|
||||
import semver = require('semver')
|
||||
import {PackageToResolve, ResolveOptions, PackageDist} from '../resolve'
|
||||
import {PackageToResolve, ResolveOptions, PackageDist, ResolveResult} from '../resolve'
|
||||
import {Package} from '../api/init_cmd'
|
||||
|
||||
/**
|
||||
@@ -20,30 +20,26 @@ import {Package} from '../api/init_cmd'
|
||||
* })
|
||||
*/
|
||||
|
||||
export default function resolveNpm (pkg: PackageToResolve, opts: ResolveOptions) {
|
||||
export default async function resolveNpm (pkg: PackageToResolve, opts: ResolveOptions): Promise<ResolveResult> {
|
||||
// { raw: 'rimraf@2', scope: null, name: 'rimraf', rawSpec: '2' || '' }
|
||||
return Promise.resolve()
|
||||
.then(_ => toUri(pkg))
|
||||
.then(url => {
|
||||
if (opts.log) opts.log('resolving')
|
||||
return opts.got.get(url)
|
||||
})
|
||||
.then(res => JSON.parse(res.body))
|
||||
.then(res => pickVersionFromRegistryDocument(res, pkg))
|
||||
.then(res => ({
|
||||
name: res.name,
|
||||
fullname: pkgFullName(res),
|
||||
version: res.version, // used for displaying
|
||||
dist: res.dist
|
||||
}))
|
||||
.catch((err: Error) => errify(err, pkg))
|
||||
}
|
||||
|
||||
function errify (err: Error, pkg: PackageToResolve) {
|
||||
if (err['statusCode'] === 404) {
|
||||
throw new Error("Module '" + pkg.raw + "' not found")
|
||||
try {
|
||||
const url = toUri(pkg)
|
||||
if (opts.log) opts.log('resolving')
|
||||
const res = await opts.got.get(url)
|
||||
const parsedBody = JSON.parse(res.body)
|
||||
const correctPkg = pickVersionFromRegistryDocument(parsedBody, pkg)
|
||||
return {
|
||||
name: correctPkg.name,
|
||||
fullname: pkgFullName(correctPkg),
|
||||
version: correctPkg.version, // used for displaying
|
||||
dist: correctPkg.dist
|
||||
}
|
||||
} catch (err) {
|
||||
if (err['statusCode'] === 404) {
|
||||
throw new Error("Module '" + pkg.raw + "' not found")
|
||||
}
|
||||
throw err
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
type StringDict = {
|
||||
|
||||
Reference in New Issue
Block a user