mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-24 18:11:39 -04:00
fix: the tag config is used only for top-level named installs
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
"@types/load-json-file": "2.0.5",
|
||||
"@types/mz": "0.0.30",
|
||||
"@types/node": "7.0.5",
|
||||
"@types/ramda": "0.0.3",
|
||||
"@types/rc": "0.0.1",
|
||||
"@types/semver": "5.3.30",
|
||||
"@types/update-notifier": "1.0.0",
|
||||
@@ -70,6 +71,7 @@
|
||||
"pnpm-install-checks": "1.1.0",
|
||||
"pnpm-logger": "0.2.0",
|
||||
"proper-lockfile": "2.0.0",
|
||||
"ramda": "0.23.0",
|
||||
"rc": "1.1.7",
|
||||
"read-pkg": "2.0.0",
|
||||
"read-pkg-up": "2.0.0",
|
||||
|
||||
@@ -32,6 +32,7 @@ import createMemoize, {MemoizedFunc} from '../memoize'
|
||||
import linkBins from '../install/linkBins'
|
||||
import {Package} from '../types'
|
||||
import {PackageSpec} from '../resolve'
|
||||
import depsToSpecs from '../depsToSpecs'
|
||||
|
||||
export type InstalledPackages = {
|
||||
[name: string]: InstalledPackage
|
||||
@@ -53,12 +54,18 @@ export async function install (maybeOpts?: PnpmOptions) {
|
||||
const installCtx = await createInstallCmd(opts, ctx.graph, ctx.shrinkwrap)
|
||||
|
||||
if (!ctx.pkg) throw new Error('No package.json found')
|
||||
const packagesToInstall = Object.assign({}, ctx.pkg.dependencies || {})
|
||||
if (!opts.production) Object.assign(packagesToInstall, ctx.pkg.devDependencies || {})
|
||||
const optionalDeps = ctx.pkg.optionalDependencies || {}
|
||||
const depsToInstall = Object.assign(
|
||||
{},
|
||||
!opts.production && ctx.pkg.devDependencies,
|
||||
optionalDeps,
|
||||
ctx.pkg.dependencies
|
||||
)
|
||||
const specs = depsToSpecs(depsToInstall)
|
||||
|
||||
return lock(
|
||||
ctx.storePath,
|
||||
() => installInContext('general', packagesToInstall, ctx, installCtx, opts),
|
||||
() => installInContext('general', specs, Object.keys(optionalDeps), ctx, installCtx, opts),
|
||||
{stale: opts.lockStaleDuration}
|
||||
)
|
||||
}
|
||||
@@ -70,46 +77,65 @@ export async function install (maybeOpts?: PnpmOptions) {
|
||||
* install({'lodash': '1.0.0', 'foo': '^2.1.0' }, { silent: true })
|
||||
*/
|
||||
export async function installPkgs (fuzzyDeps: string[] | Dependencies, maybeOpts?: PnpmOptions) {
|
||||
let packagesToInstall = mapify(fuzzyDeps)
|
||||
const opts = extendOptions(maybeOpts)
|
||||
let packagesToInstall = Array.isArray(fuzzyDeps)
|
||||
? argsToSpecs(fuzzyDeps, opts.tag)
|
||||
: depsToSpecs(fuzzyDeps)
|
||||
|
||||
if (!Object.keys(packagesToInstall).length) {
|
||||
throw new Error('At least one package has to be installed')
|
||||
}
|
||||
const opts = extendOptions(maybeOpts)
|
||||
const ctx = await getContext(opts)
|
||||
const installCtx = await createInstallCmd(opts, ctx.graph, ctx.shrinkwrap)
|
||||
|
||||
return lock(
|
||||
ctx.storePath,
|
||||
() => installInContext('named', packagesToInstall, ctx, installCtx, opts),
|
||||
() => installInContext('named', packagesToInstall, [], ctx, installCtx, opts),
|
||||
{stale: opts.lockStaleDuration}
|
||||
)
|
||||
}
|
||||
|
||||
function argsToSpecs (args: string[], defaultTag: string): PackageSpec[] {
|
||||
return args
|
||||
.map(arg => npa(arg))
|
||||
.map(spec => {
|
||||
if (spec.type === 'tag' && !spec.raw.endsWith('@latest')) {
|
||||
spec.spec = defaultTag
|
||||
}
|
||||
return spec
|
||||
})
|
||||
}
|
||||
|
||||
function getResolutions(
|
||||
packagesToInstall: Dependencies,
|
||||
packagesToInstall: PackageSpec[],
|
||||
resolvedSpecDeps: ResolvedDependencies
|
||||
): ResolvedDependencies {
|
||||
return Object.keys(packagesToInstall)
|
||||
.reduce((resolvedDeps, depName) => {
|
||||
const spec = `${depName}@${packagesToInstall[depName]}`
|
||||
if (resolvedSpecDeps[spec]) {
|
||||
resolvedDeps[depName] = resolvedSpecDeps[spec]
|
||||
return packagesToInstall
|
||||
.reduce((resolvedDeps, depSpec) => {
|
||||
if (resolvedSpecDeps[depSpec.raw]) {
|
||||
resolvedDeps[depSpec.name] = resolvedSpecDeps[depSpec.raw]
|
||||
}
|
||||
return resolvedDeps
|
||||
}, {})
|
||||
}
|
||||
|
||||
async function installInContext (installType: string, packagesToInstall: Dependencies, ctx: PnpmContext, installCtx: InstallContext, opts: StrictPnpmOptions) {
|
||||
async function installInContext (
|
||||
installType: string,
|
||||
packagesToInstall: PackageSpec[],
|
||||
optionalDependencies: string[],
|
||||
ctx: PnpmContext,
|
||||
installCtx: InstallContext,
|
||||
opts: StrictPnpmOptions
|
||||
) {
|
||||
// TODO: ctx.graph should not be muted. installMultiple should return a new graph
|
||||
const oldGraph: Graph = cloneDeep(ctx.graph)
|
||||
const nodeModulesPath = path.join(ctx.root, 'node_modules')
|
||||
const client = new RegClient(adaptConfig(opts))
|
||||
|
||||
const optionalDependencies = ctx.pkg && ctx.pkg && ctx.pkg.optionalDependencies || {}
|
||||
const resolvedDependencies: ResolvedDependencies | undefined = installType !== 'general'
|
||||
? undefined
|
||||
: getResolutions(
|
||||
Object.assign({}, optionalDependencies, packagesToInstall),
|
||||
packagesToInstall,
|
||||
ctx.shrinkwrap.dependencies
|
||||
)
|
||||
const installOpts = {
|
||||
@@ -118,7 +144,6 @@ async function installInContext (installType: string, packagesToInstall: Depende
|
||||
localRegistry: opts.localRegistry,
|
||||
force: opts.force,
|
||||
depth: opts.depth,
|
||||
tag: opts.tag,
|
||||
engineStrict: opts.engineStrict,
|
||||
nodeVersion: opts.nodeVersion,
|
||||
got: createGot(client, {networkConcurrency: opts.networkConcurrency}),
|
||||
@@ -143,10 +168,8 @@ async function installInContext (installType: string, packagesToInstall: Depende
|
||||
if (!ctx.pkg) {
|
||||
throw new Error('Cannot save because no package.json found')
|
||||
}
|
||||
const inputNames = Object.keys(packagesToInstall)
|
||||
const savedPackages = pkgs.filter((pkg: InstalledPackage) => inputNames.indexOf(pkg.pkg.name) > -1)
|
||||
const pkgJsonPath = path.join(ctx.root, 'package.json')
|
||||
newPkg = await save(pkgJsonPath, savedPackages, saveType, opts.saveExact)
|
||||
newPkg = await save(pkgJsonPath, pkgs.map(pkg => pkg.pkg), saveType, opts.saveExact)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,19 +338,3 @@ function installLogger (pkgId: string) {
|
||||
lifecycleLogger[logLevel]({pkgId, line})
|
||||
}
|
||||
}
|
||||
|
||||
function mapify (pkgs: string[] | Dependencies): Dependencies {
|
||||
if (!pkgs) return {}
|
||||
if (Array.isArray(pkgs)) {
|
||||
return pkgs.reduce((pkgsMap: Dependencies, pkgRequest: string) => {
|
||||
const matches = /(@?[^@]+)@(.*)/.exec(pkgRequest)
|
||||
if (!matches) {
|
||||
pkgsMap[pkgRequest] = '*'
|
||||
} else {
|
||||
pkgsMap[matches[1]] = matches[2]
|
||||
}
|
||||
return pkgsMap
|
||||
}, {})
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
9
src/depsToSpecs.ts
Normal file
9
src/depsToSpecs.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import npa = require('npm-package-arg')
|
||||
import {Dependencies} from './types'
|
||||
import {PackageSpec} from './resolve'
|
||||
|
||||
export default function (deps: Dependencies): PackageSpec[] {
|
||||
if (!deps) return []
|
||||
const pkgs = Object.keys(deps).map(pkgName => `${pkgName}@${deps[pkgName]}`)
|
||||
return <PackageSpec[]>pkgs.map(npa)
|
||||
}
|
||||
@@ -37,7 +37,6 @@ export default async function fetch (
|
||||
storePath: string,
|
||||
localRegistry: string,
|
||||
metaCache: Map<string, PackageMeta>,
|
||||
tag: string,
|
||||
got: Got,
|
||||
update?: boolean,
|
||||
shrinkwrapResolution?: Resolution,
|
||||
@@ -55,7 +54,6 @@ export default async function fetch (
|
||||
loggedPkg: options.loggedPkg,
|
||||
root: options.root,
|
||||
got: options.got,
|
||||
tag: options.tag,
|
||||
localRegistry: options.localRegistry,
|
||||
metaCache: options.metaCache,
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path = require('path')
|
||||
import npa = require('npm-package-arg')
|
||||
import logger from 'pnpm-logger'
|
||||
import pFilter = require('p-filter')
|
||||
import R = require('ramda')
|
||||
import fetch, {FetchedPackage} from './fetch'
|
||||
import {InstallContext, InstalledPackages} from '../api/install'
|
||||
import {Dependencies} from '../types'
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
import {PackageSpec, PackageMeta} from '../resolve'
|
||||
import linkBins from '../install/linkBins'
|
||||
import getLinkTarget = require('get-link-target')
|
||||
import depsToSpecs from '../depsToSpecs'
|
||||
|
||||
const installCheckLogger = logger('install-check')
|
||||
|
||||
@@ -38,8 +39,8 @@ export type InstalledPackage = {
|
||||
|
||||
export default async function installAll (
|
||||
ctx: InstallContext,
|
||||
dependencies: Dependencies,
|
||||
optionalDependencies: Dependencies,
|
||||
specs: PackageSpec[],
|
||||
optionalDependencies: string[],
|
||||
modules: string,
|
||||
options: {
|
||||
force: boolean,
|
||||
@@ -47,7 +48,6 @@ export default async function installAll (
|
||||
storePath: string,
|
||||
localRegistry: string,
|
||||
metaCache: Map<string, PackageMeta>,
|
||||
tag: string,
|
||||
got: Got,
|
||||
keypath?: string[],
|
||||
resolvedDependencies?: ResolvedDependencies,
|
||||
@@ -59,16 +59,13 @@ export default async function installAll (
|
||||
): Promise<InstalledPackage[]> {
|
||||
const keypath = options.keypath || []
|
||||
|
||||
const nonOptionalDependencies = Object.keys(dependencies)
|
||||
.filter(depName => !optionalDependencies[depName])
|
||||
.reduce((nonOptionalDependencies, depName) => {
|
||||
nonOptionalDependencies[depName] = dependencies[depName]
|
||||
return nonOptionalDependencies
|
||||
}, {})
|
||||
const specGroups = R.partition((spec: PackageSpec) => !!spec.name && R.contains(spec.name, optionalDependencies), specs)
|
||||
const optionalDepSpecs = specGroups[0]
|
||||
const nonOptionalDepSpecs = specGroups[1]
|
||||
|
||||
const installedPkgs: InstalledPackage[] = Array.prototype.concat.apply([], await Promise.all([
|
||||
installMultiple(ctx, nonOptionalDependencies, modules, Object.assign({}, options, {optional: false, keypath})),
|
||||
installMultiple(ctx, optionalDependencies, modules, Object.assign({}, options, {optional: true, keypath})),
|
||||
installMultiple(ctx, nonOptionalDepSpecs, modules, Object.assign({}, options, {optional: false, keypath})),
|
||||
installMultiple(ctx, optionalDepSpecs, modules, Object.assign({}, options, {optional: true, keypath})),
|
||||
]))
|
||||
|
||||
await Promise.all(
|
||||
@@ -84,7 +81,7 @@ export default async function installAll (
|
||||
|
||||
async function installMultiple (
|
||||
ctx: InstallContext,
|
||||
pkgsMap: Dependencies,
|
||||
specs: PackageSpec[],
|
||||
modules: string,
|
||||
options: {
|
||||
force: boolean,
|
||||
@@ -92,7 +89,6 @@ async function installMultiple (
|
||||
storePath: string,
|
||||
localRegistry: string,
|
||||
metaCache: Map<string, PackageMeta>,
|
||||
tag: string,
|
||||
got: Got,
|
||||
keypath: string[],
|
||||
resolvedDependencies?: ResolvedDependencies,
|
||||
@@ -103,14 +99,8 @@ async function installMultiple (
|
||||
baseNodeModules: string,
|
||||
}
|
||||
): Promise<InstalledPackage[]> {
|
||||
pkgsMap = pkgsMap || {}
|
||||
|
||||
const pkgs = Object.keys(pkgsMap).map(pkgName => getRawSpec(pkgName, pkgsMap[pkgName]))
|
||||
|
||||
ctx.graph = ctx.graph || {}
|
||||
|
||||
const specs = pkgs.map(npa)
|
||||
|
||||
const nonLinkedPkgs = modules === options.baseNodeModules
|
||||
// only check modules on the first level
|
||||
? await pFilter(specs, (spec: PackageSpec) => !spec.name || isInnerLink(modules, spec.name))
|
||||
@@ -176,7 +166,6 @@ async function install (
|
||||
storePath: string,
|
||||
localRegistry: string,
|
||||
metaCache: Map<string, PackageMeta>,
|
||||
tag: string,
|
||||
got: Got,
|
||||
keypath: string[],
|
||||
pkgId?: string,
|
||||
@@ -365,7 +354,6 @@ async function installDependencies (
|
||||
storePath: string,
|
||||
localRegistry: string,
|
||||
metaCache: Map<string, PackageMeta>,
|
||||
tag: string,
|
||||
got: Got,
|
||||
keypath: string[],
|
||||
resolvedDependencies?: ResolvedDependencies,
|
||||
@@ -383,8 +371,8 @@ async function installDependencies (
|
||||
|
||||
const bundledDeps = pkg.bundleDependencies || pkg.bundledDependencies || []
|
||||
const filterDeps = getNotBundledDeps.bind(null, bundledDeps)
|
||||
const deps = filterDeps(pkg.dependencies || {})
|
||||
const optionalDeps = filterDeps(pkg.optionalDependencies || {})
|
||||
const deps = depsToSpecs(filterDeps(Object.assign({}, pkg.optionalDependencies, pkg.dependencies)))
|
||||
const optionalDeps = Object.keys(pkg.optionalDependencies || {})
|
||||
|
||||
const installedDeps: InstalledPackage[] = await installAll(ctx, deps, optionalDeps, modules, depsInstallOpts)
|
||||
|
||||
@@ -407,7 +395,3 @@ function addInstalledPkg (installs: InstalledPackages, newPkg: InstalledPackage)
|
||||
}
|
||||
installs[newPkg.id].optional = installs[newPkg.id].optional && newPkg.optional
|
||||
}
|
||||
|
||||
function getRawSpec (name: string, version: string) {
|
||||
return version === '*' ? name : `${name}@${version}`
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ export type ResolveOptions = {
|
||||
localRegistry: string,
|
||||
metaCache: Map<string, PackageMeta>,
|
||||
root: string,
|
||||
tag: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@ export default async function resolveNpm (spec: PackageSpec, opts: ResolveOption
|
||||
try {
|
||||
if (opts.loggedPkg) logStatus({ status: 'resolving', pkg: opts.loggedPkg })
|
||||
const meta = await loadPkgMeta(spec, opts.localRegistry, opts.got, opts.metaCache)
|
||||
const correctPkg = pickVersion(meta, spec, opts.tag)
|
||||
const correctPkg = pickVersion(meta, spec)
|
||||
if (!correctPkg) {
|
||||
const versions = Object.keys(meta.versions)
|
||||
const message = versions.length
|
||||
@@ -54,11 +54,11 @@ export default async function resolveNpm (spec: PackageSpec, opts: ResolveOption
|
||||
}
|
||||
}
|
||||
|
||||
function pickVersion (meta: PackageMeta, dep: PackageSpec, latestTag: string) {
|
||||
function pickVersion (meta: PackageMeta, dep: PackageSpec) {
|
||||
if (dep.type === 'tag') {
|
||||
return pickVersionByTag(meta, dep.spec)
|
||||
}
|
||||
return pickVersionByVersionRange(meta, dep.spec, latestTag)
|
||||
return pickVersionByVersionRange(meta, dep.spec)
|
||||
}
|
||||
|
||||
function pickVersionByTag(meta: PackageMeta, tag: string) {
|
||||
@@ -69,8 +69,8 @@ function pickVersionByTag(meta: PackageMeta, tag: string) {
|
||||
return null
|
||||
}
|
||||
|
||||
function pickVersionByVersionRange(meta: PackageMeta, versionRange: string, latestTag: string) {
|
||||
const latest = meta['dist-tags'][latestTag]
|
||||
function pickVersionByVersionRange(meta: PackageMeta, versionRange: string) {
|
||||
const latest = meta['dist-tags']['latest']
|
||||
if (semver.satisfies(latest, versionRange, true)) {
|
||||
return meta.versions[latest]
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import {ignoreCache as readPkg} from './fs/readPkg'
|
||||
import writePkg = require('write-pkg')
|
||||
import {DependenciesType} from './getSaveType'
|
||||
import {InstalledPackage} from './install/installMultiple'
|
||||
import {Package} from './types'
|
||||
|
||||
export default async function save (
|
||||
pkgJsonPath: string,
|
||||
installedPackages: InstalledPackage[],
|
||||
installedPackages: Package[],
|
||||
saveType: DependenciesType,
|
||||
useExactVersion: boolean
|
||||
): Promise<Package> {
|
||||
@@ -15,7 +14,7 @@ export default async function save (
|
||||
packageJson[saveType] = packageJson[saveType] || {}
|
||||
installedPackages.forEach(dependency => {
|
||||
const semverCharacter = useExactVersion ? '' : '^'
|
||||
packageJson[saveType][dependency.pkg.name] = semverCharacter + dependency.pkg.version
|
||||
packageJson[saveType][dependency.name] = semverCharacter + dependency.version
|
||||
})
|
||||
|
||||
await writePkg(pkgJsonPath, packageJson)
|
||||
|
||||
@@ -57,6 +57,18 @@ test('scoped modules without version spec (@rstacruz/tap-spec)', async function
|
||||
t.ok(typeof _ === 'function', 'tap-spec is available')
|
||||
})
|
||||
|
||||
test('modules without version spec, with custom tag config', async function (t) {
|
||||
const project = prepare(t)
|
||||
|
||||
const tag = 'beta'
|
||||
|
||||
await addDistTag('dep-of-pkg-with-1-dep', '100.0.0', tag)
|
||||
|
||||
await installPkgs(['dep-of-pkg-with-1-dep'], testDefaults({tag}))
|
||||
|
||||
await project.storeHas('dep-of-pkg-with-1-dep', '100.0.0')
|
||||
})
|
||||
|
||||
test('scoped modules with versions (@rstacruz/tap-spec@4.1.1)', async function (t) {
|
||||
const project = prepare(t)
|
||||
await installPkgs(['@rstacruz/tap-spec@4.1.1'], testDefaults())
|
||||
|
||||
Reference in New Issue
Block a user