mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-24 10:01:48 -04:00
@@ -40,6 +40,7 @@
|
||||
"dependencies": {
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/default-resolver": "workspace:6.0.3",
|
||||
"@pnpm/error": "workspace:1.0.0",
|
||||
"@pnpm/package-is-installable": "workspace:4.0.1",
|
||||
"@pnpm/read-importer-manifest": "workspace:2.0.1",
|
||||
"@pnpm/utils": "workspace:0.12.2",
|
||||
|
||||
@@ -8,6 +8,7 @@ export * from './createLatestManifestGetter'
|
||||
export * from './getPinnedVersion'
|
||||
export * from './packageIsInstallable'
|
||||
export * from './readImporterManifest'
|
||||
export * from './recursiveSummary'
|
||||
export * from './style'
|
||||
export * from './updateToLatestSpecsFromManifest'
|
||||
|
||||
|
||||
@@ -6,13 +6,11 @@ interface ActionFailure {
|
||||
error: Error,
|
||||
}
|
||||
|
||||
interface RecursiveSummary {
|
||||
export interface RecursiveSummary {
|
||||
fails: ActionFailure[],
|
||||
passes: number,
|
||||
}
|
||||
|
||||
export default RecursiveSummary
|
||||
|
||||
class RecursiveFailError extends PnpmError {
|
||||
public readonly fails: ActionFailure[]
|
||||
public readonly passes: number
|
||||
@@ -5,7 +5,7 @@ export type UniversalOptions = Pick<Config, 'color' | 'dir' | 'rawConfig' | 'raw
|
||||
export type WsPkg = {
|
||||
dir: string,
|
||||
manifest: ImporterManifest,
|
||||
writeImporterManifest: (manifest: ImporterManifest) => Promise<void>,
|
||||
writeImporterManifest (manifest: ImporterManifest, force?: boolean | undefined): Promise<void>
|
||||
}
|
||||
|
||||
export type WsPkgsGraph = Record<string, { dependencies: string[], package: WsPkg }>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"dependencies": {
|
||||
"@pnpm/error": "workspace:1.0.0",
|
||||
"@pnpm/find-workspace-packages": "workspace:2.0.8",
|
||||
"@pnpm/matcher": "workspace:1.0.0",
|
||||
"execa": "4.0.0",
|
||||
"find-up": "4.1.0",
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import findWorkspacePackages, { WsPkg } from '@pnpm/find-workspace-packages'
|
||||
import matcher from '@pnpm/matcher'
|
||||
import isSubdir = require('is-subdir')
|
||||
import createPkgGraph, { Package, PackageNode } from 'pkgs-graph'
|
||||
@@ -15,6 +16,21 @@ interface Graph {
|
||||
[nodeId: string]: string[],
|
||||
}
|
||||
|
||||
export async function readWsPkgs (
|
||||
workspaceDir: string,
|
||||
pkgSelectors: PackageSelector[],
|
||||
) {
|
||||
const allWsPkgs = await findWorkspacePackages(workspaceDir, {})
|
||||
const selectedWsPkgsGraph = await filterPkgsBySelectorObjects(
|
||||
allWsPkgs,
|
||||
pkgSelectors,
|
||||
{
|
||||
workspaceDir,
|
||||
},
|
||||
)
|
||||
return { allWsPkgs, selectedWsPkgsGraph }
|
||||
}
|
||||
|
||||
export async function filterPackages<T> (
|
||||
pkgs: Array<Package & T>,
|
||||
filter: string[],
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/cli-utils": "workspace:0.2.5",
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/constants": "workspace:3.0.0",
|
||||
"@pnpm/types": "workspace:4.0.0",
|
||||
"find-packages": "workspace:7.0.1",
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
import { packageIsInstallable } from '@pnpm/cli-utils'
|
||||
import { WsPkg } from '@pnpm/config'
|
||||
import { WORKSPACE_MANIFEST_FILENAME } from '@pnpm/constants'
|
||||
import { ImporterManifest } from '@pnpm/types'
|
||||
import findPackages from 'find-packages'
|
||||
import path = require('path')
|
||||
import readYamlFile from 'read-yaml-file'
|
||||
|
||||
interface WorkspaceDependencyPackage {
|
||||
dir: string
|
||||
manifest: ImporterManifest
|
||||
writeImporterManifest (manifest: ImporterManifest, force?: boolean | undefined): Promise<void>
|
||||
}
|
||||
export { WsPkg }
|
||||
|
||||
export default async (
|
||||
workspaceRoot: string,
|
||||
@@ -29,7 +25,7 @@ export default async (
|
||||
packageIsInstallable(pkg.dir, pkg.manifest, opts)
|
||||
}
|
||||
|
||||
return pkgs as WorkspaceDependencyPackage[]
|
||||
return pkgs as WsPkg[]
|
||||
}
|
||||
|
||||
async function requirePackagesManifest (dir: string): Promise<{packages?: string[]} | null> {
|
||||
@@ -44,7 +40,7 @@ async function requirePackagesManifest (dir: string): Promise<{packages?: string
|
||||
}
|
||||
|
||||
export function arrayOfWorkspacePackagesToMap (
|
||||
pkgs: WorkspaceDependencyPackage[],
|
||||
pkgs: WsPkg[],
|
||||
) {
|
||||
return pkgs.reduce((acc, pkg) => {
|
||||
if (!pkg.manifest.name || !pkg.manifest.version) return acc
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import findWorkspaceDir from '@pnpm/find-workspace-dir'
|
||||
import nopt = require('nopt')
|
||||
|
||||
const RECURSIVE_CMDS = new Set(['recursive', 'multi', 'm'])
|
||||
|
||||
export default async function parseCliArgs (
|
||||
opts: {
|
||||
getCommandLongName: (commandName: string) => string,
|
||||
@@ -40,28 +42,16 @@ export default async function parseCliArgs (
|
||||
}
|
||||
}
|
||||
|
||||
const types = (() => {
|
||||
if (opts.getCommandLongName(noptExploratoryResults.argv.remain[0]) === 'recursive') {
|
||||
return {
|
||||
...opts.globalOptionsTypes,
|
||||
...opts.getTypesByCommandName('recursive'),
|
||||
...opts.getTypesByCommandName(getCommandName(noptExploratoryResults.argv.remain.slice(1))),
|
||||
}
|
||||
}
|
||||
if (noptExploratoryResults['filter'] || noptExploratoryResults['recursive'] === true) {
|
||||
return {
|
||||
...opts.globalOptionsTypes,
|
||||
...opts.getTypesByCommandName('recursive'),
|
||||
...opts.getTypesByCommandName(getCommandName(noptExploratoryResults.argv.remain)),
|
||||
}
|
||||
}
|
||||
return {
|
||||
...opts.globalOptionsTypes,
|
||||
...opts.getTypesByCommandName(getCommandName(noptExploratoryResults.argv.remain)),
|
||||
}
|
||||
})() as any // tslint:disable-line:no-any
|
||||
const types = {
|
||||
'recursive': Boolean,
|
||||
...opts.globalOptionsTypes,
|
||||
...opts.getTypesByCommandName(getCommandName(noptExploratoryResults.argv.remain)),
|
||||
} as any // tslint:disable-line:no-any
|
||||
|
||||
function getCommandName (cliArgs: string[]) {
|
||||
if (RECURSIVE_CMDS.has(cliArgs[0])) {
|
||||
cliArgs = cliArgs.slice(1)
|
||||
}
|
||||
if (opts.getCommandLongName(cliArgs[0]) !== 'install' || cliArgs.length === 1) return cliArgs[0]
|
||||
return 'add'
|
||||
}
|
||||
@@ -77,9 +67,8 @@ export default async function parseCliArgs (
|
||||
}
|
||||
}
|
||||
|
||||
let cmd = opts.getCommandLongName(argv.remain[0])
|
||||
|| 'help'
|
||||
if (!opts.isKnownCommand(cmd)) {
|
||||
let cmd = opts.getCommandLongName(argv.remain[0]) ?? 'help'
|
||||
if (!opts.isKnownCommand(cmd) && !RECURSIVE_CMDS.has(cmd)) {
|
||||
cmd = 'help'
|
||||
}
|
||||
|
||||
@@ -88,10 +77,14 @@ export default async function parseCliArgs (
|
||||
// `pnpm install ""` is going to be just `pnpm install`
|
||||
const cliArgs = argv.remain.slice(1).filter(Boolean)
|
||||
|
||||
if (cmd !== 'recursive' && (cliConf['filter'] || cliConf['recursive'] === true)) {
|
||||
subCmd = cmd
|
||||
cmd = 'recursive'
|
||||
cliArgs.unshift(subCmd)
|
||||
if (cliConf['recursive'] !== true && (cliConf['filter'] || RECURSIVE_CMDS.has(cmd))) {
|
||||
cliConf['recursive'] = true
|
||||
if (subCmd && RECURSIVE_CMDS.has(cmd)) {
|
||||
cliArgs.shift()
|
||||
argv.remain.shift()
|
||||
cmd = subCmd
|
||||
subCmd = null
|
||||
}
|
||||
} else if (subCmd && !opts.isKnownCommand(subCmd)) {
|
||||
subCmd = null
|
||||
}
|
||||
@@ -105,15 +98,13 @@ export default async function parseCliArgs (
|
||||
typeof workspaceDir === 'string' &&
|
||||
cliArgs.length === 0
|
||||
) {
|
||||
subCmd = cmd
|
||||
cmd = 'recursive'
|
||||
cliArgs.unshift(subCmd)
|
||||
cliConf['recursive'] = true
|
||||
}
|
||||
|
||||
if (cmd === 'install' && cliArgs.length > 0) {
|
||||
cmd = 'add'
|
||||
} else if (subCmd === 'install' && cliArgs.length > 1) {
|
||||
subCmd = 'add'
|
||||
cmd = 'add'
|
||||
}
|
||||
|
||||
const allowedOptions = new Set(Object.keys(types))
|
||||
|
||||
@@ -13,59 +13,69 @@ const DEFAULT_OPTS = {
|
||||
}
|
||||
|
||||
test('a command is recursive if it has a --filter option', async (t) => {
|
||||
const { cmd, subCmd } = await parseCliArgs({
|
||||
const { cliConf, cmd } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
globalOptionsTypes: { filter: [String, Array] },
|
||||
}, ['--filter', 'foo', 'update'])
|
||||
t.equal(cmd, 'recursive')
|
||||
t.equal(subCmd, 'update')
|
||||
t.equal(cmd, 'update')
|
||||
t.ok(cliConf['recursive'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('a command is recursive if -r option is used', async (t) => {
|
||||
const { cmd, subCmd } = await parseCliArgs({
|
||||
const { cliConf, cmd } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
globalOptionsTypes: { recursive: Boolean },
|
||||
shortHands: { 'r': ['--recursive'] },
|
||||
}, ['-r', 'update'])
|
||||
t.equal(cmd, 'recursive')
|
||||
t.equal(subCmd, 'update')
|
||||
t.equal(cmd, 'update')
|
||||
t.ok(cliConf['recursive'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('a command is recursive if --recursive option is used', async (t) => {
|
||||
const { cmd, subCmd } = await parseCliArgs({
|
||||
const { cliConf, cmd } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
globalOptionsTypes: { recursive: Boolean },
|
||||
}, ['-r', 'update'])
|
||||
t.equal(cmd, 'recursive')
|
||||
t.equal(subCmd, 'update')
|
||||
t.equal(cmd, 'update')
|
||||
t.ok(cliConf['recursive'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('the install command is recursive when executed in a subdir of a workspace', async (t) => {
|
||||
const { cmd, subCmd, workspaceDir } = await parseCliArgs({
|
||||
const { cliConf, cmd, workspaceDir } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
globalOptionsTypes: { dir: String },
|
||||
}, ['--dir', __dirname, 'install'])
|
||||
t.equal(cmd, 'recursive')
|
||||
t.equal(subCmd, 'install')
|
||||
t.equal(cmd, 'install')
|
||||
t.ok(cliConf['recursive'])
|
||||
t.equal(workspaceDir, path.join(__dirname, '../../..'))
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('the install command is recursive when executed in the root of a workspace', async (t) => {
|
||||
const expectedWorkspaceDir = path.join(__dirname, '../../..')
|
||||
const { cmd, subCmd, workspaceDir } = await parseCliArgs({
|
||||
const { cliConf, cmd, workspaceDir } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
globalOptionsTypes: { dir: String },
|
||||
}, ['--dir', expectedWorkspaceDir, 'install'])
|
||||
t.equal(cmd, 'recursive')
|
||||
t.equal(subCmd, 'install')
|
||||
t.equal(cmd, 'install')
|
||||
t.ok(cliConf['recursive'])
|
||||
t.equal(workspaceDir, expectedWorkspaceDir)
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('recursive is returned as the command name if no subcommand passed', async (t) => {
|
||||
const { cliConf, cmd } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
globalOptionsTypes: { filter: [String, Array] },
|
||||
}, ['recursive'])
|
||||
t.equal(cmd, 'recursive')
|
||||
t.ok(cliConf['recursive'])
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('when runnning a global command inside a workspace, the workspace should be ignored', async (t) => {
|
||||
const { workspaceDir } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
@@ -112,6 +122,7 @@ test('detect unknown options', async (t) => {
|
||||
getTypesByCommandName: (commandName: string) => {
|
||||
if (commandName === 'install') {
|
||||
return {
|
||||
recursive: Boolean,
|
||||
registry: String,
|
||||
}
|
||||
}
|
||||
@@ -124,24 +135,6 @@ test('detect unknown options', async (t) => {
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('merge option types of recursive and subcommand', async (t) => {
|
||||
const { unknownOptions } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
getTypesByCommandName: (commandName: string) => {
|
||||
switch (commandName) {
|
||||
case 'install': return { recursive: Boolean, registry: String }
|
||||
case 'recursive': return { sort: Boolean }
|
||||
default: return {}
|
||||
}
|
||||
},
|
||||
globalOptionsTypes: { filter: [String, Array] },
|
||||
isKnownCommand: (commandName) => commandName === 'install',
|
||||
shortHands: { 'r': ['--recursive'] },
|
||||
}, ['-r', 'install', '--registry=https://example.com', '--sort'])
|
||||
t.deepEqual(unknownOptions, [])
|
||||
t.end()
|
||||
})
|
||||
|
||||
test('do not incorrectly change "install" command to "add"', async (t) => {
|
||||
const { cmd } = await parseCliArgs({
|
||||
...DEFAULT_OPTS,
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
"scripts": {
|
||||
"lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts",
|
||||
"tsc": "rimraf lib && tsc",
|
||||
"test": "pnpm run tsc && pnpm run lint && ts-node test",
|
||||
"registry-mock": "registry-mock",
|
||||
"test:tap": "ts-node test --type-check",
|
||||
"pretest:e2e": "registry-mock prepare",
|
||||
"test:e2e": "run-p -r registry-mock test:tap",
|
||||
"test": "pnpm run tsc && pnpm run lint && cross-env PNPM_REGISTRY_MOCK_PORT=7777 pnpm run test:e2e",
|
||||
"prepublishOnly": "pnpm run tsc"
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/plugin-commands-installation",
|
||||
@@ -27,33 +31,54 @@
|
||||
},
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/lockfile-types": "workspace:1.1.0",
|
||||
"@pnpm/logger": "^3.1.0",
|
||||
"@pnpm/plugin-commands-installation": "link:",
|
||||
"@pnpm/prepare": "workspace:0.0.0",
|
||||
"@pnpm/registry-mock": "1.11.1",
|
||||
"@pnpm/test-fixtures": "workspace:0.0.0",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/mz": "^2.7.0",
|
||||
"@types/ramda": "^0.26.38",
|
||||
"rimraf": "3.0.0"
|
||||
"make-dir": "3.0.0",
|
||||
"read-yaml-file": "1.1.0",
|
||||
"rimraf": "3.0.0",
|
||||
"write-json-file": "4.2.1",
|
||||
"write-yaml-file": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/cli-utils": "workspace:0.2.5",
|
||||
"@pnpm/common-cli-options-help": "workspace:0.1.2",
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/constants": "workspace:3.0.0",
|
||||
"@pnpm/core-loggers": "workspace:4.0.0",
|
||||
"@pnpm/error": "workspace:1.0.0",
|
||||
"@pnpm/filter-workspace-packages": "workspace:1.0.1",
|
||||
"@pnpm/find-workspace-dir": "workspace:1.0.0",
|
||||
"@pnpm/find-workspace-packages": "workspace:2.0.8",
|
||||
"@pnpm/package-store": "workspace:7.0.2",
|
||||
"@pnpm/plugin-commands-rebuild": "workspace:0.0.0",
|
||||
"@pnpm/plugin-commands-recursive": "workspace:0.1.10",
|
||||
"@pnpm/pnpmfile": "workspace:0.1.0",
|
||||
"@pnpm/resolver-base": "workspace:6.0.0",
|
||||
"@pnpm/sort-packages": "workspace:0.0.0",
|
||||
"@pnpm/store-connection-manager": "workspace:0.2.5",
|
||||
"@pnpm/types": "workspace:4.0.0",
|
||||
"@pnpm/utils": "workspace:0.12.2",
|
||||
"camelcase-keys": "6.1.1",
|
||||
"common-tags": "1.8.0",
|
||||
"is-subdir": "1.1.1",
|
||||
"mem": "6.0.1",
|
||||
"mz": "2.7.0",
|
||||
"p-filter": "2.1.0",
|
||||
"p-limit": "2.2.1",
|
||||
"path-absolute": "1.0.1",
|
||||
"path-exists": "4.0.0",
|
||||
"ramda": "0.26.1",
|
||||
"read-ini-file": "2.0.0",
|
||||
"render-help": "0.0.0",
|
||||
"supi": "workspace:0.37.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pnpm/logger": "^3.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ export async function handler (
|
||||
input: string[],
|
||||
opts: InstallCommandOptions & {
|
||||
allowNew?: boolean,
|
||||
ignoreWorkspaceRootCheck?: boolean,
|
||||
save?: boolean,
|
||||
update?: boolean,
|
||||
useBetaCli?: boolean,
|
||||
@@ -160,5 +161,20 @@ export async function handler (
|
||||
if (opts.cliOptions['save'] === false) {
|
||||
throw new PnpmError('OPTION_NOT_SUPPORTED', 'The "add" command currently does not support the no-save option')
|
||||
}
|
||||
return install(input, opts, invocation)
|
||||
if (!input || !input.length) {
|
||||
throw new PnpmError('MISSING_PACKAGE_NAME', '`pnpm add` requires the package name')
|
||||
}
|
||||
if (
|
||||
!opts.recursive &&
|
||||
opts.workspaceDir === opts.dir &&
|
||||
!opts.ignoreWorkspaceRootCheck
|
||||
) {
|
||||
throw new PnpmError('ADDING_TO_ROOT',
|
||||
'Running this command will add the dependency to the workspace root, ' +
|
||||
'which might not be what you want - if you really meant it, ' +
|
||||
'make it explicit by running this command again with the -W flag (or --ignore-workspace-root-check).',
|
||||
)
|
||||
}
|
||||
|
||||
return install(input, opts, invocation ?? 'add')
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ import PnpmError from '@pnpm/error'
|
||||
import { filterPkgsBySelectorObjects } from '@pnpm/filter-workspace-packages'
|
||||
import findWorkspacePackages, { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages'
|
||||
import { rebuild } from '@pnpm/plugin-commands-rebuild/lib/implementation'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive/lib/recursive'
|
||||
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from '@pnpm/plugin-commands-recursive/lib/updateWorkspaceDependencies'
|
||||
import { requireHooks } from '@pnpm/pnpmfile'
|
||||
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
|
||||
import { oneLine } from 'common-tags'
|
||||
@@ -25,6 +23,8 @@ import {
|
||||
install,
|
||||
mutateModules,
|
||||
} from 'supi'
|
||||
import recursive from './recursive'
|
||||
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies'
|
||||
|
||||
const OVERWRITE_UPDATE_OPTIONS = {
|
||||
allowNew: true,
|
||||
@@ -256,6 +256,7 @@ export function help () {
|
||||
}
|
||||
|
||||
export type InstallCommandOptions = Pick<Config,
|
||||
'allWsPkgs' |
|
||||
'bail' |
|
||||
'bin' |
|
||||
'cliOptions' |
|
||||
@@ -277,7 +278,9 @@ export type InstallCommandOptions = Pick<Config,
|
||||
'savePrefix' |
|
||||
'saveProd' |
|
||||
'saveWorkspaceProtocol' |
|
||||
'selectedWsPkgsGraph' |
|
||||
'sort' |
|
||||
'sharedWorkspaceLockfile' |
|
||||
'workspaceConcurrency' |
|
||||
'workspaceDir'
|
||||
> & CreateStoreControllerOptions & {
|
||||
@@ -288,13 +291,14 @@ export type InstallCommandOptions = Pick<Config,
|
||||
latest?: boolean,
|
||||
update?: boolean,
|
||||
useBetaCli?: boolean,
|
||||
recursive?: boolean,
|
||||
workspace?: boolean,
|
||||
}
|
||||
|
||||
export async function handler (
|
||||
input: string[],
|
||||
opts: InstallCommandOptions,
|
||||
invocation?: string,
|
||||
invocation: string,
|
||||
) {
|
||||
if (opts.workspace) {
|
||||
if (opts.latest) {
|
||||
@@ -315,6 +319,10 @@ export async function handler (
|
||||
}
|
||||
opts['preserveWorkspaceProtocol'] = !opts.linkWorkspacePackages
|
||||
}
|
||||
if (opts.recursive && opts.allWsPkgs && opts.selectedWsPkgsGraph && opts.workspaceDir) {
|
||||
await recursive(opts.allWsPkgs, input, { ...opts, selectedWsPkgsGraph: opts.selectedWsPkgsGraph!, workspaceDir: opts.workspaceDir! }, invocation)
|
||||
return
|
||||
}
|
||||
// `pnpm install ""` is going to be just `pnpm install`
|
||||
input = input.filter(Boolean)
|
||||
|
||||
@@ -369,9 +377,6 @@ export async function handler (
|
||||
}
|
||||
}
|
||||
if (!input || !input.length) {
|
||||
if (invocation === 'add') {
|
||||
throw new PnpmError('MISSING_PACKAGE_NAME', '`pnpm add` requires the package name')
|
||||
}
|
||||
const updatedManifest = await install(manifest, installOpts)
|
||||
if (opts.update === true && opts.save !== false) {
|
||||
await writeImporterManifest(updatedManifest)
|
||||
@@ -401,6 +406,7 @@ export async function handler (
|
||||
const allWsPkgs = await findWorkspacePackages(opts.workspaceDir, opts)
|
||||
const selectedWsPkgsGraph = await filterPkgsBySelectorObjects(allWsPkgs, [
|
||||
{
|
||||
excludeSelf: true,
|
||||
includeDependencies: true,
|
||||
parentDir: dir,
|
||||
},
|
||||
@@ -410,10 +416,9 @@ export async function handler (
|
||||
await recursive(allWsPkgs, [], {
|
||||
...opts,
|
||||
...OVERWRITE_UPDATE_OPTIONS,
|
||||
ignoredPackages: new Set([dir]),
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: opts.workspaceDir, // Otherwise TypeScript doesn't understant that is is not undefined
|
||||
}, 'install', 'install')
|
||||
}, 'install')
|
||||
|
||||
if (opts.ignoreScripts) return
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
WorkspacePackages,
|
||||
} from 'supi'
|
||||
import * as installCommand from './install'
|
||||
import recursive from './recursive'
|
||||
|
||||
const installLimit = pLimit(4)
|
||||
|
||||
|
||||
406
packages/plugin-commands-installation/src/recursive.ts
Executable file
406
packages/plugin-commands-installation/src/recursive.ts
Executable file
@@ -0,0 +1,406 @@
|
||||
import {
|
||||
createLatestSpecs,
|
||||
getPinnedVersion,
|
||||
getSaveType,
|
||||
RecursiveSummary,
|
||||
throwOnCommandFail,
|
||||
updateToLatestSpecsFromManifest,
|
||||
} from '@pnpm/cli-utils'
|
||||
import { Config, WsPkg, WsPkgsGraph } from '@pnpm/config'
|
||||
import { scopeLogger } from '@pnpm/core-loggers'
|
||||
import { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages'
|
||||
import logger from '@pnpm/logger'
|
||||
import { rebuild } from '@pnpm/plugin-commands-rebuild'
|
||||
import { requireHooks } from '@pnpm/pnpmfile'
|
||||
import sortPackages from '@pnpm/sort-packages'
|
||||
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
|
||||
import { ImporterManifest, PackageManifest } from '@pnpm/types'
|
||||
import camelcaseKeys = require('camelcase-keys')
|
||||
import isSubdir = require('is-subdir')
|
||||
import mem = require('mem')
|
||||
import fs = require('mz/fs')
|
||||
import pFilter = require('p-filter')
|
||||
import pLimit from 'p-limit'
|
||||
import path = require('path')
|
||||
import R = require('ramda')
|
||||
import readIniFile = require('read-ini-file')
|
||||
import {
|
||||
addDependenciesToPackage,
|
||||
install,
|
||||
InstallOptions,
|
||||
MutatedImporter,
|
||||
mutateModules,
|
||||
} from 'supi'
|
||||
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies'
|
||||
|
||||
type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
|
||||
'bail' |
|
||||
'globalPnpmfile' |
|
||||
'hoistPattern' |
|
||||
'ignorePnpmfile' |
|
||||
'ignoreScripts' |
|
||||
'include' |
|
||||
'linkWorkspacePackages' |
|
||||
'lockfileDir' |
|
||||
'lockfileOnly' |
|
||||
'pnpmfile' |
|
||||
'rawLocalConfig' |
|
||||
'registries' |
|
||||
'save' |
|
||||
'saveDev' |
|
||||
'saveExact' |
|
||||
'saveOptional' |
|
||||
'savePeer' |
|
||||
'savePrefix' |
|
||||
'saveProd' |
|
||||
'saveWorkspaceProtocol' |
|
||||
'sharedWorkspaceLockfile' |
|
||||
'tag'
|
||||
> & {
|
||||
latest?: boolean,
|
||||
pending?: boolean,
|
||||
workspace?: boolean,
|
||||
} & Partial<Pick<Config, 'sort' | 'workspaceConcurrency'>>
|
||||
|
||||
export default async function recursive (
|
||||
allWsPkgs: WsPkg[],
|
||||
input: string[],
|
||||
opts: RecursiveOptions & {
|
||||
allowNew?: boolean,
|
||||
ignoredPackages?: Set<string>,
|
||||
update?: boolean,
|
||||
useBetaCli?: boolean,
|
||||
selectedWsPkgsGraph: WsPkgsGraph,
|
||||
} & Required<Pick<Config, 'workspaceDir'>>,
|
||||
cmdFullName: string,
|
||||
): Promise<boolean | string> {
|
||||
if (allWsPkgs.length === 0) {
|
||||
// It might make sense to throw an exception in this case
|
||||
return false
|
||||
}
|
||||
|
||||
const pkgs = Object.values(opts.selectedWsPkgsGraph).map((wsPkg) => wsPkg.package)
|
||||
|
||||
if (pkgs.length === 0) {
|
||||
return false
|
||||
}
|
||||
const manifestsByPath: { [dir: string]: Omit<WsPkg, 'dir'> } = {}
|
||||
for (const { dir, manifest, writeImporterManifest } of pkgs) {
|
||||
manifestsByPath[dir] = { manifest, writeImporterManifest }
|
||||
}
|
||||
|
||||
scopeLogger.debug({
|
||||
selected: pkgs.length,
|
||||
total: allWsPkgs.length,
|
||||
workspacePrefix: opts.workspaceDir,
|
||||
})
|
||||
|
||||
const throwOnFail = throwOnCommandFail.bind(null, `pnpm recursive ${cmdFullName}`)
|
||||
|
||||
const chunks = opts.sort !== false
|
||||
? sortPackages(opts.selectedWsPkgsGraph)
|
||||
: [Object.keys(opts.selectedWsPkgsGraph).sort()]
|
||||
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
|
||||
// It is enough to save the store.json file once,
|
||||
// once all installations are done.
|
||||
// That's why saveState that is passed to the install engine
|
||||
// does nothing.
|
||||
const saveState = store.ctrl.saveState
|
||||
const storeController = {
|
||||
...store.ctrl,
|
||||
saveState: async () => undefined,
|
||||
}
|
||||
|
||||
const workspacePackages = cmdFullName !== 'unlink'
|
||||
? arrayOfWorkspacePackagesToMap(allWsPkgs)
|
||||
: {}
|
||||
const installOpts = Object.assign(opts, {
|
||||
ownLifecycleHooksStdio: 'pipe',
|
||||
peer: opts.savePeer,
|
||||
pruneLockfileImporters: (!opts.ignoredPackages || opts.ignoredPackages.size === 0)
|
||||
&& pkgs.length === allWsPkgs.length,
|
||||
storeController,
|
||||
storeDir: store.dir,
|
||||
workspacePackages,
|
||||
|
||||
forceHoistPattern: typeof opts.rawLocalConfig['hoist-pattern'] !== 'undefined' || typeof opts.rawLocalConfig['hoist'] !== 'undefined',
|
||||
forceIndependentLeaves: typeof opts.rawLocalConfig['independent-leaves'] !== 'undefined',
|
||||
forceShamefullyHoist: typeof opts.rawLocalConfig['shamefully-hoist'] !== 'undefined',
|
||||
}) as InstallOptions
|
||||
|
||||
const result = {
|
||||
fails: [],
|
||||
passes: 0,
|
||||
} as RecursiveSummary
|
||||
|
||||
const memReadLocalConfig = mem(readLocalConfig)
|
||||
|
||||
async function getImporters () {
|
||||
const importers = [] as Array<{ buildIndex: number, manifest: ImporterManifest, rootDir: string }>
|
||||
await Promise.all(chunks.map((prefixes: string[], buildIndex) => {
|
||||
if (opts.ignoredPackages) {
|
||||
prefixes = prefixes.filter((prefix) => !opts.ignoredPackages!.has(prefix))
|
||||
}
|
||||
return Promise.all(
|
||||
prefixes.map(async (prefix) => {
|
||||
importers.push({
|
||||
buildIndex,
|
||||
manifest: manifestsByPath[prefix].manifest,
|
||||
rootDir: prefix,
|
||||
})
|
||||
})
|
||||
)
|
||||
}))
|
||||
return importers
|
||||
}
|
||||
|
||||
const updateToLatest = opts.update && opts.latest
|
||||
const include = opts.include
|
||||
if (updateToLatest) {
|
||||
delete opts.include
|
||||
}
|
||||
|
||||
// For a workspace with shared lockfile
|
||||
if (opts.lockfileDir && ['add', 'install', 'remove', 'update'].includes(cmdFullName)) {
|
||||
if (opts.hoistPattern) {
|
||||
logger.info({ message: 'Only the root workspace package is going to have hoisted dependencies in node_modules', prefix: opts.lockfileDir })
|
||||
}
|
||||
let importers = await getImporters()
|
||||
const isFromWorkspace = isSubdir.bind(null, opts.lockfileDir)
|
||||
importers = await pFilter(importers, async ({ rootDir }: { rootDir: string }) => isFromWorkspace(await fs.realpath(rootDir)))
|
||||
if (importers.length === 0) return true
|
||||
const hooks = opts.ignorePnpmfile ? {} : requireHooks(opts.lockfileDir, opts)
|
||||
const mutation = cmdFullName === 'remove' ? 'uninstallSome' : (input.length === 0 && !updateToLatest ? 'install' : 'installSome')
|
||||
const writeImporterManifests = [] as Array<(manifest: ImporterManifest) => Promise<void>>
|
||||
const mutatedImporters = [] as MutatedImporter[]
|
||||
await Promise.all(importers.map(async ({ buildIndex, rootDir }) => {
|
||||
const localConfig = await memReadLocalConfig(rootDir)
|
||||
const { manifest, writeImporterManifest } = manifestsByPath[rootDir]
|
||||
let currentInput = [...input]
|
||||
if (updateToLatest) {
|
||||
if (!currentInput || !currentInput.length) {
|
||||
currentInput = updateToLatestSpecsFromManifest(manifest, include)
|
||||
} else {
|
||||
currentInput = createLatestSpecs(currentInput, manifest)
|
||||
if (!currentInput.length) {
|
||||
installOpts.pruneLockfileImporters = false
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts.workspace) {
|
||||
if (!currentInput || !currentInput.length) {
|
||||
currentInput = updateToWorkspacePackagesFromManifest(manifest, opts.include, workspacePackages!)
|
||||
} else {
|
||||
currentInput = createWorkspaceSpecs(currentInput, workspacePackages!)
|
||||
}
|
||||
}
|
||||
writeImporterManifests.push(writeImporterManifest)
|
||||
switch (mutation) {
|
||||
case 'uninstallSome':
|
||||
mutatedImporters.push({
|
||||
dependencyNames: currentInput,
|
||||
manifest,
|
||||
mutation,
|
||||
rootDir,
|
||||
targetDependenciesField: getSaveType(opts),
|
||||
} as MutatedImporter)
|
||||
return
|
||||
case 'installSome':
|
||||
mutatedImporters.push({
|
||||
allowNew: cmdFullName === 'install' || cmdFullName === 'add',
|
||||
dependencySelectors: currentInput,
|
||||
manifest,
|
||||
mutation,
|
||||
peer: opts.savePeer,
|
||||
pinnedVersion: getPinnedVersion({
|
||||
saveExact: typeof localConfig.saveExact === 'boolean' ? localConfig.saveExact : opts.saveExact,
|
||||
savePrefix: typeof localConfig.savePrefix === 'string' ? localConfig.savePrefix : opts.savePrefix,
|
||||
}),
|
||||
rootDir,
|
||||
targetDependenciesField: getSaveType(opts),
|
||||
} as MutatedImporter)
|
||||
return
|
||||
case 'install':
|
||||
mutatedImporters.push({
|
||||
buildIndex,
|
||||
manifest,
|
||||
mutation,
|
||||
rootDir,
|
||||
} as MutatedImporter)
|
||||
return
|
||||
}
|
||||
}))
|
||||
const mutatedPkgs = await mutateModules(mutatedImporters, {
|
||||
...installOpts,
|
||||
hooks,
|
||||
storeController: store.ctrl,
|
||||
})
|
||||
if (opts.save !== false) {
|
||||
await Promise.all(
|
||||
mutatedPkgs
|
||||
.map(({ manifest }, index) => writeImporterManifests[index](manifest))
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
let pkgPaths = chunks.length === 0
|
||||
? chunks[0]
|
||||
: Object.keys(opts.selectedWsPkgsGraph).sort()
|
||||
|
||||
const limitInstallation = pLimit(opts.workspaceConcurrency ?? 4)
|
||||
await Promise.all(pkgPaths.map((rootDir: string) =>
|
||||
limitInstallation(async () => {
|
||||
const hooks = opts.ignorePnpmfile ? {} : requireHooks(rootDir, opts)
|
||||
try {
|
||||
if (opts.ignoredPackages && opts.ignoredPackages.has(rootDir)) {
|
||||
return
|
||||
}
|
||||
|
||||
const { manifest, writeImporterManifest } = manifestsByPath[rootDir]
|
||||
let currentInput = [...input]
|
||||
if (updateToLatest) {
|
||||
if (!currentInput || !currentInput.length) {
|
||||
currentInput = updateToLatestSpecsFromManifest(manifest, include)
|
||||
} else {
|
||||
currentInput = createLatestSpecs(currentInput, manifest)
|
||||
if (!currentInput.length) return
|
||||
}
|
||||
}
|
||||
|
||||
let action!: any // tslint:disable-line:no-any
|
||||
switch (cmdFullName) {
|
||||
case 'unlink':
|
||||
action = (currentInput.length === 0 ? unlink : unlinkPkgs.bind(null, currentInput))
|
||||
break
|
||||
case 'remove':
|
||||
action = (manifest: PackageManifest, opts: any) => mutateModules([ // tslint:disable-line:no-any
|
||||
{
|
||||
dependencyNames: currentInput,
|
||||
manifest,
|
||||
mutation: 'uninstallSome',
|
||||
rootDir,
|
||||
},
|
||||
], opts)
|
||||
break
|
||||
default:
|
||||
action = currentInput.length === 0
|
||||
? install
|
||||
: (manifest: PackageManifest, opts: any) => addDependenciesToPackage(manifest, currentInput, opts) // tslint:disable-line:no-any
|
||||
break
|
||||
}
|
||||
|
||||
const localConfig = await memReadLocalConfig(rootDir)
|
||||
const newManifest = await action(
|
||||
manifest,
|
||||
{
|
||||
...installOpts,
|
||||
...localConfig,
|
||||
bin: path.join(rootDir, 'node_modules', '.bin'),
|
||||
dir: rootDir,
|
||||
hooks,
|
||||
ignoreScripts: true,
|
||||
pinnedVersion: getPinnedVersion({
|
||||
saveExact: typeof localConfig.saveExact === 'boolean' ? localConfig.saveExact : opts.saveExact,
|
||||
savePrefix: typeof localConfig.savePrefix === 'string' ? localConfig.savePrefix : opts.savePrefix,
|
||||
}),
|
||||
rawConfig: {
|
||||
...installOpts.rawConfig,
|
||||
...localConfig,
|
||||
},
|
||||
storeController,
|
||||
},
|
||||
)
|
||||
if (opts.save !== false) {
|
||||
await writeImporterManifest(newManifest)
|
||||
}
|
||||
result.passes++
|
||||
} catch (err) {
|
||||
logger.info(err)
|
||||
|
||||
if (!opts.bail) {
|
||||
result.fails.push({
|
||||
error: err,
|
||||
message: err.message,
|
||||
prefix: rootDir,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err['prefix'] = rootDir // tslint:disable-line:no-string-literal
|
||||
throw err
|
||||
}
|
||||
}),
|
||||
))
|
||||
|
||||
await saveState()
|
||||
// The store should be unlocked because otherwise rebuild will not be able
|
||||
// to access it
|
||||
await storeController.close()
|
||||
|
||||
if (
|
||||
!opts.lockfileOnly && !opts.ignoreScripts && (
|
||||
cmdFullName === 'add' ||
|
||||
cmdFullName === 'install' ||
|
||||
cmdFullName === 'update' ||
|
||||
cmdFullName === 'unlink'
|
||||
)
|
||||
) {
|
||||
await rebuild.handler([], {
|
||||
...opts,
|
||||
pending: opts.pending === true,
|
||||
})
|
||||
}
|
||||
|
||||
throwOnFail(result)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async function unlink (manifest: ImporterManifest, opts: any) { // tslint:disable-line:no-any
|
||||
return mutateModules(
|
||||
[
|
||||
{
|
||||
manifest,
|
||||
mutation: 'unlink',
|
||||
rootDir: opts.dir,
|
||||
},
|
||||
],
|
||||
opts,
|
||||
)
|
||||
}
|
||||
|
||||
async function unlinkPkgs (dependencyNames: string[], manifest: ImporterManifest, opts: any) { // tslint:disable-line:no-any
|
||||
return mutateModules(
|
||||
[
|
||||
{
|
||||
dependencyNames,
|
||||
manifest,
|
||||
mutation: 'unlinkSome',
|
||||
rootDir: opts.dir,
|
||||
},
|
||||
],
|
||||
opts,
|
||||
)
|
||||
}
|
||||
|
||||
async function readLocalConfig (prefix: string) {
|
||||
try {
|
||||
const ini = await readIniFile(path.join(prefix, '.npmrc')) as { [key: string]: string }
|
||||
const config = camelcaseKeys(ini) as ({ [key: string]: string } & { hoist?: boolean })
|
||||
if (config.shamefullyFlatten) {
|
||||
config.hoistPattern = '*'
|
||||
// TODO: print a warning
|
||||
}
|
||||
if (config.hoist === false) {
|
||||
config.hoistPattern = ''
|
||||
}
|
||||
return config
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
return {}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import renderHelp = require('render-help')
|
||||
import {
|
||||
mutateModules,
|
||||
} from 'supi'
|
||||
import recursive from './recursive'
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
@@ -68,8 +69,29 @@ export const commandNames = ['remove', 'uninstall', 'r', 'rm', 'un']
|
||||
|
||||
export async function handler (
|
||||
input: string[],
|
||||
opts: CreateStoreControllerOptions & Pick<Config, 'ignorePnpmfile' | 'engineStrict' | 'lockfileDir' | 'linkWorkspacePackages' | 'workspaceDir' | 'bin' | 'globalPnpmfile' | 'pnpmfile'>,
|
||||
opts: CreateStoreControllerOptions & Pick<Config,
|
||||
'allWsPkgs' |
|
||||
'bail' |
|
||||
'bin' |
|
||||
'engineStrict' |
|
||||
'globalPnpmfile' |
|
||||
'ignorePnpmfile' |
|
||||
'include' |
|
||||
'lockfileDir' |
|
||||
'linkWorkspacePackages' |
|
||||
'pnpmfile' |
|
||||
'rawLocalConfig' |
|
||||
'registries' |
|
||||
'selectedWsPkgsGraph' |
|
||||
'workspaceDir'
|
||||
> & {
|
||||
recursive?: boolean,
|
||||
},
|
||||
) {
|
||||
if (opts.recursive && opts.allWsPkgs && opts.selectedWsPkgsGraph && opts.workspaceDir) {
|
||||
await recursive(opts.allWsPkgs, input, { ...opts, selectedWsPkgsGraph: opts.selectedWsPkgsGraph!, workspaceDir: opts.workspaceDir! }, 'remove')
|
||||
return
|
||||
}
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
const removeOpts = Object.assign(opts, {
|
||||
storeController: store.ctrl,
|
||||
|
||||
@@ -6,6 +6,7 @@ import { oneLine } from 'common-tags'
|
||||
import renderHelp = require('render-help')
|
||||
import { mutateModules } from 'supi'
|
||||
import { cliOptionsTypes, rcOptionsTypes } from './install'
|
||||
import recursive from './recursive'
|
||||
|
||||
export { cliOptionsTypes, rcOptionsTypes }
|
||||
|
||||
@@ -40,7 +41,28 @@ export function help () {
|
||||
})
|
||||
}
|
||||
|
||||
export async function handler (input: string[], opts: CreateStoreControllerOptions & Pick<Config, 'engineStrict'>) {
|
||||
export async function handler (
|
||||
input: string[],
|
||||
opts: CreateStoreControllerOptions &
|
||||
Pick<Config,
|
||||
'allWsPkgs' |
|
||||
'bail' |
|
||||
'engineStrict' |
|
||||
'include' |
|
||||
'linkWorkspacePackages' |
|
||||
'selectedWsPkgsGraph' |
|
||||
'rawLocalConfig' |
|
||||
'registries' |
|
||||
'pnpmfile' |
|
||||
'workspaceDir'
|
||||
> & {
|
||||
recursive?: boolean,
|
||||
},
|
||||
) {
|
||||
if (opts.recursive && opts.allWsPkgs && opts.selectedWsPkgsGraph && opts.workspaceDir) {
|
||||
await recursive(opts.allWsPkgs, input, { ...opts, selectedWsPkgsGraph: opts.selectedWsPkgsGraph!, workspaceDir: opts.workspaceDir! }, 'unlink')
|
||||
return
|
||||
}
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
const unlinkOpts = Object.assign(opts, {
|
||||
storeController: store.ctrl,
|
||||
|
||||
@@ -106,5 +106,5 @@ export async function handler (
|
||||
input: string[],
|
||||
opts: InstallCommandOptions,
|
||||
) {
|
||||
return install(input, { ...opts, update: true, allowNew: false })
|
||||
return install(input, { ...opts, update: true, allowNew: false }, 'update')
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
///<reference path="../../../typings/index.d.ts" />
|
||||
import './add'
|
||||
import './linkRecursive'
|
||||
import './miscRecursive'
|
||||
import './prune'
|
||||
import './updateRecursive'
|
||||
import './updateWorkspaceDependencies.spec'
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { install, link, unlink } from '@pnpm/plugin-commands-installation'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import path = require('path')
|
||||
import exists = require('path-exists')
|
||||
import test = require('tape')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
test('recursive linking/unlinking', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -27,12 +28,14 @@ test('recursive linking/unlinking', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
t.ok(projects['is-positive'].requireModule('is-negative'))
|
||||
t.notOk(projects['project-1'].requireModule('is-positive/package.json').author, 'local package is linked')
|
||||
@@ -42,11 +45,13 @@ test('recursive linking/unlinking', async (t) => {
|
||||
t.equal(project1Lockfile.devDependencies['is-positive'], 'link:../is-positive')
|
||||
}
|
||||
|
||||
await recursive.handler(['unlink'], {
|
||||
await unlink.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
process.chdir('project-1')
|
||||
@@ -86,12 +91,14 @@ test('recursive unlink specific package', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
t.ok(projects['is-positive'].requireModule('is-negative'))
|
||||
t.notOk(projects['project-1'].requireModule('is-positive/package.json').author, 'local package is linked')
|
||||
@@ -101,11 +108,13 @@ test('recursive unlink specific package', async (t) => {
|
||||
t.equal(project1Lockfile.devDependencies['is-positive'], 'link:../is-positive')
|
||||
}
|
||||
|
||||
await recursive.handler(['unlink', 'is-positive'], {
|
||||
await unlink.handler(['is-positive'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
process.chdir('project-1')
|
||||
@@ -1,5 +1,6 @@
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { add, install, remove, update } from '@pnpm/plugin-commands-installation'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import makeDir = require('make-dir')
|
||||
import fs = require('mz/fs')
|
||||
@@ -7,7 +8,7 @@ import path = require('path')
|
||||
import test = require('tape')
|
||||
import writeJsonFile = require('write-json-file')
|
||||
import writeYamlFile = require('write-yaml-file')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
test('recursive install/uninstall', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -30,32 +31,38 @@ test('recursive install/uninstall', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
t.ok(projects['project-1'].requireModule('is-positive'))
|
||||
t.ok(projects['project-2'].requireModule('is-negative'))
|
||||
await projects['project-2'].has('is-negative')
|
||||
|
||||
await recursive.handler(['add', 'noop'], {
|
||||
await add.handler(['noop'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'add')
|
||||
|
||||
t.ok(projects['project-1'].requireModule('noop'))
|
||||
t.ok(projects['project-2'].requireModule('noop'))
|
||||
|
||||
await recursive.handler(['remove', 'is-negative'], {
|
||||
await remove.handler(['is-negative'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
await projects['project-2'].hasNot('is-negative')
|
||||
@@ -85,11 +92,13 @@ test('recursive install with package that has link', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
t.ok(projects['project-1'].requireModule('is-positive'))
|
||||
t.ok(projects['project-1'].requireModule('project-2/package.json'))
|
||||
@@ -119,11 +128,13 @@ test('running `pnpm recursive` on a subset of packages', async t => {
|
||||
|
||||
await writeYamlFile('pnpm-workspace.yaml', { packages: ['project-1'] })
|
||||
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
await projects['project-1'].has('is-positive')
|
||||
await projects['project-2'].hasNot('is-negative')
|
||||
@@ -170,11 +181,13 @@ test('running `pnpm recursive` only for packages in subdirectories of cwd', asyn
|
||||
await makeDir('node_modules')
|
||||
process.chdir('packages')
|
||||
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
await projects['project-1'].has('is-positive')
|
||||
await projects['project-2'].has('is-negative')
|
||||
@@ -204,15 +217,17 @@ test('recursive installation fails when installation in one of the packages fail
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
}
|
||||
t.equal(err.code, 'ERR_PNPM_RECURSIVE_FAIL')
|
||||
t.equal(err.code, 'ERR_PNPM_REGISTRY_META_RESPONSE_404')
|
||||
t.end()
|
||||
})
|
||||
|
||||
@@ -233,12 +248,14 @@ test('second run of `recursive install` after package.json has been edited manua
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
await writeJsonFile('is-negative/package.json', {
|
||||
name: 'is-negative',
|
||||
@@ -249,12 +266,14 @@ test('second run of `recursive install` after package.json has been edited manua
|
||||
},
|
||||
})
|
||||
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
t.ok(projects['is-negative'].requireModule('is-positive/package.json'))
|
||||
t.end()
|
||||
@@ -296,13 +315,15 @@ test('recursive --filter ignore excluded packages', async (t) => {
|
||||
],
|
||||
})
|
||||
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), [
|
||||
{ includeDependencies: true, namePattern: 'project-1' },
|
||||
]),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
projects['project-1'].hasNot('is-positive')
|
||||
projects['project-2'].hasNot('is-negative')
|
||||
@@ -339,14 +360,16 @@ test('recursive filter multiple times', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), [
|
||||
{ namePattern: 'project-1' },
|
||||
{ namePattern: 'project-2' },
|
||||
]),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
projects['project-1'].has('is-positive')
|
||||
projects['project-2'].has('is-negative')
|
||||
@@ -376,12 +399,14 @@ test('recursive install --no-bail', async (t) => {
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
bail: false,
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
}
|
||||
@@ -408,12 +433,13 @@ test('installing with "workspace=true" should work even if link-workspace-packag
|
||||
},
|
||||
])
|
||||
|
||||
await recursive.handler(['update', 'project-2'], {
|
||||
await update.handler(['project-2'], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
linkWorkspacePackages: false,
|
||||
lockfileDir: process.cwd(),
|
||||
recursive: true,
|
||||
saveWorkspaceProtocol: false,
|
||||
sharedWorkspaceLockfile: true,
|
||||
workspace: true,
|
||||
@@ -1,10 +1,11 @@
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { Lockfile } from '@pnpm/lockfile-types'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { install, update } from '@pnpm/plugin-commands-installation'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { addDistTag } from '@pnpm/registry-mock'
|
||||
import readYamlFile from 'read-yaml-file'
|
||||
import test = require('tape')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
test('recursive update', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -27,18 +28,22 @@ test('recursive update', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
await recursive.handler(['update', 'is-positive@2.0.0'], {
|
||||
await update.handler(['is-positive@2.0.0'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
t.equal(projects['project-1'].requireModule('is-positive/package.json').version, '2.0.0')
|
||||
@@ -75,24 +80,28 @@ test('recursive update --latest foo should only update workspace packages that h
|
||||
const lockfileDir = process.cwd()
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
lockfileDir,
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
await addDistTag({ package: 'foo', version: '100.1.0', distTag: 'latest' })
|
||||
await addDistTag({ package: 'bar', version: '100.1.0', distTag: 'latest' })
|
||||
|
||||
await recursive.handler(['update', '@zkochan/async-regex-replace', 'foo', 'qar@100.1.0'], {
|
||||
await update.handler(['@zkochan/async-regex-replace', 'foo', 'qar@100.1.0'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
latest: true,
|
||||
lockfileDir,
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const lockfile = await readYamlFile<Lockfile>('./pnpm-lock.yaml')
|
||||
@@ -127,22 +136,26 @@ test('recursive update --latest foo should only update packages that have foo',
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
await addDistTag({ package: 'foo', version: '100.1.0', distTag: 'latest' })
|
||||
await addDistTag({ package: 'bar', version: '100.1.0', distTag: 'latest' })
|
||||
|
||||
await recursive.handler(['update', 'foo', 'qar@100.1.0'], {
|
||||
await update.handler(['foo', 'qar@100.1.0'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
latest: true,
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
{
|
||||
@@ -171,10 +184,12 @@ test('recursive update in workspace should not add new dependencies', async (t)
|
||||
},
|
||||
])
|
||||
|
||||
await recursive.handler(['update', 'is-positive'], {
|
||||
await update.handler(['is-positive'], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
projects['project-1'].hasNot('is-positive')
|
||||
@@ -194,10 +209,12 @@ test('recursive update should not add new dependencies', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
await recursive.handler(['update', 'is-positive'], {
|
||||
await update.handler(['is-positive'], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
projects['project-1'].hasNot('is-positive')
|
||||
@@ -2,7 +2,7 @@ import PnpmError from '@pnpm/error'
|
||||
import {
|
||||
createWorkspaceSpecs,
|
||||
updateToWorkspacePackagesFromManifest,
|
||||
} from '@pnpm/plugin-commands-recursive/lib/updateWorkspaceDependencies'
|
||||
} from '@pnpm/plugin-commands-installation/lib/updateWorkspaceDependencies'
|
||||
import test = require('tape')
|
||||
|
||||
const INCLUDE_ALL = {
|
||||
46
packages/plugin-commands-installation/test/utils.ts
Normal file
46
packages/plugin-commands-installation/test/utils.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
|
||||
const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}`
|
||||
|
||||
export const DEFAULT_OPTS = {
|
||||
alwaysAuth: false,
|
||||
argv: {
|
||||
original: [],
|
||||
},
|
||||
bail: true,
|
||||
ca: undefined,
|
||||
cert: undefined,
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
fetchRetryFactor: 90,
|
||||
fetchRetryMaxtimeout: 90,
|
||||
fetchRetryMintimeout: 10,
|
||||
filter: [] as string[],
|
||||
httpsProxy: undefined,
|
||||
include: {
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: true,
|
||||
},
|
||||
key: undefined,
|
||||
linkWorkspacePackages: true,
|
||||
localAddress: undefined,
|
||||
lock: false,
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
pending: false,
|
||||
pnpmfile: './pnpmfile.js',
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
sort: true,
|
||||
storeDir: '../store',
|
||||
strictSsl: false,
|
||||
userAgent: 'pnpm',
|
||||
useRunningStoreServer: false,
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
}
|
||||
@@ -16,15 +16,13 @@
|
||||
"registry-mock": "registry-mock",
|
||||
"test:tap": "ts-node test --type-check",
|
||||
"pretest:e2e": "registry-mock prepare",
|
||||
"test:e2e": "cross-env PNPM_REGISTRY_MOCK_PORT=7775 run-p -r registry-mock test:tap",
|
||||
"test": "pnpm run tsc && pnpm run lint && pnpm run test:e2e",
|
||||
"test:e2e": "run-p -r registry-mock test:tap",
|
||||
"test": "pnpm run tsc && pnpm run lint && cross-env PNPM_REGISTRY_MOCK_PORT=7773 pnpm run test:e2e",
|
||||
"prepublishOnly": "pnpm run tsc"
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/plugin-commands-listing",
|
||||
"keywords": [
|
||||
"pnpm",
|
||||
"pack",
|
||||
"publish"
|
||||
"pnpm"
|
||||
],
|
||||
"author": "Zoltan Kochan <z@kochan.io> (https://www.kochan.io/)",
|
||||
"license": "MIT",
|
||||
@@ -34,6 +32,9 @@
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/constants": "workspace:3.0.0",
|
||||
"@pnpm/filter-workspace-packages": "workspace:1.0.1",
|
||||
"@pnpm/logger": "^3.1.0",
|
||||
"@pnpm/plugin-commands-installation": "workspace:*",
|
||||
"@pnpm/plugin-commands-listing": "link:",
|
||||
"@pnpm/prepare": "workspace:0.0.0",
|
||||
"@types/common-tags": "1.8.0",
|
||||
@@ -54,5 +55,8 @@
|
||||
"common-tags": "1.8.0",
|
||||
"ramda": "0.26.1",
|
||||
"render-help": "0.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pnpm/logger": "^3.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import list, { forPackages as listForPackages } from '@pnpm/list'
|
||||
import { oneLine } from 'common-tags'
|
||||
import R = require('ramda')
|
||||
import renderHelp = require('render-help')
|
||||
import listRecursive from './recursive'
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
@@ -97,15 +98,20 @@ export function help () {
|
||||
|
||||
export function handler (
|
||||
args: string[],
|
||||
opts: Pick<Config, 'dir' | 'include'> & {
|
||||
opts: Pick<Config, 'allWsPkgs' | 'dir' | 'include' | 'selectedWsPkgsGraph'> & {
|
||||
alwaysPrintRootPackage?: boolean,
|
||||
depth?: number,
|
||||
lockfileDir?: string,
|
||||
long?: boolean,
|
||||
parseable?: boolean,
|
||||
recursive?: boolean,
|
||||
},
|
||||
command: string,
|
||||
) {
|
||||
if (opts.recursive && opts.selectedWsPkgsGraph) {
|
||||
const pkgs = Object.values(opts.selectedWsPkgsGraph).map((wsPkg) => wsPkg.package)
|
||||
return listRecursive(pkgs, args, command, opts)
|
||||
}
|
||||
return render([opts.dir], args, {
|
||||
...opts,
|
||||
lockfileDir: opts.lockfileDir || opts.dir,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Config } from '@pnpm/config'
|
||||
import { Config, WsPkg } from '@pnpm/config'
|
||||
import logger from '@pnpm/logger'
|
||||
import { list } from '@pnpm/plugin-commands-listing'
|
||||
import { ImporterManifest } from '@pnpm/types'
|
||||
import { render } from './list'
|
||||
|
||||
export default async (
|
||||
pkgs: Array<{ dir: string, manifest: ImporterManifest }>,
|
||||
pkgs: WsPkg[],
|
||||
args: string[],
|
||||
cmd: string,
|
||||
opts: Config & {
|
||||
opts: Pick<Config, 'lockfileDir' | 'include'> & {
|
||||
depth?: number,
|
||||
long?: boolean,
|
||||
parseable?: boolean,
|
||||
@@ -16,7 +15,7 @@ export default async (
|
||||
) => {
|
||||
const depth = opts.depth ?? 0
|
||||
if (opts.lockfileDir) {
|
||||
return list.render(pkgs.map((pkg) => pkg.dir), args, {
|
||||
return render(pkgs.map((pkg) => pkg.dir), args, {
|
||||
...opts,
|
||||
alwaysPrintRootPackage: depth === -1,
|
||||
lockfileDir: opts.lockfileDir,
|
||||
@@ -25,7 +24,7 @@ export default async (
|
||||
const outputs = []
|
||||
for (const { dir } of pkgs) {
|
||||
try {
|
||||
const output = await list.render([dir], args, {
|
||||
const output = await render([dir], args, {
|
||||
...opts,
|
||||
alwaysPrintRootPackage: depth === -1,
|
||||
lockfileDir: opts.lockfileDir || dir,
|
||||
@@ -10,6 +10,7 @@ import path = require('path')
|
||||
import stripAnsi = require('strip-ansi')
|
||||
import test = require('tape')
|
||||
import writeYamlFile = require('write-yaml-file')
|
||||
import './recursive'
|
||||
|
||||
test('listing packages', async (t) => {
|
||||
prepare(t, {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { install } from '@pnpm/plugin-commands-installation'
|
||||
import { list, why } from '@pnpm/plugin-commands-listing'
|
||||
import prepare, { preparePackages } from '@pnpm/prepare'
|
||||
import { addDistTag } from '@pnpm/registry-mock'
|
||||
import { stripIndent } from 'common-tags'
|
||||
@@ -8,7 +10,7 @@ import path = require('path')
|
||||
import stripAnsi = require('strip-ansi')
|
||||
import test = require('tape')
|
||||
import writeYamlFile = require('write-yaml-file')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
test('recursive list', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -35,19 +37,22 @@ test('recursive list', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
const output = await recursive.handler(['list'], {
|
||||
const output = await list.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
}, 'list')
|
||||
|
||||
t.equal(stripAnsi(output as unknown as string), stripIndent`
|
||||
Legend: production dependency, optional only, dev only
|
||||
@@ -97,20 +102,23 @@ test('recursive list with shared-workspace-lockfile', async (t) => {
|
||||
await fs.writeFile('.npmrc', 'shared-workspace-lockfile = true', 'utf8')
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
const output = await recursive.handler(['list'], {
|
||||
const output = await list.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
depth: 2,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
}, 'list')
|
||||
|
||||
t.equal(stripAnsi(output as unknown as string), stripIndent`
|
||||
Legend: production dependency, optional only, dev only
|
||||
@@ -161,19 +169,22 @@ test('recursive list --filter', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
const output = await recursive.handler(['list'], {
|
||||
const output = await list.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
...await readWsPkgs(process.cwd(), [
|
||||
{ includeDependencies: true, namePattern: 'project-1' },
|
||||
]),
|
||||
})
|
||||
}, 'list')
|
||||
|
||||
t.equal(stripAnsi(output as unknown as string), stripIndent`
|
||||
Legend: production dependency, optional only, dev only
|
||||
@@ -199,11 +210,12 @@ test('`pnpm recursive why` should fail if no package name was provided', async (
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
const output = await recursive.handler(['why'], {
|
||||
await why.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
})
|
||||
recursive: true,
|
||||
}, 'why')
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
import { filterPkgsBySelectorObjects, PackageSelector } from '@pnpm/filter-workspace-packages'
|
||||
import findWorkspacePackages from '@pnpm/find-workspace-packages'
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
|
||||
const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}`
|
||||
@@ -46,18 +44,3 @@ export const DEFAULT_OPTS = {
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
}
|
||||
|
||||
export async function readWsPkgs (
|
||||
workspaceDir: string,
|
||||
pkgSelectors: PackageSelector[],
|
||||
) {
|
||||
const allWsPkgs = await findWorkspacePackages(workspaceDir, {})
|
||||
const selectedWsPkgsGraph = await filterPkgsBySelectorObjects(
|
||||
allWsPkgs,
|
||||
pkgSelectors,
|
||||
{
|
||||
workspaceDir,
|
||||
},
|
||||
)
|
||||
return { allWsPkgs, selectedWsPkgsGraph }
|
||||
}
|
||||
@@ -33,6 +33,8 @@
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/constants": "workspace:3.0.0",
|
||||
"@pnpm/filter-workspace-packages": "workspace:1.0.1",
|
||||
"@pnpm/plugin-commands-installation": "workspace:*",
|
||||
"@pnpm/plugin-commands-outdated": "link:",
|
||||
"@pnpm/prepare": "workspace:0.0.0",
|
||||
"@pnpm/types": "workspace:4.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createLatestManifestGetter, docsUrl, readImporterManifestOnly, TABLE_OPTIONS } from '@pnpm/cli-utils'
|
||||
import { FILTERING, OPTIONS, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
|
||||
import { types as allTypes } from '@pnpm/config'
|
||||
import { Config, types as allTypes } from '@pnpm/config'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import {
|
||||
getLockfileImporterId,
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
import matcher from '@pnpm/matcher'
|
||||
import { read as readModulesManifest } from '@pnpm/modules-yaml'
|
||||
import outdated, { OutdatedPackage } from '@pnpm/outdated'
|
||||
import semverDiff, { SEMVER_CHANGE } from '@pnpm/semver-diff'
|
||||
import semverDiff from '@pnpm/semver-diff'
|
||||
import storePath from '@pnpm/store-path'
|
||||
import { ImporterManifest, Registries } from '@pnpm/types'
|
||||
import chalk = require('chalk')
|
||||
@@ -21,6 +21,11 @@ import renderHelp = require('render-help')
|
||||
import stripAnsi = require('strip-ansi')
|
||||
import { table } from 'table'
|
||||
import wrapAnsi = require('wrap-ansi')
|
||||
import outdatedRecursive from './recursive'
|
||||
import {
|
||||
DEFAULT_COMPARATORS,
|
||||
OutdatedWithVersionDiff,
|
||||
} from './utils'
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
@@ -82,17 +87,7 @@ export function help () {
|
||||
})
|
||||
}
|
||||
|
||||
export type OutdatedWithVersionDiff = OutdatedPackage & { change: SEMVER_CHANGE | null, diff?: [string[], string[]] }
|
||||
|
||||
/**
|
||||
* Default comparators used as the argument to `ramda.sortWith()`.
|
||||
*/
|
||||
export const DEFAULT_COMPARATORS = [
|
||||
sortBySemverChange,
|
||||
(o1: OutdatedWithVersionDiff, o2: OutdatedWithVersionDiff) => o1.packageName.localeCompare(o2.packageName),
|
||||
]
|
||||
|
||||
export interface OutdatedOptions {
|
||||
export type OutdatedOptions = {
|
||||
alwaysAuth: boolean
|
||||
ca?: string
|
||||
cert?: string
|
||||
@@ -112,6 +107,7 @@ export interface OutdatedOptions {
|
||||
dir: string
|
||||
proxy?: string
|
||||
rawConfig: object
|
||||
recursive?: boolean,
|
||||
registries: Registries
|
||||
lockfileDir?: string
|
||||
store?: string
|
||||
@@ -119,13 +115,17 @@ export interface OutdatedOptions {
|
||||
table?: boolean
|
||||
tag: string
|
||||
userAgent: string
|
||||
}
|
||||
} & Pick<Config, 'allWsPkgs' | 'selectedWsPkgsGraph'>
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: OutdatedOptions,
|
||||
command: string,
|
||||
command?: string,
|
||||
) {
|
||||
if (opts.recursive && opts.selectedWsPkgsGraph) {
|
||||
const pkgs = Object.values(opts.selectedWsPkgsGraph).map((wsPkg) => wsPkg.package)
|
||||
return outdatedRecursive(pkgs, args, opts)
|
||||
}
|
||||
const packages = [
|
||||
{
|
||||
dir: opts.dir,
|
||||
@@ -274,20 +274,6 @@ function joinVersionTuples (versionTuples: string[], startIndex: number) {
|
||||
}`
|
||||
}
|
||||
|
||||
export function sortBySemverChange (outdated1: OutdatedWithVersionDiff, outdated2: OutdatedWithVersionDiff) {
|
||||
return pkgPriority(outdated1) - pkgPriority(outdated2)
|
||||
}
|
||||
|
||||
function pkgPriority (pkg: OutdatedWithVersionDiff) {
|
||||
switch (pkg.change) {
|
||||
case null: return 0
|
||||
case 'fix': return 1
|
||||
case 'feature': return 2
|
||||
case 'breaking': return 3
|
||||
default: return 4
|
||||
}
|
||||
}
|
||||
|
||||
export function renderDetails ({ latestManifest }: OutdatedPackage) {
|
||||
if (!latestManifest) return ''
|
||||
const outputs = []
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { TABLE_OPTIONS } from '@pnpm/cli-utils'
|
||||
import { getLockfileImporterId } from '@pnpm/lockfile-file'
|
||||
import { OutdatedPackage } from '@pnpm/outdated'
|
||||
import { DependenciesField, ImporterManifest } from '@pnpm/types'
|
||||
import chalk = require('chalk')
|
||||
import { stripIndent } from 'common-tags'
|
||||
import R = require('ramda')
|
||||
import { table } from 'table'
|
||||
import {
|
||||
DEFAULT_COMPARATORS,
|
||||
getCellWidth,
|
||||
outdatedDependenciesOfWorkspacePackages,
|
||||
OutdatedOptions,
|
||||
@@ -11,12 +15,8 @@ import {
|
||||
renderLatest,
|
||||
renderPackageName,
|
||||
toOutdatedWithVersionDiff,
|
||||
} from '@pnpm/plugin-commands-outdated/lib/outdated'
|
||||
import { DependenciesField, ImporterManifest } from '@pnpm/types'
|
||||
import chalk = require('chalk')
|
||||
import { stripIndent } from 'common-tags'
|
||||
import R = require('ramda')
|
||||
import { table } from 'table'
|
||||
} from './outdated'
|
||||
import { DEFAULT_COMPARATORS } from './utils'
|
||||
|
||||
const DEP_PRIORITY: Record<DependenciesField, number> = {
|
||||
dependencies: 1,
|
||||
@@ -26,7 +26,8 @@ const DEP_PRIORITY: Record<DependenciesField, number> = {
|
||||
|
||||
const COMPARATORS = [
|
||||
...DEFAULT_COMPARATORS,
|
||||
(o1: OutdatedInWorkspace, o2: OutdatedInWorkspace) => DEP_PRIORITY[o1.belongsTo] - DEP_PRIORITY[o2.belongsTo],
|
||||
(o1: OutdatedInWorkspace, o2: OutdatedInWorkspace) =>
|
||||
DEP_PRIORITY[o1.belongsTo] - DEP_PRIORITY[o2.belongsTo],
|
||||
]
|
||||
|
||||
interface OutdatedInWorkspace extends OutdatedPackage {
|
||||
@@ -41,7 +42,6 @@ interface OutdatedInWorkspace extends OutdatedPackage {
|
||||
export default async (
|
||||
pkgs: Array<{ dir: string, manifest: ImporterManifest }>,
|
||||
args: string[],
|
||||
cmd: string,
|
||||
opts: OutdatedOptions,
|
||||
) => {
|
||||
const outdatedByNameAndType = {} as Record<string, OutdatedInWorkspace>
|
||||
27
packages/plugin-commands-outdated/src/utils.ts
Normal file
27
packages/plugin-commands-outdated/src/utils.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { OutdatedPackage } from '@pnpm/outdated'
|
||||
import { SEMVER_CHANGE } from '@pnpm/semver-diff'
|
||||
|
||||
export type OutdatedWithVersionDiff = OutdatedPackage & { change: SEMVER_CHANGE | null, diff?: [string[], string[]] }
|
||||
|
||||
/**
|
||||
* Default comparators used as the argument to `ramda.sortWith()`.
|
||||
*/
|
||||
export const DEFAULT_COMPARATORS = [
|
||||
sortBySemverChange,
|
||||
(o1: OutdatedWithVersionDiff, o2: OutdatedWithVersionDiff) =>
|
||||
o1.packageName.localeCompare(o2.packageName),
|
||||
]
|
||||
|
||||
export function sortBySemverChange (outdated1: OutdatedWithVersionDiff, outdated2: OutdatedWithVersionDiff) {
|
||||
return pkgPriority(outdated1) - pkgPriority(outdated2)
|
||||
}
|
||||
|
||||
function pkgPriority (pkg: OutdatedWithVersionDiff) {
|
||||
switch (pkg.change) {
|
||||
case null: return 0
|
||||
case 'fix': return 1
|
||||
case 'feature': return 2
|
||||
case 'breaking': return 3
|
||||
default: return 4
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import path = require('path')
|
||||
import stripAnsi = require('strip-ansi')
|
||||
import test = require('tape')
|
||||
import { promisify } from 'util'
|
||||
import './recursive'
|
||||
|
||||
const copyFile = promisify(fs.copyFile)
|
||||
const fixtures = path.join(__dirname, '../../../fixtures')
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { install } from '@pnpm/plugin-commands-installation'
|
||||
import { outdated } from '@pnpm/plugin-commands-outdated'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { stripIndent } from 'common-tags'
|
||||
import stripAnsi = require('strip-ansi')
|
||||
import test = require('tape')
|
||||
import writeYamlFile = require('write-yaml-file')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
test('pnpm recursive outdated', async (t) => {
|
||||
preparePackages(t, [
|
||||
@@ -38,18 +39,21 @@ test('pnpm recursive outdated', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
{
|
||||
const output = await recursive.handler(['outdated'], {
|
||||
const output = await outdated.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
|
||||
@@ -67,11 +71,12 @@ test('pnpm recursive outdated', async (t) => {
|
||||
}
|
||||
|
||||
{
|
||||
const output = await recursive.handler(['outdated'], {
|
||||
const output = await outdated.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
long: true,
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
|
||||
@@ -89,10 +94,11 @@ test('pnpm recursive outdated', async (t) => {
|
||||
}
|
||||
|
||||
{
|
||||
const output = await recursive.handler(['outdated'], {
|
||||
const output = await outdated.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
table: false,
|
||||
})
|
||||
@@ -113,11 +119,12 @@ test('pnpm recursive outdated', async (t) => {
|
||||
}
|
||||
|
||||
{
|
||||
const output = await recursive.handler(['outdated'], {
|
||||
const output = await outdated.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
long: true,
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
table: false,
|
||||
})
|
||||
@@ -141,10 +148,11 @@ test('pnpm recursive outdated', async (t) => {
|
||||
}
|
||||
|
||||
{
|
||||
const output = await recursive.handler(['outdated', 'is-positive'], {
|
||||
const output = await outdated.handler(['is-positive'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
|
||||
@@ -190,21 +198,22 @@ test('pnpm recursive outdated in workspace with shared lockfile', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
await writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
await install.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
workspaceDir: process.cwd(),
|
||||
}, 'install')
|
||||
|
||||
{
|
||||
const output = await recursive.handler(['outdated'], {
|
||||
const output = await outdated.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
|
||||
@@ -222,10 +231,11 @@ test('pnpm recursive outdated in workspace with shared lockfile', async (t) => {
|
||||
}
|
||||
|
||||
{
|
||||
const output = await recursive.handler(['outdated', 'is-positive'], {
|
||||
const output = await outdated.handler(['is-positive'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
|
||||
49
packages/plugin-commands-outdated/test/utils.ts
Normal file
49
packages/plugin-commands-outdated/test/utils.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
|
||||
const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}`
|
||||
|
||||
export const DEFAULT_OPTS = {
|
||||
alwaysAuth: false,
|
||||
argv: {
|
||||
original: [],
|
||||
},
|
||||
bail: false,
|
||||
ca: undefined,
|
||||
cert: undefined,
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
fetchRetryFactor: 90,
|
||||
fetchRetryMaxtimeout: 90,
|
||||
fetchRetryMintimeout: 10,
|
||||
filter: [] as string[],
|
||||
global: false,
|
||||
httpsProxy: undefined,
|
||||
include: {
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: true,
|
||||
},
|
||||
independentLeaves: false,
|
||||
key: undefined,
|
||||
linkWorkspacePackages: true,
|
||||
localAddress: undefined,
|
||||
lock: false,
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
pending: false,
|
||||
pnpmfile: './pnpmfile.js',
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
sort: true,
|
||||
storeDir: '../store',
|
||||
strictSsl: false,
|
||||
tag: 'latest',
|
||||
userAgent: 'pnpm',
|
||||
useRunningStoreServer: false,
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
"test:tap": "ts-node test --type-check",
|
||||
"pretest:e2e": "registry-mock prepare",
|
||||
"test:e2e": "run-p -r registry-mock test:tap",
|
||||
"test": "pnpm run tsc && pnpm run lint && cross-env PNPM_REGISTRY_MOCK_PORT=7775 pnpm run test:e2e",
|
||||
"test": "pnpm run tsc && pnpm run lint && cross-env PNPM_REGISTRY_MOCK_PORT=7771 pnpm run test:e2e",
|
||||
"prepublishOnly": "pnpm run tsc"
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/plugin-commands-publishing",
|
||||
@@ -33,12 +33,15 @@
|
||||
},
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/filter-workspace-packages": "workspace:1.0.1",
|
||||
"@pnpm/plugin-commands-publishing": "link:",
|
||||
"@pnpm/prepare": "workspace:0.0.0",
|
||||
"@types/cross-spawn": "^6.0.1",
|
||||
"@types/lru-cache": "^5.1.0",
|
||||
"@types/mz": "^2.7.0",
|
||||
"@types/ramda": "^0.26.38",
|
||||
"cross-spawn": "7.0.1",
|
||||
"execa": "4.0.0",
|
||||
"path-exists": "4.0.0",
|
||||
"rimraf": "3.0.0",
|
||||
"write-yaml-file": "3.0.1"
|
||||
@@ -47,13 +50,19 @@
|
||||
"@pnpm/cli-utils": "workspace:0.2.5",
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/error": "workspace:1.0.0",
|
||||
"@pnpm/npm-resolver": "workspace:6.0.2",
|
||||
"@pnpm/read-importer-manifest": "workspace:2.0.1",
|
||||
"@pnpm/resolver-base": "workspace:6.0.0",
|
||||
"@pnpm/run-npm": "workspace:1.0.0",
|
||||
"@pnpm/store-path": "2.1.1",
|
||||
"@pnpm/types": "workspace:4.0.0",
|
||||
"@pnpm/utils": "workspace:0.12.2",
|
||||
"@zkochan/rimraf": "1.0.0",
|
||||
"cp-file": "7.0.0",
|
||||
"fast-glob": "3.1.1",
|
||||
"lru-cache": "5.1.1",
|
||||
"mz": "2.7.0",
|
||||
"p-filter": "2.1.0",
|
||||
"ramda": "0.26.1",
|
||||
"render-help": "0.0.0",
|
||||
"write-json-file": "4.2.1"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { docsUrl, readImporterManifest } from '@pnpm/cli-utils'
|
||||
import { types as allTypes } from '@pnpm/config'
|
||||
import { Config, types as allTypes } from '@pnpm/config'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { tryReadImporterManifest } from '@pnpm/read-importer-manifest'
|
||||
import runNpm from '@pnpm/run-npm'
|
||||
@@ -12,6 +12,7 @@ import path = require('path')
|
||||
import R = require('ramda')
|
||||
import renderHelp = require('render-help')
|
||||
import writeJsonFile = require('write-json-file')
|
||||
import recursivePublish, { PublishRecursiveOpts } from './recursivePublish'
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
@@ -35,15 +36,24 @@ export function help () {
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: {
|
||||
opts: Omit<PublishRecursiveOpts, 'workspaceDir'> & {
|
||||
argv: {
|
||||
original: string[],
|
||||
},
|
||||
engineStrict?: boolean,
|
||||
recursive?: boolean,
|
||||
workspaceDir?: string,
|
||||
},
|
||||
command: string,
|
||||
} & Pick<Config, 'allWsPkgs' | 'selectedWsPkgsGraph'>,
|
||||
command?: string,
|
||||
) {
|
||||
if (opts.recursive && opts.selectedWsPkgsGraph) {
|
||||
const pkgs = Object.values(opts.selectedWsPkgsGraph).map((wsPkg) => wsPkg.package)
|
||||
await recursivePublish(pkgs, {
|
||||
...opts,
|
||||
workspaceDir: opts.workspaceDir ?? process.cwd(),
|
||||
})
|
||||
return
|
||||
}
|
||||
if (args.length && args[0].endsWith('.tgz')) {
|
||||
await runNpm(['publish', ...args])
|
||||
return
|
||||
|
||||
@@ -1,43 +1,49 @@
|
||||
import { Config } from '@pnpm/config'
|
||||
import { Config, WsPkg } from '@pnpm/config'
|
||||
import createResolver from '@pnpm/npm-resolver'
|
||||
import { publish } from '@pnpm/plugin-commands-publishing'
|
||||
import { ResolveFunction } from '@pnpm/resolver-base'
|
||||
import runNpm from '@pnpm/run-npm'
|
||||
import storePath from '@pnpm/store-path'
|
||||
import { ImporterManifest, Registries } from '@pnpm/types'
|
||||
import { Registries } from '@pnpm/types'
|
||||
import { pickRegistryForPackage } from '@pnpm/utils'
|
||||
import LRU = require('lru-cache')
|
||||
import pFilter = require('p-filter')
|
||||
import { handler as publish } from './publish'
|
||||
|
||||
export type PublishRecursiveOpts = Required<Pick<Config,
|
||||
'cliOptions' |
|
||||
'dir' |
|
||||
'rawConfig' |
|
||||
'registries' |
|
||||
'workspaceDir'
|
||||
>> &
|
||||
Partial<Pick<Config,
|
||||
'tag' |
|
||||
'ca' |
|
||||
'cert' |
|
||||
'fetchRetries' |
|
||||
'fetchRetryFactor' |
|
||||
'fetchRetryMaxtimeout' |
|
||||
'fetchRetryMintimeout' |
|
||||
'httpsProxy' |
|
||||
'key' |
|
||||
'localAddress' |
|
||||
'lockfileDir' |
|
||||
'offline' |
|
||||
'proxy' |
|
||||
'storeDir' |
|
||||
'strictSsl' |
|
||||
'userAgent' |
|
||||
'verifyStoreIntegrity'
|
||||
>> & {
|
||||
access?: 'public' | 'restricted',
|
||||
argv: {
|
||||
original: string[],
|
||||
},
|
||||
}
|
||||
|
||||
export default async function (
|
||||
pkgs: Array<{ dir: string, manifest: ImporterManifest }>,
|
||||
opts: Pick<Config, 'cliOptions'> & {
|
||||
access?: 'public' | 'restricted',
|
||||
argv: {
|
||||
original: string[],
|
||||
},
|
||||
tag?: string,
|
||||
ca?: string,
|
||||
cert?: string,
|
||||
fetchRetries?: number,
|
||||
fetchRetryFactor?: number,
|
||||
fetchRetryMaxtimeout?: number,
|
||||
fetchRetryMintimeout?: number,
|
||||
httpsProxy?: string,
|
||||
key?: string,
|
||||
localAddress?: string,
|
||||
lockfileDir?: string,
|
||||
offline?: boolean,
|
||||
dir: string,
|
||||
proxy?: string,
|
||||
rawConfig: object,
|
||||
registries: Registries,
|
||||
storeDir?: string,
|
||||
strictSsl?: boolean,
|
||||
userAgent?: string,
|
||||
verifyStoreIntegrity?: boolean,
|
||||
workspaceDir: string,
|
||||
},
|
||||
pkgs: WsPkg[],
|
||||
opts: PublishRecursiveOpts,
|
||||
) {
|
||||
const storeDir = await storePath(opts.workspaceDir, opts.storeDir)
|
||||
const resolve = createResolver(Object.assign(opts, {
|
||||
@@ -59,7 +65,8 @@ export default async function (
|
||||
})
|
||||
const access = opts.cliOptions['access'] ? ['--access', opts.cliOptions['access']] : []
|
||||
for (const pkg of pkgsToPublish) {
|
||||
await publish.handler([pkg.dir], {
|
||||
await publish([pkg.dir], {
|
||||
...opts,
|
||||
argv: {
|
||||
original: [
|
||||
'publish',
|
||||
@@ -71,7 +78,7 @@ export default async function (
|
||||
...access,
|
||||
],
|
||||
},
|
||||
workspaceDir: opts.workspaceDir,
|
||||
recursive: false,
|
||||
}, 'publish')
|
||||
}
|
||||
const tag = opts.tag || 'latest'
|
||||
@@ -1,3 +1,4 @@
|
||||
///<reference path="../../../typings/index.d.ts" />
|
||||
import './pack'
|
||||
import './publish'
|
||||
import './recursivePublish'
|
||||
|
||||
@@ -8,6 +8,7 @@ import path = require('path')
|
||||
import exists = require('path-exists')
|
||||
import test = require('tape')
|
||||
import writeYamlFile = require('write-yaml-file')
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
const CREDENTIALS = [
|
||||
`--registry=http://localhost:${REGISTRY_MOCK_PORT}/`,
|
||||
@@ -22,7 +23,11 @@ test('publish: package with package.json', async (t) => {
|
||||
version: '0.0.0',
|
||||
})
|
||||
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] } }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
}, 'publish')
|
||||
t.end()
|
||||
})
|
||||
|
||||
@@ -32,7 +37,11 @@ test('publish: package with package.yaml', async (t) => {
|
||||
version: '0.0.0',
|
||||
}, { manifestFormat: 'YAML' })
|
||||
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] } }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
}, 'publish')
|
||||
|
||||
t.ok(await exists('package.yaml'))
|
||||
t.notOk(await exists('package.json'))
|
||||
@@ -45,7 +54,11 @@ test('publish: package with package.json5', async (t) => {
|
||||
version: '0.0.0',
|
||||
}, { manifestFormat: 'JSON5' })
|
||||
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] } }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
}, 'publish')
|
||||
|
||||
t.ok(await exists('package.json5'))
|
||||
t.notOk(await exists('package.json'))
|
||||
@@ -60,7 +73,11 @@ test('publish: package with package.json5 running publish from different folder'
|
||||
|
||||
process.chdir('..')
|
||||
|
||||
await publish.handler(['project'], { argv: { original: ['publish', ...CREDENTIALS, 'project'] } }, 'publish')
|
||||
await publish.handler(['project'], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS, 'project'] },
|
||||
dir: process.cwd(),
|
||||
}, 'publish')
|
||||
|
||||
t.ok(await exists('project/package.json5'))
|
||||
t.notOk(await exists('project/package.json'))
|
||||
@@ -129,10 +146,20 @@ test('publish packages with workspace LICENSE if no own LICENSE is present', asy
|
||||
await fs.writeFile('project-200/LICENSE', 'project-200 license', 'utf8')
|
||||
|
||||
process.chdir('project-100')
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] }, workspaceDir }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
workspaceDir,
|
||||
}, 'publish')
|
||||
|
||||
process.chdir('../project-200')
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] }, workspaceDir }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
workspaceDir,
|
||||
}, 'publish')
|
||||
|
||||
process.chdir('../target')
|
||||
|
||||
@@ -181,7 +208,11 @@ test('publish: package with all possible fields in publishConfig', async (t) =>
|
||||
|
||||
process.chdir('test-publish-config')
|
||||
await fs.writeFile('published-bin.js', `#!/usr/bin/env node`, 'utf8')
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] } }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
}, 'publish')
|
||||
|
||||
const originalManifests = await import(path.resolve('package.json'))
|
||||
t.deepEqual(originalManifests, {
|
||||
@@ -286,7 +317,12 @@ test.skip('publish package that calls executable from the workspace .bin folder
|
||||
await writeYamlFile('pnpm-workspace.yaml', { packages: ['**', '!store/**'] })
|
||||
|
||||
process.chdir('test-publish-scripts')
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] }, workspaceDir }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
workspaceDir,
|
||||
}, 'publish')
|
||||
|
||||
t.deepEqual(
|
||||
await import(path.resolve('output.json')),
|
||||
@@ -353,7 +389,11 @@ test('convert specs with workspace protocols to regular version ranges', async (
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] } }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
}, 'publish')
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
}
|
||||
@@ -368,7 +408,11 @@ test('convert specs with workspace protocols to regular version ranges', async (
|
||||
crossSpawn.sync('pnpm', ['multi', 'install', '--store-dir=store', `--registry=http://localhost:${REGISTRY_MOCK_PORT}`])
|
||||
|
||||
process.chdir('workspace-protocol-package')
|
||||
await publish.handler([], { argv: { original: ['publish', ...CREDENTIALS] } }, 'publish')
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
argv: { original: ['publish', ...CREDENTIALS] },
|
||||
dir: process.cwd(),
|
||||
}, 'publish')
|
||||
|
||||
process.chdir('../target')
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { publish } from '@pnpm/plugin-commands-publishing'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
import execa = require('execa')
|
||||
import fs = require('mz/fs')
|
||||
import test = require('tape')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
const CREDENTIALS = [
|
||||
`--registry=http://localhost:${REGISTRY_MOCK_PORT}/`,
|
||||
@@ -65,10 +66,11 @@ test('recursive publish', async (t) => {
|
||||
|
||||
await fs.writeFile('.npmrc', CREDENTIALS, 'utf8')
|
||||
|
||||
await recursive.handler(['publish'], {
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
})
|
||||
|
||||
{
|
||||
@@ -82,10 +84,11 @@ test('recursive publish', async (t) => {
|
||||
|
||||
await projects[pkg1.name].writePackageJson({ ...pkg1, version: '2.0.0' })
|
||||
|
||||
await recursive.handler(['publish'], {
|
||||
await publish.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
tag: 'next',
|
||||
})
|
||||
|
||||
46
packages/plugin-commands-publishing/test/utils.ts
Normal file
46
packages/plugin-commands-publishing/test/utils.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
|
||||
const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}`
|
||||
|
||||
export const DEFAULT_OPTS = {
|
||||
alwaysAuth: false,
|
||||
argv: {
|
||||
original: [],
|
||||
},
|
||||
bail: false,
|
||||
ca: undefined,
|
||||
cert: undefined,
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
fetchRetryFactor: 90,
|
||||
fetchRetryMaxtimeout: 90,
|
||||
fetchRetryMintimeout: 10,
|
||||
filter: [] as string[],
|
||||
httpsProxy: undefined,
|
||||
include: {
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: true,
|
||||
},
|
||||
key: undefined,
|
||||
linkWorkspacePackages: true,
|
||||
localAddress: undefined,
|
||||
lock: false,
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
pending: false,
|
||||
pnpmfile: './pnpmfile.js',
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
sort: true,
|
||||
storeDir: '../store',
|
||||
strictSsl: false,
|
||||
userAgent: 'pnpm',
|
||||
useRunningStoreServer: false,
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
},
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/filter-workspace-packages": "workspace:1.0.1",
|
||||
"@pnpm/logger": "^3.1.0",
|
||||
"@pnpm/plugin-commands-rebuild": "link:",
|
||||
"@pnpm/prepare": "workspace:0.0.0",
|
||||
@@ -44,7 +45,8 @@
|
||||
"execa": "4.0.0",
|
||||
"path-exists": "4.0.0",
|
||||
"rimraf": "3.0.0",
|
||||
"sinon": "8.0.0"
|
||||
"sinon": "8.0.0",
|
||||
"write-yaml-file": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/cli-utils": "workspace:0.2.5",
|
||||
@@ -52,6 +54,7 @@
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/constants": "workspace:3.0.0",
|
||||
"@pnpm/core-loggers": "workspace:4.0.0",
|
||||
"@pnpm/find-workspace-packages": "workspace:2.0.8",
|
||||
"@pnpm/get-context": "workspace:0.0.0",
|
||||
"@pnpm/lifecycle": "workspace:8.0.1",
|
||||
"@pnpm/link-bins": "5.0.1",
|
||||
@@ -59,17 +62,21 @@
|
||||
"@pnpm/lockfile-walker": "workspace:1.0.1",
|
||||
"@pnpm/modules-yaml": "workspace:5.0.0",
|
||||
"@pnpm/pkgid-to-filename": "2.0.0",
|
||||
"@pnpm/sort-packages": "workspace:0.0.0",
|
||||
"@pnpm/store-connection-manager": "workspace:0.2.5",
|
||||
"@pnpm/store-controller-types": "workspace:6.0.0",
|
||||
"@pnpm/types": "workspace:4.0.0",
|
||||
"@pnpm/utils": "workspace:0.12.2",
|
||||
"@zkochan/npm-package-arg": "1.0.2",
|
||||
"camelcase-keys": "6.1.1",
|
||||
"common-tags": "1.8.0",
|
||||
"dependency-path": "workspace:4.0.2",
|
||||
"graph-sequencer": "2.0.0",
|
||||
"load-json-file": "6.2.0",
|
||||
"mem": "6.0.1",
|
||||
"p-limit": "2.2.1",
|
||||
"ramda": "0.26.1",
|
||||
"read-ini-file": "2.0.0",
|
||||
"render-help": "0.0.0",
|
||||
"run-groups": "2.0.1",
|
||||
"semver": "7.1.1"
|
||||
|
||||
@@ -34,6 +34,8 @@ import extendOptions, {
|
||||
StrictRebuildOptions,
|
||||
} from './extendRebuildOptions'
|
||||
|
||||
export { RebuildOptions }
|
||||
|
||||
function findPackages (
|
||||
packages: PackageSnapshots,
|
||||
searched: PackageSelector[],
|
||||
|
||||
@@ -2,7 +2,10 @@ import { docsUrl, readImporterManifestOnly } from '@pnpm/cli-utils'
|
||||
import { FILTERING, UNIVERSAL_OPTIONS } from '@pnpm/common-cli-options-help'
|
||||
import { Config, types as allTypes } from '@pnpm/config'
|
||||
import { LogBase } from '@pnpm/logger'
|
||||
import { CreateStoreControllerOptions, createOrConnectStoreController } from '@pnpm/store-connection-manager'
|
||||
import {
|
||||
createOrConnectStoreController,
|
||||
CreateStoreControllerOptions,
|
||||
} from '@pnpm/store-connection-manager'
|
||||
import { oneLine } from 'common-tags'
|
||||
import R = require('ramda')
|
||||
import renderHelp = require('render-help')
|
||||
@@ -10,6 +13,7 @@ import {
|
||||
rebuild,
|
||||
rebuildPkgs,
|
||||
} from './implementation'
|
||||
import recursive from './recursive'
|
||||
|
||||
export function rcOptionsTypes () {
|
||||
return {}
|
||||
@@ -58,10 +62,18 @@ export function help () {
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: Pick<Config, 'dir' | 'engineStrict' | 'independentLeaves'> &
|
||||
opts: Pick<Config, 'allWsPkgs' | 'dir' | 'engineStrict' | 'independentLeaves' | 'rawLocalConfig' | 'registries' | 'selectedWsPkgsGraph' | 'workspaceDir'> &
|
||||
CreateStoreControllerOptions &
|
||||
{ reporter?: (logObj: LogBase) => void, pending: boolean },
|
||||
{
|
||||
recursive?: boolean,
|
||||
reporter?: (logObj: LogBase) => void,
|
||||
pending: boolean,
|
||||
},
|
||||
) {
|
||||
if (opts.recursive && opts.allWsPkgs && opts.selectedWsPkgsGraph && opts.workspaceDir) {
|
||||
await recursive(opts.allWsPkgs, args, { ...opts, selectedWsPkgsGraph: opts.selectedWsPkgsGraph!, workspaceDir: opts.workspaceDir! })
|
||||
return
|
||||
}
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
const rebuildOpts = Object.assign(opts, {
|
||||
storeController: store.ctrl,
|
||||
|
||||
187
packages/plugin-commands-rebuild/src/recursive.ts
Executable file
187
packages/plugin-commands-rebuild/src/recursive.ts
Executable file
@@ -0,0 +1,187 @@
|
||||
import {
|
||||
RecursiveSummary,
|
||||
throwOnCommandFail,
|
||||
} from '@pnpm/cli-utils'
|
||||
import { Config, types as allTypes, WsPkg, WsPkgsGraph } from '@pnpm/config'
|
||||
import { scopeLogger } from '@pnpm/core-loggers'
|
||||
import { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages'
|
||||
import logger from '@pnpm/logger'
|
||||
import sortPackages from '@pnpm/sort-packages'
|
||||
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
|
||||
import { ImporterManifest, PackageManifest } from '@pnpm/types'
|
||||
import camelcaseKeys = require('camelcase-keys')
|
||||
import mem = require('mem')
|
||||
import pLimit from 'p-limit'
|
||||
import path = require('path')
|
||||
import readIniFile = require('read-ini-file')
|
||||
import { rebuild as rebuildAll, RebuildOptions, rebuildPkgs } from './implementation'
|
||||
|
||||
type RecursiveRebuildOpts = CreateStoreControllerOptions & Pick<Config,
|
||||
'hoistPattern' |
|
||||
'ignorePnpmfile' |
|
||||
'ignoreScripts' |
|
||||
'lockfileDir' |
|
||||
'lockfileOnly' |
|
||||
'rawLocalConfig' |
|
||||
'registries' |
|
||||
'sharedWorkspaceLockfile'
|
||||
> & {
|
||||
pending?: boolean,
|
||||
} & Partial<Pick<Config, 'bail' | 'sort' | 'workspaceConcurrency'>>
|
||||
|
||||
export default async function recursive (
|
||||
allWsPkgs: WsPkg[],
|
||||
input: string[],
|
||||
opts: RecursiveRebuildOpts & {
|
||||
ignoredPackages?: Set<string>,
|
||||
} & Required<Pick<Config, 'selectedWsPkgsGraph' | 'workspaceDir'>>,
|
||||
) {
|
||||
if (allWsPkgs.length === 0) {
|
||||
// It might make sense to throw an exception in this case
|
||||
return
|
||||
}
|
||||
|
||||
const pkgs = Object.values(opts.selectedWsPkgsGraph).map((wsPkg) => wsPkg.package)
|
||||
|
||||
if (pkgs.length === 0) {
|
||||
return
|
||||
}
|
||||
const manifestsByPath: { [dir: string]: Omit<WsPkg, 'dir'> } = {}
|
||||
for (const { dir, manifest, writeImporterManifest } of pkgs) {
|
||||
manifestsByPath[dir] = { manifest, writeImporterManifest }
|
||||
}
|
||||
|
||||
scopeLogger.debug({
|
||||
selected: pkgs.length,
|
||||
total: allWsPkgs.length,
|
||||
workspacePrefix: opts.workspaceDir,
|
||||
})
|
||||
|
||||
const throwOnFail = throwOnCommandFail.bind(null, `pnpm recursive rebuild`)
|
||||
|
||||
const chunks = opts.sort !== false
|
||||
? sortPackages(opts.selectedWsPkgsGraph)
|
||||
: [Object.keys(opts.selectedWsPkgsGraph).sort()]
|
||||
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
|
||||
const workspacePackages = arrayOfWorkspacePackagesToMap(allWsPkgs)
|
||||
const rebuildOpts = Object.assign(opts, {
|
||||
ownLifecycleHooksStdio: 'pipe',
|
||||
pruneLockfileImporters: (!opts.ignoredPackages || opts.ignoredPackages.size === 0)
|
||||
&& pkgs.length === allWsPkgs.length,
|
||||
storeController: store.ctrl,
|
||||
storeDir: store.dir,
|
||||
workspacePackages,
|
||||
}) as RebuildOptions
|
||||
|
||||
const result = {
|
||||
fails: [],
|
||||
passes: 0,
|
||||
} as RecursiveSummary
|
||||
|
||||
const memReadLocalConfig = mem(readLocalConfig)
|
||||
|
||||
async function getImporters () {
|
||||
const importers = [] as Array<{ buildIndex: number, manifest: ImporterManifest, rootDir: string }>
|
||||
await Promise.all(chunks.map((prefixes: string[], buildIndex) => {
|
||||
if (opts.ignoredPackages) {
|
||||
prefixes = prefixes.filter((prefix) => !opts.ignoredPackages!.has(prefix))
|
||||
}
|
||||
return Promise.all(
|
||||
prefixes.map(async (prefix) => {
|
||||
importers.push({
|
||||
buildIndex,
|
||||
manifest: manifestsByPath[prefix].manifest,
|
||||
rootDir: prefix,
|
||||
})
|
||||
})
|
||||
)
|
||||
}))
|
||||
return importers
|
||||
}
|
||||
|
||||
const rebuild = (
|
||||
input.length === 0
|
||||
? rebuildAll
|
||||
: (importers: any, opts: any) => rebuildPkgs(importers, input, opts) // tslint:disable-line
|
||||
)
|
||||
if (opts.lockfileDir) {
|
||||
const importers = await getImporters()
|
||||
await rebuild(
|
||||
importers,
|
||||
{
|
||||
...rebuildOpts,
|
||||
pending: opts.pending === true,
|
||||
},
|
||||
)
|
||||
return
|
||||
}
|
||||
const limitRebuild = pLimit(opts.workspaceConcurrency ?? 4)
|
||||
for (const chunk of chunks) {
|
||||
await Promise.all(chunk.map((rootDir: string) =>
|
||||
limitRebuild(async () => {
|
||||
try {
|
||||
if (opts.ignoredPackages && opts.ignoredPackages.has(rootDir)) {
|
||||
return
|
||||
}
|
||||
const localConfig = await memReadLocalConfig(rootDir)
|
||||
await rebuild(
|
||||
[
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: manifestsByPath[rootDir].manifest,
|
||||
rootDir,
|
||||
},
|
||||
],
|
||||
{
|
||||
...rebuildOpts,
|
||||
...localConfig,
|
||||
dir: rootDir,
|
||||
pending: opts.pending === true,
|
||||
rawConfig: {
|
||||
...rebuildOpts.rawConfig,
|
||||
...localConfig,
|
||||
},
|
||||
},
|
||||
)
|
||||
result.passes++
|
||||
} catch (err) {
|
||||
logger.info(err)
|
||||
|
||||
if (!opts.bail) {
|
||||
result.fails.push({
|
||||
error: err,
|
||||
message: err.message,
|
||||
prefix: rootDir,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err['prefix'] = rootDir // tslint:disable-line:no-string-literal
|
||||
throw err
|
||||
}
|
||||
}),
|
||||
))
|
||||
}
|
||||
|
||||
throwOnFail(result)
|
||||
}
|
||||
|
||||
async function readLocalConfig (prefix: string) {
|
||||
try {
|
||||
const ini = await readIniFile(path.join(prefix, '.npmrc')) as { [key: string]: string }
|
||||
const config = camelcaseKeys(ini) as ({ [key: string]: string } & { hoist?: boolean })
|
||||
if (config.shamefullyFlatten) {
|
||||
config.hoistPattern = '*'
|
||||
// TODO: print a warning
|
||||
}
|
||||
if (config.hoist === false) {
|
||||
config.hoistPattern = ''
|
||||
}
|
||||
return config
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
return {}
|
||||
}
|
||||
}
|
||||
@@ -11,16 +11,10 @@ import exists = require('path-exists')
|
||||
import sinon = require('sinon')
|
||||
import test = require('tape')
|
||||
import { promisify } from 'util'
|
||||
import './recursive'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}/`
|
||||
const DEFAULT_OPTS = {
|
||||
independentLeaves: false,
|
||||
lock: false,
|
||||
rawConfig: {
|
||||
registry: `http://localhost:${REGISTRY_MOCK_PORT}/`,
|
||||
},
|
||||
registries: { default: `http://localhost:${REGISTRY_MOCK_PORT}/` },
|
||||
}
|
||||
|
||||
test('rebuilds dependencies', async (t) => {
|
||||
const project = prepareEmpty(t)
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { rebuild } from '@pnpm/plugin-commands-rebuild'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { PackageManifest } from '@pnpm/types'
|
||||
import execa = require('execa')
|
||||
import path = require('path')
|
||||
import test = require('tape')
|
||||
import writeYamlFile = require('write-yaml-file')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS, REGISTRY } from './utils'
|
||||
|
||||
test('pnpm recursive rebuild', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -27,24 +29,28 @@ test('pnpm recursive rebuild', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
ignoreScripts: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
'--ignore-scripts',
|
||||
])
|
||||
|
||||
await projects['project-1'].hasNot('pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
||||
await projects['project-1'].hasNot('pre-and-postinstall-scripts-example/generated-by-postinstall.js')
|
||||
await projects['project-2'].hasNot('pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
||||
await projects['project-2'].hasNot('pre-and-postinstall-scripts-example/generated-by-postinstall.js')
|
||||
|
||||
await recursive.handler(['rebuild'], {
|
||||
await rebuild.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
await projects['project-1'].has('pre-and-postinstall-scripts-example/generated-by-preinstall.js')
|
||||
@@ -103,19 +109,23 @@ test.skip('rebuild multiple packages in correct order', async (t) => {
|
||||
await writeYamlFile('pnpm-workspace.yaml', { packages: ['project-1'] })
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
ignoreScripts: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
'--ignore-scripts',
|
||||
])
|
||||
|
||||
await recursive.handler(['rebuild'], {
|
||||
await rebuild.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const outputs1 = await import(path.resolve('output1.json')) as string[]
|
||||
46
packages/plugin-commands-rebuild/test/utils.ts
Normal file
46
packages/plugin-commands-rebuild/test/utils.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
|
||||
export const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}`
|
||||
|
||||
export const DEFAULT_OPTS = {
|
||||
alwaysAuth: false,
|
||||
argv: {
|
||||
original: [],
|
||||
},
|
||||
bail: false,
|
||||
ca: undefined,
|
||||
cert: undefined,
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
fetchRetryFactor: 90,
|
||||
fetchRetryMaxtimeout: 90,
|
||||
fetchRetryMintimeout: 10,
|
||||
filter: [] as string[],
|
||||
httpsProxy: undefined,
|
||||
include: {
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: true,
|
||||
},
|
||||
key: undefined,
|
||||
linkWorkspacePackages: true,
|
||||
localAddress: undefined,
|
||||
lock: false,
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
pending: false,
|
||||
pnpmfile: './pnpmfile.js',
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
sort: true,
|
||||
storeDir: '../store',
|
||||
strictSsl: false,
|
||||
userAgent: 'pnpm',
|
||||
useRunningStoreServer: false,
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# @pnpm/plugin-commands-recursive
|
||||
|
||||
> Recursive commands
|
||||
|
||||
[](https://www.npmjs.com/package/@pnpm/plugin-commands-recursive)
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
<pnpm|npm|yarn> add @pnpm/plugin-commands-recursive
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Zoltan Kochan](https://www.kochan.io/)
|
||||
@@ -1,98 +0,0 @@
|
||||
{
|
||||
"name": "@pnpm/plugin-commands-recursive",
|
||||
"version": "0.1.10",
|
||||
"description": "Recursive commands",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts",
|
||||
"tsc": "rimraf lib && tsc",
|
||||
"registry-mock": "registry-mock",
|
||||
"test:tap": "ts-node test --type-check",
|
||||
"pretest:e2e": "registry-mock prepare",
|
||||
"test:e2e": "run-p -r registry-mock test:tap",
|
||||
"test": "pnpm run tsc && pnpm run lint && cross-env PNPM_REGISTRY_MOCK_PORT=7777 pnpm run test:e2e",
|
||||
"prepublishOnly": "pnpm run tsc"
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/plugin-commands-recursive",
|
||||
"keywords": [
|
||||
"pnpm"
|
||||
],
|
||||
"author": "Zoltan Kochan <z@kochan.io> (https://www.kochan.io/)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/lockfile-types": "workspace:1.1.0",
|
||||
"@pnpm/logger": "3.1.0",
|
||||
"@pnpm/plugin-commands-recursive": "link:",
|
||||
"@pnpm/prepare": "workspace:0.0.0",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/lru-cache": "^5.1.0",
|
||||
"@types/mz": "^2.7.0",
|
||||
"@types/ramda": "^0.26.38",
|
||||
"@types/table": "^4.0.7",
|
||||
"@zkochan/rimraf": "1.0.0",
|
||||
"make-dir": "3.0.0",
|
||||
"path-exists": "4.0.0",
|
||||
"read-yaml-file": "1.1.0",
|
||||
"rimraf": "3.0.0",
|
||||
"strip-ansi": "6.0.0",
|
||||
"write-json-file": "4.2.1",
|
||||
"write-yaml-file": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/cli-utils": "workspace:0.2.5",
|
||||
"@pnpm/common-cli-options-help": "workspace:0.1.2",
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/constants": "workspace:3.0.0",
|
||||
"@pnpm/core-loggers": "workspace:4.0.0",
|
||||
"@pnpm/error": "workspace:1.0.0",
|
||||
"@pnpm/filter-workspace-packages": "workspace:1.0.1",
|
||||
"@pnpm/find-workspace-packages": "workspace:2.0.8",
|
||||
"@pnpm/lifecycle": "workspace:8.0.1",
|
||||
"@pnpm/lockfile-file": "workspace:3.0.1",
|
||||
"@pnpm/matcher": "workspace:1.0.0",
|
||||
"@pnpm/npm-resolver": "workspace:6.0.2",
|
||||
"@pnpm/outdated": "workspace:6.0.5",
|
||||
"@pnpm/plugin-commands-listing": "workspace:0.1.9",
|
||||
"@pnpm/plugin-commands-outdated": "workspace:0.1.9",
|
||||
"@pnpm/plugin-commands-publishing": "workspace:0.1.9",
|
||||
"@pnpm/plugin-commands-rebuild": "workspace:0.0.0",
|
||||
"@pnpm/pnpmfile": "workspace:0.1.0",
|
||||
"@pnpm/resolver-base": "workspace:6.0.0",
|
||||
"@pnpm/run-npm": "workspace:1.0.0",
|
||||
"@pnpm/store-connection-manager": "workspace:0.2.5",
|
||||
"@pnpm/store-path": "2.1.1",
|
||||
"@pnpm/types": "workspace:4.0.0",
|
||||
"@pnpm/utils": "workspace:0.12.2",
|
||||
"camelcase-keys": "6.1.1",
|
||||
"chalk": "3.0.0",
|
||||
"common-tags": "1.8.0",
|
||||
"execa": "4.0.0",
|
||||
"graph-sequencer": "2.0.0",
|
||||
"is-subdir": "1.1.1",
|
||||
"lru-cache": "5.1.1",
|
||||
"mem": "6.0.1",
|
||||
"mz": "2.7.0",
|
||||
"p-filter": "2.1.0",
|
||||
"p-limit": "2.2.1",
|
||||
"pkgs-graph": "workspace:5.0.1",
|
||||
"ramda": "0.26.1",
|
||||
"read-ini-file": "2.0.0",
|
||||
"render-help": "0.0.0",
|
||||
"supi": "workspace:0.37.6",
|
||||
"table": "5.4.6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pnpm/logger": "^3.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
import { WsPkgsGraph } from '@pnpm/config'
|
||||
import logger from '@pnpm/logger'
|
||||
import execa = require('execa')
|
||||
import pLimit from 'p-limit'
|
||||
import RecursiveSummary from './recursiveSummary'
|
||||
|
||||
export default async <T> (
|
||||
packageChunks: string[][],
|
||||
graph: WsPkgsGraph,
|
||||
args: string[],
|
||||
cmd: string,
|
||||
opts: {
|
||||
bail: boolean,
|
||||
workspaceConcurrency: number,
|
||||
unsafePerm: boolean,
|
||||
rawConfig: object,
|
||||
},
|
||||
): Promise<RecursiveSummary> => {
|
||||
const limitRun = pLimit(opts.workspaceConcurrency)
|
||||
|
||||
const result = {
|
||||
fails: [],
|
||||
passes: 0,
|
||||
} as RecursiveSummary
|
||||
|
||||
for (const chunk of packageChunks) {
|
||||
await Promise.all(chunk.map((prefix: string) =>
|
||||
limitRun(async () => {
|
||||
try {
|
||||
await execa(args[0], args.slice(1), {
|
||||
cwd: prefix,
|
||||
env: {
|
||||
...process.env,
|
||||
PNPM_PACKAGE_NAME: graph[prefix].package.manifest.name,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
})
|
||||
result.passes++
|
||||
} catch (err) {
|
||||
logger.info(err)
|
||||
|
||||
if (!opts.bail) {
|
||||
result.fails.push({
|
||||
error: err,
|
||||
message: err.message,
|
||||
prefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// tslint:disable:no-string-literal
|
||||
err['code'] = 'ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL'
|
||||
err['prefix'] = prefix
|
||||
// tslint:enable:no-string-literal
|
||||
throw err
|
||||
}
|
||||
},
|
||||
)))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import * as recursive from './recursive'
|
||||
|
||||
export { recursive }
|
||||
@@ -1,811 +0,0 @@
|
||||
import {
|
||||
createLatestSpecs,
|
||||
docsUrl,
|
||||
getPinnedVersion,
|
||||
getSaveType,
|
||||
updateToLatestSpecsFromManifest,
|
||||
} from '@pnpm/cli-utils'
|
||||
import { FILTERING } from '@pnpm/common-cli-options-help'
|
||||
import { Config, types as allTypes, WsPkg, WsPkgsGraph } from '@pnpm/config'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { scopeLogger } from '@pnpm/core-loggers'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import filterGraph, { PackageSelector, parsePackageSelector } from '@pnpm/filter-workspace-packages'
|
||||
import findWorkspacePackages, { arrayOfWorkspacePackagesToMap } from '@pnpm/find-workspace-packages'
|
||||
import logger from '@pnpm/logger'
|
||||
import { rebuild, rebuildPkgs } from '@pnpm/plugin-commands-rebuild/lib/implementation'
|
||||
import { requireHooks } from '@pnpm/pnpmfile'
|
||||
import { createOrConnectStoreController, CreateStoreControllerOptions } from '@pnpm/store-connection-manager'
|
||||
import { DependencyManifest, ImporterManifest, PackageManifest } from '@pnpm/types'
|
||||
import camelcaseKeys = require('camelcase-keys')
|
||||
import { oneLine } from 'common-tags'
|
||||
import graphSequencer = require('graph-sequencer')
|
||||
import isSubdir = require('is-subdir')
|
||||
import mem = require('mem')
|
||||
import fs = require('mz/fs')
|
||||
import pFilter = require('p-filter')
|
||||
import pLimit from 'p-limit'
|
||||
import path = require('path')
|
||||
import createPkgGraph, { PackageNode } from 'pkgs-graph'
|
||||
import R = require('ramda')
|
||||
import readIniFile = require('read-ini-file')
|
||||
import renderHelp = require('render-help')
|
||||
import {
|
||||
addDependenciesToPackage,
|
||||
install,
|
||||
InstallOptions,
|
||||
MutatedImporter,
|
||||
mutateModules,
|
||||
} from 'supi'
|
||||
import exec from './exec'
|
||||
import list from './list'
|
||||
import outdated from './outdated'
|
||||
import publish from './publish'
|
||||
import RecursiveSummary, { throwOnCommandFail } from './recursiveSummary'
|
||||
import run from './run'
|
||||
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies'
|
||||
|
||||
const supportedRecursiveCommands = new Set([
|
||||
'add',
|
||||
'install',
|
||||
'remove',
|
||||
'update',
|
||||
'unlink',
|
||||
'list',
|
||||
'why',
|
||||
'outdated',
|
||||
'rebuild',
|
||||
'run',
|
||||
'test',
|
||||
'exec',
|
||||
'publish',
|
||||
])
|
||||
|
||||
function getCommandFullName (commandName: string) {
|
||||
switch (commandName) {
|
||||
case 'i':
|
||||
return 'install'
|
||||
case 'r':
|
||||
case 'rm':
|
||||
case 'un':
|
||||
case 'uninstall':
|
||||
return 'remove'
|
||||
case 'up':
|
||||
case 'upgrade':
|
||||
return 'update'
|
||||
case 'dislink':
|
||||
return 'unlink'
|
||||
case 'ls':
|
||||
case 'la':
|
||||
case 'll':
|
||||
return 'list'
|
||||
case 'rb':
|
||||
return 'rebuild'
|
||||
case 'run-script':
|
||||
return 'run'
|
||||
case 't':
|
||||
case 'tst':
|
||||
return 'test'
|
||||
}
|
||||
return commandName
|
||||
}
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
export function cliOptionsTypes () {
|
||||
return {
|
||||
access: ['public', 'restricted'],
|
||||
recursive: Boolean,
|
||||
table: Boolean,
|
||||
...R.pick([
|
||||
'bail',
|
||||
'link-workspace-packages',
|
||||
'reporter',
|
||||
'shared-workspace-lockfile',
|
||||
'sort',
|
||||
'tag',
|
||||
'workspace-concurrency',
|
||||
], allTypes),
|
||||
}
|
||||
}
|
||||
|
||||
export const commandNames = ['recursive', 'multi', 'm']
|
||||
|
||||
export function help () {
|
||||
return renderHelp({
|
||||
description: oneLine`
|
||||
Concurrently performs some actions in all subdirectories with a \`package.json\` (excluding node_modules).
|
||||
A \`pnpm-workspace.yaml\` file may be used to control what directories are searched for packages.`,
|
||||
descriptionLists: [
|
||||
{
|
||||
title: 'Commands',
|
||||
|
||||
list: [
|
||||
{
|
||||
name: 'install',
|
||||
},
|
||||
{
|
||||
name: 'add',
|
||||
},
|
||||
{
|
||||
name: 'update',
|
||||
},
|
||||
{
|
||||
description: 'Uninstall a dependency from each package',
|
||||
name: 'remove <pkg>...',
|
||||
},
|
||||
{
|
||||
description: 'Removes links to local packages and reinstalls them from the registry.',
|
||||
name: 'unlink',
|
||||
},
|
||||
{
|
||||
description: 'List dependencies in each package.',
|
||||
name: 'list [<pkg>...]',
|
||||
},
|
||||
{
|
||||
description: 'List packages that depend on <pkg>.',
|
||||
name: 'why <pkg>...',
|
||||
},
|
||||
{
|
||||
description: 'Check for outdated dependencies in every package.',
|
||||
name: 'outdated [<pkg>...]',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
This runs an arbitrary command from each package's "scripts" object.
|
||||
If a package doesn't have the command, it is skipped.
|
||||
If none of the packages have the command, the command fails.`,
|
||||
name: 'run <command> [-- <args>...]',
|
||||
},
|
||||
{
|
||||
description: `This runs each package's "test" script, if one was provided.`,
|
||||
name: 'test [-- <args>...]',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
This command runs the "npm build" command on each package.
|
||||
This is useful when you install a new version of node,
|
||||
and must recompile all your C++ addons with the new binary.`,
|
||||
name: 'rebuild [[<@scope>/<name>]...]',
|
||||
},
|
||||
{
|
||||
description: `Run a command in each package.`,
|
||||
name: 'exec -- <command> [args...]',
|
||||
},
|
||||
{
|
||||
description: 'Publishes packages to the npm registry. Only publishes a package if its version is not taken in the registry.',
|
||||
name: 'publish [--tag <tag>] [--access <public|restricted>]',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Options',
|
||||
|
||||
list: [
|
||||
{
|
||||
description: 'Continues executing other tasks even if a task threw an error.',
|
||||
name: '--no-bail',
|
||||
},
|
||||
{
|
||||
description: 'Set the maximum number of concurrency. Default is 4. For unlimited concurrency use Infinity.',
|
||||
name: '--workspace-concurrency <number>',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
Locally available packages are linked to node_modules instead of being downloaded from the registry.
|
||||
Convenient to use in a multi-package repository.`,
|
||||
name: '--link-workspace-packages',
|
||||
},
|
||||
{
|
||||
description: 'Sort packages topologically (dependencies before dependents). Pass --no-sort to disable.',
|
||||
name: '--sort',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
Creates a single ${WANTED_LOCKFILE} file in the root of the workspace.
|
||||
A shared lockfile also means that all dependencies of all workspace packages will be in a single node_modules.`,
|
||||
name: '--shared-workspace-lockfile',
|
||||
},
|
||||
],
|
||||
},
|
||||
FILTERING,
|
||||
],
|
||||
url: docsUrl('recursive'),
|
||||
usages: [
|
||||
'pnpm recursive [command] [flags] [--filter <package selector>]',
|
||||
'pnpm multi [command] [flags] [--filter <package selector>]',
|
||||
'pnpm m [command] [flags] [--filter <package selector>]'
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
export async function handler (
|
||||
input: string[],
|
||||
opts: RecursiveOptions & Pick<Config, 'filter' | 'depth' | 'engineStrict' | 'tag' | 'workspaceDir'> & { long?: boolean, table?: boolean } & Required<Pick<Config, 'allWsPkgs' | 'selectedWsPkgsGraph'>>,
|
||||
) {
|
||||
if (opts.workspaceConcurrency < 1) {
|
||||
throw new PnpmError('INVALID_WORKSPACE_CONCURRENCY', 'Workspace concurrency should be at least 1')
|
||||
}
|
||||
|
||||
const cmd = input.shift()
|
||||
if (!cmd) {
|
||||
help()
|
||||
return undefined
|
||||
}
|
||||
const cmdFullName = getCommandFullName(cmd)
|
||||
if (!supportedRecursiveCommands.has(cmdFullName)) {
|
||||
help()
|
||||
throw new PnpmError('INVALID_RECURSIVE_COMMAND',
|
||||
`"recursive ${cmdFullName}" is not a pnpm command. See "pnpm help recursive".`)
|
||||
}
|
||||
|
||||
const workspaceDir = opts.workspaceDir ?? process.cwd()
|
||||
|
||||
const atLeastOnePackageMatched = await recursive(opts.allWsPkgs, input, { ...opts, workspaceDir }, cmdFullName, cmd)
|
||||
|
||||
if (typeof atLeastOnePackageMatched === 'string') {
|
||||
return atLeastOnePackageMatched
|
||||
}
|
||||
|
||||
if (atLeastOnePackageMatched === false) {
|
||||
logger.info({ message: `No packages matched the filters in "${workspaceDir}"`, prefix: workspaceDir })
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
|
||||
'bail' |
|
||||
'cliOptions' |
|
||||
'globalPnpmfile' |
|
||||
'hoistPattern' |
|
||||
'ignorePnpmfile' |
|
||||
'ignoreScripts' |
|
||||
'include' |
|
||||
'linkWorkspacePackages' |
|
||||
'lockfileDir' |
|
||||
'lockfileOnly' |
|
||||
'pnpmfile' |
|
||||
'rawLocalConfig' |
|
||||
'registries' |
|
||||
'save' |
|
||||
'saveDev' |
|
||||
'saveExact' |
|
||||
'saveOptional' |
|
||||
'savePeer' |
|
||||
'savePrefix' |
|
||||
'saveProd' |
|
||||
'saveWorkspaceProtocol' |
|
||||
'sharedWorkspaceLockfile' |
|
||||
'sort' |
|
||||
'tag' |
|
||||
'workspaceConcurrency'
|
||||
> & {
|
||||
access?: 'public' | 'restricted',
|
||||
argv: {
|
||||
original: string[],
|
||||
},
|
||||
latest?: boolean,
|
||||
pending?: boolean,
|
||||
workspace?: boolean,
|
||||
}
|
||||
|
||||
export async function recursive (
|
||||
allWsPkgs: WsPkg[],
|
||||
input: string[],
|
||||
opts: RecursiveOptions & {
|
||||
allowNew?: boolean,
|
||||
ignoredPackages?: Set<string>,
|
||||
update?: boolean,
|
||||
useBetaCli?: boolean,
|
||||
selectedWsPkgsGraph: WsPkgsGraph,
|
||||
} & Required<Pick<Config, 'workspaceDir'>>,
|
||||
cmdFullName: string,
|
||||
cmd: string,
|
||||
): Promise<boolean | string> {
|
||||
if (allWsPkgs.length === 0) {
|
||||
// It might make sense to throw an exception in this case
|
||||
return false
|
||||
}
|
||||
|
||||
const pkgs = Object.values(opts.selectedWsPkgsGraph).map((wsPkg) => wsPkg.package)
|
||||
const allPackagesAreSelected = pkgs.length === allWsPkgs.length
|
||||
|
||||
if (pkgs.length === 0) {
|
||||
return false
|
||||
}
|
||||
const manifestsByPath: { [dir: string]: { manifest: ImporterManifest, writeImporterManifest: (manifest: ImporterManifest) => Promise<void> } } = {}
|
||||
for (const { dir, manifest, writeImporterManifest } of pkgs) {
|
||||
manifestsByPath[dir] = { manifest, writeImporterManifest }
|
||||
}
|
||||
|
||||
scopeLogger.debug({
|
||||
selected: pkgs.length,
|
||||
total: allWsPkgs.length,
|
||||
workspacePrefix: opts.workspaceDir,
|
||||
})
|
||||
|
||||
const throwOnFail = throwOnCommandFail.bind(null, `pnpm recursive ${cmd}`)
|
||||
|
||||
switch (cmdFullName) {
|
||||
case 'why':
|
||||
case 'list':
|
||||
return list(pkgs, input, cmd, opts as any) // tslint:disable-line:no-any
|
||||
case 'outdated':
|
||||
return outdated(pkgs, input, cmd, opts as any) // tslint:disable-line:no-any
|
||||
case 'add':
|
||||
if (!input || !input.length) {
|
||||
throw new PnpmError('MISSING_PACKAGE_NAME', '`pnpm recursive add` requires the package name')
|
||||
}
|
||||
break
|
||||
case 'publish': {
|
||||
await publish(pkgs, opts)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const chunks = opts.sort
|
||||
? sortPackages(opts.selectedWsPkgsGraph)
|
||||
: [Object.keys(opts.selectedWsPkgsGraph).sort()]
|
||||
|
||||
switch (cmdFullName) {
|
||||
case 'test':
|
||||
throwOnFail(await run(chunks, opts.selectedWsPkgsGraph, ['test', ...input], cmd, opts as any)) // tslint:disable-line:no-any
|
||||
return true
|
||||
case 'run':
|
||||
throwOnFail(await run(chunks, opts.selectedWsPkgsGraph, input, cmd, { ...opts, allPackagesAreSelected } as any)) // tslint:disable-line:no-any
|
||||
return true
|
||||
case 'update':
|
||||
opts = { ...opts, update: true, allowNew: false } as any // tslint:disable-line:no-any
|
||||
break
|
||||
case 'exec':
|
||||
throwOnFail(await exec(chunks, opts.selectedWsPkgsGraph, input, cmd, opts as any)) // tslint:disable-line:no-any
|
||||
return true
|
||||
}
|
||||
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
|
||||
// It is enough to save the store.json file once,
|
||||
// once all installations are done.
|
||||
// That's why saveState that is passed to the install engine
|
||||
// does nothing.
|
||||
const saveState = store.ctrl.saveState
|
||||
const storeController = {
|
||||
...store.ctrl,
|
||||
saveState: async () => undefined,
|
||||
}
|
||||
|
||||
const workspacePackages = cmdFullName !== 'unlink'
|
||||
? arrayOfWorkspacePackagesToMap(allWsPkgs)
|
||||
: {}
|
||||
const installOpts = Object.assign(opts, {
|
||||
ownLifecycleHooksStdio: 'pipe',
|
||||
peer: opts.savePeer,
|
||||
pruneLockfileImporters: (!opts.ignoredPackages || opts.ignoredPackages.size === 0)
|
||||
&& pkgs.length === allWsPkgs.length,
|
||||
storeController,
|
||||
storeDir: store.dir,
|
||||
workspacePackages,
|
||||
|
||||
forceHoistPattern: typeof opts.rawLocalConfig['hoist-pattern'] !== 'undefined' || typeof opts.rawLocalConfig['hoist'] !== 'undefined',
|
||||
forceIndependentLeaves: typeof opts.rawLocalConfig['independent-leaves'] !== 'undefined',
|
||||
forceShamefullyHoist: typeof opts.rawLocalConfig['shamefully-hoist'] !== 'undefined',
|
||||
}) as InstallOptions
|
||||
|
||||
const result = {
|
||||
fails: [],
|
||||
passes: 0,
|
||||
} as RecursiveSummary
|
||||
|
||||
const memReadLocalConfig = mem(readLocalConfig)
|
||||
|
||||
async function getImporters () {
|
||||
const importers = [] as Array<{ buildIndex: number, manifest: ImporterManifest, rootDir: string }>
|
||||
await Promise.all(chunks.map((prefixes: string[], buildIndex) => {
|
||||
if (opts.ignoredPackages) {
|
||||
prefixes = prefixes.filter((prefix) => !opts.ignoredPackages!.has(prefix))
|
||||
}
|
||||
return Promise.all(
|
||||
prefixes.map(async (prefix) => {
|
||||
importers.push({
|
||||
buildIndex,
|
||||
manifest: manifestsByPath[prefix].manifest,
|
||||
rootDir: prefix,
|
||||
})
|
||||
})
|
||||
)
|
||||
}))
|
||||
return importers
|
||||
}
|
||||
|
||||
const updateToLatest = opts.update && opts.latest
|
||||
const include = opts.include
|
||||
if (updateToLatest) {
|
||||
delete opts.include
|
||||
}
|
||||
if (opts.workspace && (cmdFullName === 'install' || cmdFullName === 'add')) {
|
||||
if (opts.latest) {
|
||||
throw new PnpmError('BAD_OPTIONS', 'Cannot use --latest with --workspace simultaneously')
|
||||
}
|
||||
if (!opts.workspaceDir) {
|
||||
throw new PnpmError('WORKSPACE_OPTION_OUTSIDE_WORKSPACE', '--workspace can only be used inside a workspace')
|
||||
}
|
||||
if (!opts.linkWorkspacePackages && !opts.saveWorkspaceProtocol) {
|
||||
if (opts.rawLocalConfig['save-workspace-protocol'] === false) {
|
||||
throw new PnpmError('BAD_OPTIONS', oneLine`This workspace has link-workspace-packages turned off,
|
||||
so dependencies are linked from the workspace only when the workspace protocol is used.
|
||||
Either set link-workspace-packages to true or don't use the --no-save-workspace-protocol option
|
||||
when running add/update with the --workspace option`)
|
||||
} else {
|
||||
opts.saveWorkspaceProtocol = true
|
||||
}
|
||||
}
|
||||
opts['preserveWorkspaceProtocol'] = !opts.linkWorkspacePackages
|
||||
}
|
||||
|
||||
if (cmdFullName !== 'rebuild') {
|
||||
// For a workspace with shared lockfile
|
||||
if (opts.lockfileDir && ['add', 'install', 'remove', 'update'].includes(cmdFullName)) {
|
||||
if (opts.hoistPattern) {
|
||||
logger.info({ message: 'Only the root workspace package is going to have hoisted dependencies in node_modules', prefix: opts.lockfileDir })
|
||||
}
|
||||
let importers = await getImporters()
|
||||
const isFromWorkspace = isSubdir.bind(null, opts.lockfileDir)
|
||||
importers = await pFilter(importers, async ({ rootDir }: { rootDir: string }) => isFromWorkspace(await fs.realpath(rootDir)))
|
||||
if (importers.length === 0) return true
|
||||
const hooks = opts.ignorePnpmfile ? {} : requireHooks(opts.lockfileDir, opts)
|
||||
const mutation = cmdFullName === 'remove' ? 'uninstallSome' : (input.length === 0 && !updateToLatest ? 'install' : 'installSome')
|
||||
const writeImporterManifests = [] as Array<(manifest: ImporterManifest) => Promise<void>>
|
||||
const mutatedImporters = [] as MutatedImporter[]
|
||||
await Promise.all(importers.map(async ({ buildIndex, rootDir }) => {
|
||||
const localConfig = await memReadLocalConfig(rootDir)
|
||||
const { manifest, writeImporterManifest } = manifestsByPath[rootDir]
|
||||
let currentInput = [...input]
|
||||
if (updateToLatest) {
|
||||
if (!currentInput || !currentInput.length) {
|
||||
currentInput = updateToLatestSpecsFromManifest(manifest, include)
|
||||
} else {
|
||||
currentInput = createLatestSpecs(currentInput, manifest)
|
||||
if (!currentInput.length) {
|
||||
installOpts.pruneLockfileImporters = false
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opts.workspace) {
|
||||
if (!currentInput || !currentInput.length) {
|
||||
currentInput = updateToWorkspacePackagesFromManifest(manifest, opts.include, workspacePackages!)
|
||||
} else {
|
||||
currentInput = createWorkspaceSpecs(currentInput, workspacePackages!)
|
||||
}
|
||||
}
|
||||
writeImporterManifests.push(writeImporterManifest)
|
||||
switch (mutation) {
|
||||
case 'uninstallSome':
|
||||
mutatedImporters.push({
|
||||
dependencyNames: currentInput,
|
||||
manifest,
|
||||
mutation,
|
||||
rootDir,
|
||||
targetDependenciesField: getSaveType(opts),
|
||||
} as MutatedImporter)
|
||||
return
|
||||
case 'installSome':
|
||||
mutatedImporters.push({
|
||||
allowNew: cmdFullName === 'install' || cmdFullName === 'add',
|
||||
dependencySelectors: currentInput,
|
||||
manifest,
|
||||
mutation,
|
||||
peer: opts.savePeer,
|
||||
pinnedVersion: getPinnedVersion({
|
||||
saveExact: typeof localConfig.saveExact === 'boolean' ? localConfig.saveExact : opts.saveExact,
|
||||
savePrefix: typeof localConfig.savePrefix === 'string' ? localConfig.savePrefix : opts.savePrefix,
|
||||
}),
|
||||
rootDir,
|
||||
targetDependenciesField: getSaveType(opts),
|
||||
} as MutatedImporter)
|
||||
return
|
||||
case 'install':
|
||||
mutatedImporters.push({
|
||||
buildIndex,
|
||||
manifest,
|
||||
mutation,
|
||||
rootDir,
|
||||
} as MutatedImporter)
|
||||
return
|
||||
}
|
||||
}))
|
||||
const mutatedPkgs = await mutateModules(mutatedImporters, {
|
||||
...installOpts,
|
||||
hooks,
|
||||
storeController: store.ctrl,
|
||||
})
|
||||
if (opts.save !== false) {
|
||||
await Promise.all(
|
||||
mutatedPkgs
|
||||
.map(({ manifest }, index) => writeImporterManifests[index](manifest))
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
let pkgPaths = chunks.length === 0
|
||||
? chunks[0]
|
||||
: Object.keys(opts.selectedWsPkgsGraph).sort()
|
||||
|
||||
const limitInstallation = pLimit(opts.workspaceConcurrency)
|
||||
await Promise.all(pkgPaths.map((rootDir: string) =>
|
||||
limitInstallation(async () => {
|
||||
const hooks = opts.ignorePnpmfile ? {} : requireHooks(rootDir, opts)
|
||||
try {
|
||||
if (opts.ignoredPackages && opts.ignoredPackages.has(rootDir)) {
|
||||
return
|
||||
}
|
||||
|
||||
const { manifest, writeImporterManifest } = manifestsByPath[rootDir]
|
||||
let currentInput = [...input]
|
||||
if (updateToLatest) {
|
||||
if (!currentInput || !currentInput.length) {
|
||||
currentInput = updateToLatestSpecsFromManifest(manifest, include)
|
||||
} else {
|
||||
currentInput = createLatestSpecs(currentInput, manifest)
|
||||
if (!currentInput.length) return
|
||||
}
|
||||
}
|
||||
|
||||
let action!: any // tslint:disable-line:no-any
|
||||
switch (cmdFullName) {
|
||||
case 'unlink':
|
||||
action = (currentInput.length === 0 ? unlink : unlinkPkgs.bind(null, currentInput))
|
||||
break
|
||||
case 'remove':
|
||||
action = (manifest: PackageManifest, opts: any) => mutateModules([ // tslint:disable-line:no-any
|
||||
{
|
||||
dependencyNames: currentInput,
|
||||
manifest,
|
||||
mutation: 'uninstallSome',
|
||||
rootDir,
|
||||
},
|
||||
], opts)
|
||||
break
|
||||
default:
|
||||
action = currentInput.length === 0
|
||||
? install
|
||||
: (manifest: PackageManifest, opts: any) => addDependenciesToPackage(manifest, currentInput, opts) // tslint:disable-line:no-any
|
||||
break
|
||||
}
|
||||
|
||||
const localConfig = await memReadLocalConfig(rootDir)
|
||||
const newManifest = await action(
|
||||
manifest,
|
||||
{
|
||||
...installOpts,
|
||||
...localConfig,
|
||||
bin: path.join(rootDir, 'node_modules', '.bin'),
|
||||
dir: rootDir,
|
||||
hooks,
|
||||
ignoreScripts: true,
|
||||
pinnedVersion: getPinnedVersion({
|
||||
saveExact: typeof localConfig.saveExact === 'boolean' ? localConfig.saveExact : opts.saveExact,
|
||||
savePrefix: typeof localConfig.savePrefix === 'string' ? localConfig.savePrefix : opts.savePrefix,
|
||||
}),
|
||||
rawConfig: {
|
||||
...installOpts.rawConfig,
|
||||
...localConfig,
|
||||
},
|
||||
storeController,
|
||||
},
|
||||
)
|
||||
if (opts.save !== false) {
|
||||
await writeImporterManifest(newManifest)
|
||||
}
|
||||
result.passes++
|
||||
} catch (err) {
|
||||
logger.info(err)
|
||||
|
||||
if (!opts.bail) {
|
||||
result.fails.push({
|
||||
error: err,
|
||||
message: err.message,
|
||||
prefix: rootDir,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err['prefix'] = rootDir // tslint:disable-line:no-string-literal
|
||||
throw err
|
||||
}
|
||||
}),
|
||||
))
|
||||
|
||||
await saveState()
|
||||
}
|
||||
|
||||
if (
|
||||
cmdFullName === 'rebuild' ||
|
||||
!opts.lockfileOnly && !opts.ignoreScripts && (
|
||||
cmdFullName === 'add' ||
|
||||
cmdFullName === 'install' ||
|
||||
cmdFullName === 'update' ||
|
||||
cmdFullName === 'unlink'
|
||||
)
|
||||
) {
|
||||
const action = (
|
||||
cmdFullName !== 'rebuild' || input.length === 0
|
||||
? rebuild
|
||||
: (importers: any, opts: any) => rebuildPkgs(importers, input, opts) // tslint:disable-line
|
||||
)
|
||||
if (opts.lockfileDir) {
|
||||
const importers = await getImporters()
|
||||
await action(
|
||||
importers,
|
||||
{
|
||||
...installOpts,
|
||||
pending: cmdFullName !== 'rebuild' || opts.pending === true,
|
||||
},
|
||||
)
|
||||
return true
|
||||
}
|
||||
const limitRebuild = pLimit(opts.workspaceConcurrency)
|
||||
for (const chunk of chunks) {
|
||||
await Promise.all(chunk.map((rootDir: string) =>
|
||||
limitRebuild(async () => {
|
||||
try {
|
||||
if (opts.ignoredPackages && opts.ignoredPackages.has(rootDir)) {
|
||||
return
|
||||
}
|
||||
const localConfig = await memReadLocalConfig(rootDir)
|
||||
await action(
|
||||
[
|
||||
{
|
||||
buildIndex: 0,
|
||||
manifest: manifestsByPath[rootDir].manifest,
|
||||
rootDir,
|
||||
},
|
||||
],
|
||||
{
|
||||
...installOpts,
|
||||
...localConfig,
|
||||
dir: rootDir,
|
||||
pending: cmdFullName !== 'rebuild' || opts.pending === true,
|
||||
rawConfig: {
|
||||
...installOpts.rawConfig,
|
||||
...localConfig,
|
||||
},
|
||||
},
|
||||
)
|
||||
result.passes++
|
||||
} catch (err) {
|
||||
logger.info(err)
|
||||
|
||||
if (!opts.bail) {
|
||||
result.fails.push({
|
||||
error: err,
|
||||
message: err.message,
|
||||
prefix: rootDir,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err['prefix'] = rootDir // tslint:disable-line:no-string-literal
|
||||
throw err
|
||||
}
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
throwOnFail(result)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
async function unlink (manifest: ImporterManifest, opts: any) { // tslint:disable-line:no-any
|
||||
return mutateModules(
|
||||
[
|
||||
{
|
||||
manifest,
|
||||
mutation: 'unlink',
|
||||
rootDir: opts.dir,
|
||||
},
|
||||
],
|
||||
opts,
|
||||
)
|
||||
}
|
||||
|
||||
async function unlinkPkgs (dependencyNames: string[], manifest: ImporterManifest, opts: any) { // tslint:disable-line:no-any
|
||||
return mutateModules(
|
||||
[
|
||||
{
|
||||
dependencyNames,
|
||||
manifest,
|
||||
mutation: 'unlinkSome',
|
||||
rootDir: opts.dir,
|
||||
},
|
||||
],
|
||||
opts,
|
||||
)
|
||||
}
|
||||
|
||||
function sortPackages (pkgGraph: WsPkgsGraph): string[][] {
|
||||
const keys = Object.keys(pkgGraph)
|
||||
const setOfKeys = new Set(keys)
|
||||
const graph = new Map(
|
||||
keys.map((pkgPath) => [
|
||||
pkgPath,
|
||||
pkgGraph[pkgPath].dependencies.filter(
|
||||
/* remove cycles of length 1 (ie., package 'a' depends on 'a'). They
|
||||
confuse the graph-sequencer, but can be ignored when ordering packages
|
||||
topologically.
|
||||
|
||||
See the following example where 'b' and 'c' depend on themselves:
|
||||
|
||||
graphSequencer({graph: new Map([
|
||||
['a', ['b', 'c']],
|
||||
['b', ['b']],
|
||||
['c', ['b', 'c']]]
|
||||
),
|
||||
groups: [['a', 'b', 'c']]})
|
||||
|
||||
returns chunks:
|
||||
|
||||
[['b'],['a'],['c']]
|
||||
|
||||
But both 'b' and 'c' should be executed _before_ 'a', because 'a' depends on
|
||||
them. It works (and is considered 'safe' if we run:)
|
||||
|
||||
graphSequencer({graph: new Map([
|
||||
['a', ['b', 'c']],
|
||||
['b', []],
|
||||
['c', ['b']]]
|
||||
), groups: [['a', 'b', 'c']]})
|
||||
|
||||
returning:
|
||||
|
||||
[['b'], ['c'], ['a']]
|
||||
|
||||
*/
|
||||
d => d !== pkgPath &&
|
||||
/* remove unused dependencies that we can ignore due to a filter expression.
|
||||
|
||||
Again, the graph sequencer used to behave weirdly in the following edge case:
|
||||
|
||||
graphSequencer({graph: new Map([
|
||||
['a', ['b', 'c']],
|
||||
['d', ['a']],
|
||||
['e', ['a', 'b', 'c']]]
|
||||
),
|
||||
groups: [['a', 'e', 'e']]})
|
||||
|
||||
returns chunks:
|
||||
|
||||
[['d'],['a'],['e']]
|
||||
|
||||
But we really want 'a' to be executed first.
|
||||
*/
|
||||
setOfKeys.has(d))]
|
||||
) as Array<[string, string[]]>,
|
||||
)
|
||||
const graphSequencerResult = graphSequencer({
|
||||
graph,
|
||||
groups: [keys],
|
||||
})
|
||||
return graphSequencerResult.chunks
|
||||
}
|
||||
|
||||
async function readLocalConfig (prefix: string) {
|
||||
try {
|
||||
const ini = await readIniFile(path.join(prefix, '.npmrc')) as { [key: string]: string }
|
||||
const config = camelcaseKeys(ini) as ({ [key: string]: string } & { hoist?: boolean })
|
||||
if (config.shamefullyFlatten) {
|
||||
config.hoistPattern = '*'
|
||||
// TODO: print a warning
|
||||
}
|
||||
if (config.hoist === false) {
|
||||
config.hoistPattern = ''
|
||||
}
|
||||
return config
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') throw err
|
||||
return {}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
///<reference path="../../../typings/index.d.ts" />
|
||||
import './updateWorkspaceDependencies.spec'
|
||||
|
||||
import './exec'
|
||||
import './link'
|
||||
import './list'
|
||||
import './misc'
|
||||
import './outdated'
|
||||
import './publish'
|
||||
import './rebuild'
|
||||
import './run'
|
||||
import './test'
|
||||
import './update'
|
||||
@@ -32,14 +32,18 @@
|
||||
},
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/filter-workspace-packages": "workspace:1.0.1",
|
||||
"@pnpm/logger": "^3.1.0",
|
||||
"@pnpm/plugin-commands-script-runners": "link:",
|
||||
"@pnpm/prepare": "workspace:0.0.0",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/mz": "^2.7.0",
|
||||
"@types/ramda": "^0.26.38",
|
||||
"@zkochan/rimraf": "1.0.0",
|
||||
"execa": "4.0.0",
|
||||
"mz": "2.7.0",
|
||||
"rimraf": "3.0.0"
|
||||
"rimraf": "3.0.0",
|
||||
"write-yaml-file": "3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/cli-utils": "workspace:0.2.5",
|
||||
@@ -47,10 +51,15 @@
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/error": "workspace:1.0.0",
|
||||
"@pnpm/lifecycle": "workspace:8.0.1",
|
||||
"@pnpm/sort-packages": "workspace:0.0.0",
|
||||
"@pnpm/types": "workspace:4.0.0",
|
||||
"@pnpm/utils": "workspace:0.12.2",
|
||||
"common-tags": "1.8.0",
|
||||
"p-limit": "2.2.1",
|
||||
"ramda": "0.26.1",
|
||||
"render-help": "0.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@pnpm/logger": "^3.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
86
packages/plugin-commands-script-runners/src/exec.ts
Normal file
86
packages/plugin-commands-script-runners/src/exec.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { RecursiveSummary, throwOnCommandFail } from '@pnpm/cli-utils'
|
||||
import { Config, types, WsPkgsGraph } from '@pnpm/config'
|
||||
import logger from '@pnpm/logger'
|
||||
import sortPackages from '@pnpm/sort-packages'
|
||||
import execa = require('execa')
|
||||
import pLimit from 'p-limit'
|
||||
import R = require('ramda')
|
||||
import renderHelp = require('render-help')
|
||||
|
||||
export const commandNames = ['exec']
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
export function cliOptionsTypes () {
|
||||
return R.pick([
|
||||
'bail',
|
||||
'unsafe-perm',
|
||||
'workspace-concurrency',
|
||||
], types)
|
||||
}
|
||||
|
||||
export function help () {
|
||||
return renderHelp({
|
||||
description: 'Run a command in each package.',
|
||||
usages: ['-r exec -- <command> [args...]'],
|
||||
})
|
||||
}
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: Required<Pick<Config, 'selectedWsPkgsGraph'>> & {
|
||||
bail?: boolean,
|
||||
unsafePerm?: boolean,
|
||||
rawConfig: object,
|
||||
sort?: boolean,
|
||||
workspaceConcurrency?: number,
|
||||
},
|
||||
) {
|
||||
const limitRun = pLimit(opts.workspaceConcurrency ?? 4)
|
||||
|
||||
const result = {
|
||||
fails: [],
|
||||
passes: 0,
|
||||
} as RecursiveSummary
|
||||
|
||||
const chunks = opts.sort
|
||||
? sortPackages(opts.selectedWsPkgsGraph)
|
||||
: [Object.keys(opts.selectedWsPkgsGraph).sort()]
|
||||
|
||||
for (const chunk of chunks) {
|
||||
await Promise.all(chunk.map((prefix: string) =>
|
||||
limitRun(async () => {
|
||||
try {
|
||||
await execa(args[0], args.slice(1), {
|
||||
cwd: prefix,
|
||||
env: {
|
||||
...process.env,
|
||||
PNPM_PACKAGE_NAME: opts.selectedWsPkgsGraph[prefix].package.manifest.name,
|
||||
},
|
||||
stdio: 'inherit',
|
||||
})
|
||||
result.passes++
|
||||
} catch (err) {
|
||||
logger.info(err)
|
||||
|
||||
if (!opts.bail) {
|
||||
result.fails.push({
|
||||
error: err,
|
||||
message: err.message,
|
||||
prefix,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// tslint:disable:no-string-literal
|
||||
err['code'] = 'ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL'
|
||||
err['prefix'] = prefix
|
||||
// tslint:enable:no-string-literal
|
||||
throw err
|
||||
}
|
||||
},
|
||||
)))
|
||||
}
|
||||
|
||||
throwOnCommandFail('pnpm recursive exec', result)
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import * as exec from './exec'
|
||||
import * as restart from './restart'
|
||||
import * as run from './run'
|
||||
import * as start from './start'
|
||||
import * as stop from './stop'
|
||||
import * as test from './test'
|
||||
|
||||
export { restart, run, start, stop, test }
|
||||
export { exec, restart, run, start, stop, test }
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import renderHelp = require('render-help')
|
||||
import { handler as run, IF_PRESENT_OPTION, IF_PRESENT_OPTION_HELP } from './run'
|
||||
import {
|
||||
handler as run,
|
||||
IF_PRESENT_OPTION,
|
||||
IF_PRESENT_OPTION_HELP,
|
||||
RunOpts,
|
||||
} from './run'
|
||||
import { handler as start } from './start'
|
||||
import { handler as stop } from './stop'
|
||||
|
||||
@@ -31,11 +36,7 @@ export function help () {
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: {
|
||||
extraBinPaths: string[],
|
||||
dir: string,
|
||||
rawConfig: object,
|
||||
},
|
||||
opts: RunOpts,
|
||||
) {
|
||||
await stop(args, opts)
|
||||
await run(['restart', ...args], opts)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { docsUrl, readImporterManifestOnly } from '@pnpm/cli-utils'
|
||||
import { FILTERING } from '@pnpm/common-cli-options-help'
|
||||
import { types as allTypes } from '@pnpm/config'
|
||||
import { Config, types as allTypes } from '@pnpm/config'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import runLifecycleHooks from '@pnpm/lifecycle'
|
||||
import { ImporterManifest } from '@pnpm/types'
|
||||
@@ -8,6 +8,7 @@ import { realNodeModulesDir } from '@pnpm/utils'
|
||||
import { oneLine } from 'common-tags'
|
||||
import R = require('ramda')
|
||||
import renderHelp = require('render-help')
|
||||
import runRecursive, { RecursiveRunOpts } from './runRecursive'
|
||||
|
||||
export const IF_PRESENT_OPTION = {
|
||||
'if-present': Boolean,
|
||||
@@ -54,16 +55,25 @@ export function help () {
|
||||
})
|
||||
}
|
||||
|
||||
export type RunOpts = Omit<RecursiveRunOpts, 'allWsPkgs' | 'selectedWsPkgsGraph' | 'workspaceDir'> & {
|
||||
ifPresent?: boolean,
|
||||
recursive?: boolean,
|
||||
} & Pick<Config, 'dir' | 'engineStrict'> & (
|
||||
{ recursive?: false } &
|
||||
Partial<Pick<Config, 'allWsPkgs' | 'selectedWsPkgsGraph' | 'workspaceDir'>>
|
||||
|
|
||||
{ recursive: true } &
|
||||
Required<Pick<Config, 'allWsPkgs' | 'selectedWsPkgsGraph' | 'workspaceDir'>>
|
||||
)
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: {
|
||||
engineStrict?: boolean,
|
||||
extraBinPaths: string[],
|
||||
dir: string,
|
||||
ifPresent?: boolean,
|
||||
rawConfig: object,
|
||||
},
|
||||
opts: RunOpts,
|
||||
) {
|
||||
if (opts.recursive) {
|
||||
await runRecursive(args, opts)
|
||||
return
|
||||
}
|
||||
const dir = opts.dir
|
||||
const manifest = await readImporterManifestOnly(dir, opts)
|
||||
const scriptName = args[0]
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
import { WsPkgsGraph } from '@pnpm/config'
|
||||
import { RecursiveSummary, throwOnCommandFail } from '@pnpm/cli-utils'
|
||||
import { Config, WsPkgsGraph } from '@pnpm/config'
|
||||
import PnpmError from '@pnpm/error'
|
||||
import runLifecycleHooks from '@pnpm/lifecycle'
|
||||
import logger from '@pnpm/logger'
|
||||
import sortPackages from '@pnpm/sort-packages'
|
||||
import { PackageManifest } from '@pnpm/types'
|
||||
import { realNodeModulesDir } from '@pnpm/utils'
|
||||
import pLimit from 'p-limit'
|
||||
import RecursiveSummary from './recursiveSummary'
|
||||
|
||||
export default async <T> (
|
||||
packageChunks: string[][],
|
||||
graph: WsPkgsGraph,
|
||||
export type RecursiveRunOpts = Pick<Config,
|
||||
'unsafePerm' |
|
||||
'rawConfig'
|
||||
> & Required<Pick<Config, 'allWsPkgs' | 'selectedWsPkgsGraph' | 'workspaceDir'>> &
|
||||
Partial<Pick<Config, 'extraBinPaths' | 'bail' | 'sort' | 'workspaceConcurrency'>>
|
||||
|
||||
export default async (
|
||||
args: string[],
|
||||
cmd: string,
|
||||
opts: {
|
||||
bail: boolean,
|
||||
extraBinPaths: string[],
|
||||
workspaceConcurrency: number,
|
||||
unsafePerm: boolean,
|
||||
rawConfig: object,
|
||||
workspaceDir: string,
|
||||
allPackagesAreSelected: boolean,
|
||||
},
|
||||
opts: RecursiveRunOpts,
|
||||
) => {
|
||||
const scriptName = args[0]
|
||||
let hasCommand = 0
|
||||
const packageChunks = opts.sort
|
||||
? sortPackages(opts.selectedWsPkgsGraph)
|
||||
: [Object.keys(opts.selectedWsPkgsGraph).sort()]
|
||||
|
||||
const result = {
|
||||
fails: [],
|
||||
passes: 0,
|
||||
} as RecursiveSummary
|
||||
|
||||
const limitRun = pLimit(opts.workspaceConcurrency)
|
||||
const limitRun = pLimit(opts.workspaceConcurrency ?? 4)
|
||||
const stdio = (
|
||||
opts.workspaceConcurrency === 1 ||
|
||||
packageChunks.length === 1 && packageChunks[0].length === 1
|
||||
@@ -40,7 +39,7 @@ export default async <T> (
|
||||
for (const chunk of packageChunks) {
|
||||
await Promise.all(chunk.map((prefix: string) =>
|
||||
limitRun(async () => {
|
||||
const pkg = graph[prefix] as {package: {dir: string, manifest: PackageManifest}}
|
||||
const pkg = opts.selectedWsPkgsGraph[prefix] as {package: {dir: string, manifest: PackageManifest}}
|
||||
if (!pkg.package.manifest.scripts || !pkg.package.manifest.scripts[scriptName]) {
|
||||
return
|
||||
}
|
||||
@@ -86,7 +85,8 @@ export default async <T> (
|
||||
}
|
||||
|
||||
if (scriptName !== 'test' && !hasCommand) {
|
||||
if (opts.allPackagesAreSelected) {
|
||||
const allPackagesAreSelected = Object.keys(opts.selectedWsPkgsGraph).length === opts.allWsPkgs.length
|
||||
if (allPackagesAreSelected) {
|
||||
throw new PnpmError('RECURSIVE_RUN_NO_SCRIPT', `None of the packages has a "${scriptName}" script`)
|
||||
} else {
|
||||
logger.info({
|
||||
@@ -96,5 +96,5 @@ export default async <T> (
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
throwOnCommandFail('pnpm recursive run', result)
|
||||
}
|
||||
@@ -1,7 +1,12 @@
|
||||
import { docsUrl } from '@pnpm/cli-utils'
|
||||
import { oneLine } from 'common-tags'
|
||||
import renderHelp = require('render-help')
|
||||
import { handler as run, IF_PRESENT_OPTION, IF_PRESENT_OPTION_HELP } from './run'
|
||||
import {
|
||||
handler as run,
|
||||
IF_PRESENT_OPTION,
|
||||
IF_PRESENT_OPTION_HELP,
|
||||
RunOpts,
|
||||
} from './run'
|
||||
|
||||
export function rcOptionsTypes () {
|
||||
return {}
|
||||
@@ -34,11 +39,7 @@ export function help () {
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: {
|
||||
extraBinPaths: string[],
|
||||
dir: string,
|
||||
rawConfig: object,
|
||||
},
|
||||
opts: RunOpts,
|
||||
) {
|
||||
return run(['start', ...args], opts)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import { docsUrl } from '@pnpm/cli-utils'
|
||||
import renderHelp = require('render-help')
|
||||
import { handler as run, IF_PRESENT_OPTION, IF_PRESENT_OPTION_HELP } from './run'
|
||||
import {
|
||||
handler as run,
|
||||
IF_PRESENT_OPTION,
|
||||
IF_PRESENT_OPTION_HELP,
|
||||
RunOpts,
|
||||
} from './run'
|
||||
|
||||
export function rcOptionsTypes () {
|
||||
return {}
|
||||
@@ -31,11 +36,7 @@ export function help () {
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: {
|
||||
extraBinPaths: string[],
|
||||
dir: string,
|
||||
rawConfig: object,
|
||||
},
|
||||
opts: RunOpts,
|
||||
) {
|
||||
return run(['stop', ...args], opts)
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import { types as allTypes } from '@pnpm/config'
|
||||
import { oneLine } from 'common-tags'
|
||||
import R = require('ramda')
|
||||
import renderHelp = require('render-help')
|
||||
import { handler as run } from './run'
|
||||
import { handler as run, RunOpts } from './run'
|
||||
|
||||
export function rcOptionsTypes () {
|
||||
return {}
|
||||
@@ -46,11 +46,7 @@ export function help () {
|
||||
|
||||
export async function handler (
|
||||
args: string[],
|
||||
opts: {
|
||||
extraBinPaths: string[],
|
||||
dir: string,
|
||||
rawConfig: object,
|
||||
},
|
||||
opts: RunOpts,
|
||||
) {
|
||||
return run(['test', ...args], opts)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { exec } from '@pnpm/plugin-commands-script-runners'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import rimraf = require('@zkochan/rimraf')
|
||||
import execa = require('execa')
|
||||
import fs = require('mz/fs')
|
||||
import path = require('path')
|
||||
import test = require('tape')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS, REGISTRY } from './utils'
|
||||
|
||||
test('pnpm recursive exec', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -48,17 +50,17 @@ test('pnpm recursive exec', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
const { selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler(['npm', 'run', 'build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['exec', 'npm', 'run', 'build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
|
||||
@@ -80,10 +82,8 @@ test('pnpm recursive exec sets PNPM_PACKAGE_NAME env var', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['exec', 'node', '-e', `require('fs').writeFileSync('pkgname', process.env.PNPM_PACKAGE_NAME, 'utf8')`], {
|
||||
await exec.handler(['node', '-e', `require('fs').writeFileSync('pkgname', process.env.PNPM_PACKAGE_NAME, 'utf8')`], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
|
||||
@@ -130,21 +130,21 @@ test('testing the bail config with "pnpm recursive exec"', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
const { selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
|
||||
let failed = false
|
||||
let err1!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['exec', 'npm', 'run', 'build', '--no-bail'], {
|
||||
await exec.handler(['npm', 'run', 'build', '--no-bail'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
} catch (_err) {
|
||||
@@ -162,10 +162,8 @@ test('testing the bail config with "pnpm recursive exec"', async (t) => {
|
||||
failed = false
|
||||
let err2!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['exec', 'npm', 'run', 'build'], {
|
||||
await exec.handler(['npm', 'run', 'build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
} catch (_err) {
|
||||
@@ -205,18 +203,17 @@ test('pnpm recursive exec --no-sort', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
const { selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await exec.handler(['npm', 'run', 'build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
linkWorkspacePackages: true,
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['exec', 'npm', 'run', 'build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
sort: false,
|
||||
workspaceConcurrency: 1,
|
||||
@@ -11,6 +11,9 @@ import execa = require('execa')
|
||||
import fs = require('mz/fs')
|
||||
import path = require('path')
|
||||
import test = require('tape')
|
||||
import './exec'
|
||||
import './runRecursive'
|
||||
import './testRecursive'
|
||||
|
||||
test('pnpm run: returns correct exit code', async (t) => {
|
||||
prepare(t, {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import PnpmError from '@pnpm/error'
|
||||
import { filterPkgsBySelectorObjects } from '@pnpm/filter-workspace-packages'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { filterPkgsBySelectorObjects, readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { run } from '@pnpm/plugin-commands-script-runners'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import rimraf = require('@zkochan/rimraf')
|
||||
import execa = require('execa')
|
||||
import fs = require('mz/fs')
|
||||
import path = require('path')
|
||||
import test = require('tape')
|
||||
import writeYamlFile = require('write-yaml-file')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS, REGISTRY } from './utils'
|
||||
|
||||
test('pnpm recursive run', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -57,17 +58,21 @@ test('pnpm recursive run', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['run', 'build'], {
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await run.handler(['build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const outputs1 = await import(path.resolve('output1.json')) as string[]
|
||||
@@ -105,17 +110,21 @@ test('pnpm recursive run concurrently', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['run', 'build'], {
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await run.handler(['build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const outputs1 = await import(path.resolve('output1.json')) as number[]
|
||||
@@ -154,20 +163,24 @@ test('`pnpm recursive run` fails when run without filters and no package has the
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['run', 'this-command-does-not-exist'], {
|
||||
await run.handler(['this-command-does-not-exist'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
@@ -206,10 +219,12 @@ test('`pnpm recursive run` fails when run with a filter that includes all packag
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['run', 'this-command-does-not-exist'], {
|
||||
await run.handler(['this-command-does-not-exist'], {
|
||||
...DEFAULT_OPTS,
|
||||
...await readWsPkgs(process.cwd(), [{ namePattern: '*' }]),
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
@@ -246,22 +261,26 @@ test('`pnpm recursive run` succeeds when run against a subset of packages and no
|
||||
},
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['run', 'this-command-does-not-exist'], {
|
||||
const { allWsPkgs } = await readWsPkgs(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await run.handler(['this-command-does-not-exist'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph: await filterPkgsBySelectorObjects(
|
||||
allWsPkgs,
|
||||
[{ namePattern: 'project-1' }],
|
||||
{ workspaceDir: process.cwd() },
|
||||
),
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
t.end()
|
||||
})
|
||||
@@ -306,20 +325,24 @@ test('testing the bail config with "pnpm recursive run"', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
|
||||
let err1!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['run', 'build', '--no-bail'], {
|
||||
await run.handler(['build', '--no-bail'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
} catch (_err) {
|
||||
err1 = _err
|
||||
@@ -333,11 +356,13 @@ test('testing the bail config with "pnpm recursive run"', async (t) => {
|
||||
|
||||
let err2!: PnpmError
|
||||
try {
|
||||
await recursive.handler(['run', 'build'], {
|
||||
await run.handler(['build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
} catch (_err) {
|
||||
err2 = _err
|
||||
@@ -376,22 +401,26 @@ test('pnpm recursive run with filtering', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['run', 'build'], {
|
||||
const { allWsPkgs } = await readWsPkgs(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await run.handler(['build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph: await filterPkgsBySelectorObjects(
|
||||
allWsPkgs,
|
||||
[{ namePattern: 'project-1' }],
|
||||
{ workspaceDir: process.cwd() },
|
||||
),
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const outputs = await import(path.resolve('output.json')) as string[]
|
||||
@@ -415,19 +444,23 @@ test('`pnpm recursive run` should always trust the scripts', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
const { allWsPkgs } = await readWsPkgs(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
|
||||
process.env['npm_config_unsafe_perm'] = 'false'
|
||||
await recursive.handler(['run', 'build'], {
|
||||
await run.handler(['build'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
workspaceDir: process.cwd(),
|
||||
...await readWsPkgs(process.cwd(), []),
|
||||
})
|
||||
delete process.env['npm_config_unsafe_perm']
|
||||
@@ -1,9 +1,10 @@
|
||||
import { filterPkgsBySelectorObjects } from '@pnpm/filter-workspace-packages'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import { filterPkgsBySelectorObjects, readWsPkgs } from '@pnpm/filter-workspace-packages'
|
||||
import { test as testCommand } from '@pnpm/plugin-commands-script-runners'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import execa = require('execa')
|
||||
import path = require('path')
|
||||
import test = require('tape')
|
||||
import { DEFAULT_OPTS, readWsPkgs } from './utils'
|
||||
import { DEFAULT_OPTS, REGISTRY } from './utils'
|
||||
|
||||
test('pnpm recursive test', async (t) => {
|
||||
const projects = preparePackages(t, [
|
||||
@@ -51,17 +52,21 @@ test('pnpm recursive test', async (t) => {
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['test'], {
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await testCommand.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const outputs1 = await import(path.resolve('output1.json')) as string[]
|
||||
@@ -103,18 +108,22 @@ test('`pnpm recursive test` does not fail if none of the packaegs has a test com
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
|
||||
await recursive.handler(['test'], {
|
||||
await testCommand.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph,
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
t.pass('command did not fail')
|
||||
@@ -148,22 +157,26 @@ test('pnpm recursive test with filtering', async (t) => {
|
||||
},
|
||||
])
|
||||
|
||||
const { allWsPkgs, selectedWsPkgsGraph } = await readWsPkgs(process.cwd(), [])
|
||||
await recursive.handler(['install'], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
selectedWsPkgsGraph,
|
||||
})
|
||||
await recursive.handler(['test'], {
|
||||
const { allWsPkgs } = await readWsPkgs(process.cwd(), [])
|
||||
await execa('pnpm', [
|
||||
'install',
|
||||
'-r',
|
||||
'--registry',
|
||||
REGISTRY,
|
||||
'--store-dir',
|
||||
path.resolve(DEFAULT_OPTS.storeDir),
|
||||
])
|
||||
await testCommand.handler([], {
|
||||
...DEFAULT_OPTS,
|
||||
allWsPkgs,
|
||||
dir: process.cwd(),
|
||||
recursive: true,
|
||||
selectedWsPkgsGraph: await filterPkgsBySelectorObjects(
|
||||
allWsPkgs,
|
||||
[{ namePattern: 'project-1' }],
|
||||
{ workspaceDir: process.cwd() },
|
||||
),
|
||||
workspaceDir: process.cwd(),
|
||||
})
|
||||
|
||||
const outputs = await import(path.resolve('output.json')) as string[]
|
||||
44
packages/plugin-commands-script-runners/test/utils.ts
Normal file
44
packages/plugin-commands-script-runners/test/utils.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export const REGISTRY = `https://registry.npmjs.org/`
|
||||
|
||||
export const DEFAULT_OPTS = {
|
||||
alwaysAuth: false,
|
||||
argv: {
|
||||
original: [],
|
||||
},
|
||||
bail: false,
|
||||
ca: undefined,
|
||||
cert: undefined,
|
||||
cliOptions: {},
|
||||
fetchRetries: 2,
|
||||
fetchRetryFactor: 90,
|
||||
fetchRetryMaxtimeout: 90,
|
||||
fetchRetryMintimeout: 10,
|
||||
filter: [] as string[],
|
||||
httpsProxy: undefined,
|
||||
include: {
|
||||
dependencies: true,
|
||||
devDependencies: true,
|
||||
optionalDependencies: true,
|
||||
},
|
||||
key: undefined,
|
||||
linkWorkspacePackages: true,
|
||||
localAddress: undefined,
|
||||
lock: false,
|
||||
lockStaleDuration: 90,
|
||||
networkConcurrency: 16,
|
||||
offline: false,
|
||||
pending: false,
|
||||
pnpmfile: './pnpmfile.js',
|
||||
proxy: undefined,
|
||||
rawConfig: { registry: REGISTRY },
|
||||
rawLocalConfig: {},
|
||||
registries: { default: REGISTRY },
|
||||
registry: REGISTRY,
|
||||
sort: true,
|
||||
storeDir: '../store',
|
||||
strictSsl: false,
|
||||
userAgent: 'pnpm',
|
||||
useRunningStoreServer: false,
|
||||
useStoreServer: false,
|
||||
workspaceConcurrency: 4,
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@pnpm/cli-utils": "workspace:0.2.5",
|
||||
"@pnpm/common-cli-options-help": "workspace:0.1.2",
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"@pnpm/core-loggers": "workspace:4.0.0",
|
||||
"@pnpm/default-reporter": "workspace:5.0.5",
|
||||
@@ -37,7 +38,6 @@
|
||||
"@pnpm/plugin-commands-outdated": "workspace:0.1.9",
|
||||
"@pnpm/plugin-commands-publishing": "workspace:0.1.9",
|
||||
"@pnpm/plugin-commands-rebuild": "workspace:0.0.0",
|
||||
"@pnpm/plugin-commands-recursive": "workspace:0.1.10",
|
||||
"@pnpm/plugin-commands-script-runners": "workspace:0.1.9",
|
||||
"@pnpm/plugin-commands-server": "workspace:0.0.0",
|
||||
"@pnpm/plugin-commands-store": "workspace:0.0.0",
|
||||
|
||||
@@ -5,8 +5,8 @@ import { list, why } from '@pnpm/plugin-commands-listing'
|
||||
import { outdated } from '@pnpm/plugin-commands-outdated'
|
||||
import { pack, publish } from '@pnpm/plugin-commands-publishing'
|
||||
import { rebuild } from '@pnpm/plugin-commands-rebuild'
|
||||
import { recursive } from '@pnpm/plugin-commands-recursive'
|
||||
import {
|
||||
exec,
|
||||
restart,
|
||||
run,
|
||||
start,
|
||||
@@ -18,6 +18,7 @@ import { store } from '@pnpm/plugin-commands-store'
|
||||
import { PnpmOptions } from '../types'
|
||||
import createHelp from './help'
|
||||
import * as installTest from './installTest'
|
||||
import * as recursive from './recursive'
|
||||
import * as root from './root'
|
||||
|
||||
export type Command = (
|
||||
@@ -35,6 +36,7 @@ const commands: Array<{
|
||||
}> = [
|
||||
add,
|
||||
audit,
|
||||
exec,
|
||||
importCommand,
|
||||
install,
|
||||
installTest,
|
||||
|
||||
@@ -20,6 +20,6 @@ export function help () {
|
||||
}
|
||||
|
||||
export async function handler (input: string[], opts: PnpmOptions) {
|
||||
await install.handler(input, opts)
|
||||
await install.handler(input, opts, 'install')
|
||||
await test.handler(input, opts)
|
||||
}
|
||||
|
||||
123
packages/pnpm/src/cmd/recursive.ts
Normal file
123
packages/pnpm/src/cmd/recursive.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { docsUrl } from '@pnpm/cli-utils'
|
||||
import { FILTERING } from '@pnpm/common-cli-options-help'
|
||||
import { WANTED_LOCKFILE } from '@pnpm/constants'
|
||||
import { oneLine } from 'common-tags'
|
||||
import renderHelp = require('render-help')
|
||||
|
||||
export const rcOptionsTypes = () => ({})
|
||||
export const cliOptionsTypes = () => ({})
|
||||
|
||||
export const commandNames = ['recursive', 'multi', 'm']
|
||||
|
||||
export function help () {
|
||||
return renderHelp({
|
||||
description: oneLine`
|
||||
Concurrently performs some actions in all subdirectories with a \`package.json\` (excluding node_modules).
|
||||
A \`pnpm-workspace.yaml\` file may be used to control what directories are searched for packages.`,
|
||||
descriptionLists: [
|
||||
{
|
||||
title: 'Commands',
|
||||
|
||||
list: [
|
||||
{
|
||||
name: 'install',
|
||||
},
|
||||
{
|
||||
name: 'add',
|
||||
},
|
||||
{
|
||||
name: 'update',
|
||||
},
|
||||
{
|
||||
description: 'Uninstall a dependency from each package',
|
||||
name: 'remove <pkg>...',
|
||||
},
|
||||
{
|
||||
description: 'Removes links to local packages and reinstalls them from the registry.',
|
||||
name: 'unlink',
|
||||
},
|
||||
{
|
||||
description: 'List dependencies in each package.',
|
||||
name: 'list [<pkg>...]',
|
||||
},
|
||||
{
|
||||
description: 'List packages that depend on <pkg>.',
|
||||
name: 'why <pkg>...',
|
||||
},
|
||||
{
|
||||
description: 'Check for outdated dependencies in every package.',
|
||||
name: 'outdated [<pkg>...]',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
This runs an arbitrary command from each package's "scripts" object.
|
||||
If a package doesn't have the command, it is skipped.
|
||||
If none of the packages have the command, the command fails.`,
|
||||
name: 'run <command> [-- <args>...]',
|
||||
},
|
||||
{
|
||||
description: `This runs each package's "test" script, if one was provided.`,
|
||||
name: 'test [-- <args>...]',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
This command runs the "npm build" command on each package.
|
||||
This is useful when you install a new version of node,
|
||||
and must recompile all your C++ addons with the new binary.`,
|
||||
name: 'rebuild [[<@scope>/<name>]...]',
|
||||
},
|
||||
{
|
||||
description: `Run a command in each package.`,
|
||||
name: 'exec -- <command> [args...]',
|
||||
},
|
||||
{
|
||||
description: 'Publishes packages to the npm registry. Only publishes a package if its version is not taken in the registry.',
|
||||
name: 'publish [--tag <tag>] [--access <public|restricted>]',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Options',
|
||||
|
||||
list: [
|
||||
{
|
||||
description: 'Continues executing other tasks even if a task threw an error.',
|
||||
name: '--no-bail',
|
||||
},
|
||||
{
|
||||
description: 'Set the maximum number of concurrency. Default is 4. For unlimited concurrency use Infinity.',
|
||||
name: '--workspace-concurrency <number>',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
Locally available packages are linked to node_modules instead of being downloaded from the registry.
|
||||
Convenient to use in a multi-package repository.`,
|
||||
name: '--link-workspace-packages',
|
||||
},
|
||||
{
|
||||
description: 'Sort packages topologically (dependencies before dependents). Pass --no-sort to disable.',
|
||||
name: '--sort',
|
||||
},
|
||||
{
|
||||
description: oneLine`
|
||||
Creates a single ${WANTED_LOCKFILE} file in the root of the workspace.
|
||||
A shared lockfile also means that all dependencies of all workspace packages will be in a single node_modules.`,
|
||||
name: '--shared-workspace-lockfile',
|
||||
},
|
||||
],
|
||||
},
|
||||
FILTERING,
|
||||
],
|
||||
url: docsUrl('recursive'),
|
||||
usages: [
|
||||
'pnpm recursive [command] [flags] [--filter <package selector>]',
|
||||
'pnpm multi [command] [flags] [--filter <package selector>]',
|
||||
'pnpm m [command] [flags] [--filter <package selector>]'
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
export function handler () {
|
||||
console.log(help())
|
||||
process.exit(1)
|
||||
}
|
||||
@@ -130,21 +130,6 @@ export default async function run (inputArgv: string[]) {
|
||||
process.env['FORCE_COLOR'] = '0'
|
||||
}
|
||||
|
||||
if (
|
||||
cmd === 'add' &&
|
||||
workspaceDir === dir &&
|
||||
!config.ignoreWorkspaceRootCheck
|
||||
) {
|
||||
// Reporting is not initialized at this point, so just printing the error
|
||||
console.error(`${chalk.bgRed.black('\u2009ERROR\u2009')} ${
|
||||
chalk.red('Running this command will add the dependency to the workspace root, ' +
|
||||
'which might not be what you want - if you really meant it, ' +
|
||||
'make it explicit by running this command again with the -W flag (or --ignore-workspace-root-check).')}`)
|
||||
console.log(`For help, run: pnpm help ${cmd}`)
|
||||
process.exit(1)
|
||||
return
|
||||
}
|
||||
|
||||
const selfUpdate = config.global && (cmd === 'add' || cmd === 'update') && argv.remain.includes(packageManager.name)
|
||||
|
||||
// Don't check for updates
|
||||
@@ -172,7 +157,7 @@ export default async function run (inputArgv: string[]) {
|
||||
await pnpmCmds.server(['stop'], config as any) // tslint:disable-line:no-any
|
||||
}
|
||||
|
||||
if (cmd === 'recursive') {
|
||||
if (cliConf['recursive']) {
|
||||
const wsDir = workspaceDir ?? process.cwd()
|
||||
const allWsPkgs = await findWorkspacePackages(wsDir, config)
|
||||
|
||||
@@ -186,6 +171,7 @@ export default async function run (inputArgv: string[]) {
|
||||
workspaceDir: wsDir,
|
||||
})
|
||||
config.allWsPkgs = allWsPkgs
|
||||
config.workspaceDir = wsDir
|
||||
}
|
||||
|
||||
// NOTE: we defer the next stage, otherwise reporter might not catch all the logs
|
||||
@@ -198,7 +184,7 @@ export default async function run (inputArgv: string[]) {
|
||||
})
|
||||
}
|
||||
|
||||
if (cmd !== 'recursive') {
|
||||
if (!cliConf['recursive']) {
|
||||
scopeLogger.debug(workspaceDir
|
||||
? { selected: 1, workspacePrefix: workspaceDir }
|
||||
: { selected: 1 })
|
||||
|
||||
@@ -323,7 +323,7 @@ test('`pnpm recursive add` should fail if no package name was provided', (t: tap
|
||||
const { status, stdout } = execPnpmSync('recursive', 'add')
|
||||
|
||||
t.equal(status, 1)
|
||||
t.ok(stdout.toString().includes('`pnpm recursive add` requires the package name'))
|
||||
t.ok(stdout.toString().includes('`pnpm add` requires the package name'))
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
@@ -371,12 +371,12 @@ test('adding new dependency in the root should fail if --ignore-workspace-root-c
|
||||
await fs.writeFile('pnpm-workspace.yaml', '', 'utf8')
|
||||
|
||||
{
|
||||
const { status, stderr } = execPnpmSync('add', 'is-positive')
|
||||
const { status, stdout } = execPnpmSync('add', 'is-positive')
|
||||
|
||||
t.equal(status, 1)
|
||||
|
||||
t.ok(
|
||||
stderr.toString().includes(
|
||||
stdout.toString().includes(
|
||||
'Running this command will add the dependency to the workspace root, ' +
|
||||
'which might not be what you want - if you really meant it, ' +
|
||||
'make it explicit by running this command again with the -W flag (or --ignore-workspace-root-check).'
|
||||
|
||||
15
packages/sort-packages/README.md
Normal file
15
packages/sort-packages/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# @pnpm/sort-packages
|
||||
|
||||
> Sort packages
|
||||
|
||||
[](https://www.npmjs.com/package/@pnpm/plugin-commands-recursive)
|
||||
|
||||
## Installation
|
||||
|
||||
```sh
|
||||
<pnpm|npm|yarn> add @pnpm/sort-packages
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT © [Zoltan Kochan](https://www.kochan.io/)
|
||||
37
packages/sort-packages/package.json
Normal file
37
packages/sort-packages/package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@pnpm/sort-packages",
|
||||
"version": "0.0.0",
|
||||
"description": "Sort packages",
|
||||
"main": "lib/index.js",
|
||||
"typings": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "tslint -c tslint.json src/**/*.ts test/**/*.ts",
|
||||
"tsc": "rimraf lib && tsc",
|
||||
"test": "pnpm run tsc && pnpm run lint",
|
||||
"prepublishOnly": "pnpm run tsc"
|
||||
},
|
||||
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/sort-packages",
|
||||
"keywords": [
|
||||
"pnpm"
|
||||
],
|
||||
"author": "Zoltan Kochan <z@kochan.io> (https://www.kochan.io/)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/pnpm/pnpm/issues"
|
||||
},
|
||||
"homepage": "https://pnpm.js.org",
|
||||
"devDependencies": {
|
||||
"@pnpm/sort-packages": "link:",
|
||||
"rimraf": "3.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@pnpm/config": "workspace:6.0.0",
|
||||
"graph-sequencer": "2.0.0"
|
||||
}
|
||||
}
|
||||
68
packages/sort-packages/src/index.ts
Normal file
68
packages/sort-packages/src/index.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { WsPkgsGraph } from '@pnpm/config'
|
||||
import graphSequencer = require('graph-sequencer')
|
||||
|
||||
export default function sortPackages (pkgGraph: WsPkgsGraph): string[][] {
|
||||
const keys = Object.keys(pkgGraph)
|
||||
const setOfKeys = new Set(keys)
|
||||
const graph = new Map(
|
||||
keys.map((pkgPath) => [
|
||||
pkgPath,
|
||||
pkgGraph[pkgPath].dependencies.filter(
|
||||
/* remove cycles of length 1 (ie., package 'a' depends on 'a'). They
|
||||
confuse the graph-sequencer, but can be ignored when ordering packages
|
||||
topologically.
|
||||
|
||||
See the following example where 'b' and 'c' depend on themselves:
|
||||
|
||||
graphSequencer({graph: new Map([
|
||||
['a', ['b', 'c']],
|
||||
['b', ['b']],
|
||||
['c', ['b', 'c']]]
|
||||
),
|
||||
groups: [['a', 'b', 'c']]})
|
||||
|
||||
returns chunks:
|
||||
|
||||
[['b'],['a'],['c']]
|
||||
|
||||
But both 'b' and 'c' should be executed _before_ 'a', because 'a' depends on
|
||||
them. It works (and is considered 'safe' if we run:)
|
||||
|
||||
graphSequencer({graph: new Map([
|
||||
['a', ['b', 'c']],
|
||||
['b', []],
|
||||
['c', ['b']]]
|
||||
), groups: [['a', 'b', 'c']]})
|
||||
|
||||
returning:
|
||||
|
||||
[['b'], ['c'], ['a']]
|
||||
|
||||
*/
|
||||
d => d !== pkgPath &&
|
||||
/* remove unused dependencies that we can ignore due to a filter expression.
|
||||
|
||||
Again, the graph sequencer used to behave weirdly in the following edge case:
|
||||
|
||||
graphSequencer({graph: new Map([
|
||||
['a', ['b', 'c']],
|
||||
['d', ['a']],
|
||||
['e', ['a', 'b', 'c']]]
|
||||
),
|
||||
groups: [['a', 'e', 'e']]})
|
||||
|
||||
returns chunks:
|
||||
|
||||
[['d'],['a'],['e']]
|
||||
|
||||
But we really want 'a' to be executed first.
|
||||
*/
|
||||
setOfKeys.has(d))]
|
||||
) as Array<[string, string[]]>,
|
||||
)
|
||||
const graphSequencerResult = graphSequencer({
|
||||
graph,
|
||||
groups: [keys],
|
||||
})
|
||||
return graphSequencerResult.chunks
|
||||
}
|
||||
252
pnpm-lock.yaml
generated
252
pnpm-lock.yaml
generated
@@ -86,6 +86,7 @@ importers:
|
||||
dependencies:
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/default-resolver': 'link:../default-resolver'
|
||||
'@pnpm/error': 'link:../error'
|
||||
'@pnpm/package-is-installable': 'link:../package-is-installable'
|
||||
'@pnpm/read-importer-manifest': 'link:../read-importer-manifest'
|
||||
'@pnpm/utils': 'link:../utils'
|
||||
@@ -107,6 +108,7 @@ importers:
|
||||
'@pnpm/cli-utils': 'link:'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/default-resolver': 'workspace:6.0.3'
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/logger': ^3.1.0
|
||||
'@pnpm/package-is-installable': 'workspace:4.0.1'
|
||||
'@pnpm/read-importer-manifest': 'workspace:2.0.1'
|
||||
@@ -404,6 +406,7 @@ importers:
|
||||
packages/filter-workspace-packages:
|
||||
dependencies:
|
||||
'@pnpm/error': 'link:../error'
|
||||
'@pnpm/find-workspace-packages': 'link:../find-workspace-packages'
|
||||
'@pnpm/matcher': 'link:../matcher'
|
||||
execa: 4.0.0
|
||||
find-up: 4.1.0
|
||||
@@ -424,6 +427,7 @@ importers:
|
||||
specifiers:
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'link:'
|
||||
'@pnpm/find-workspace-packages': 'workspace:2.0.8'
|
||||
'@pnpm/matcher': 'workspace:1.0.0'
|
||||
'@types/is-ci': ^2.0.0
|
||||
'@types/is-windows': ^1.0.0
|
||||
@@ -464,6 +468,7 @@ importers:
|
||||
packages/find-workspace-packages:
|
||||
dependencies:
|
||||
'@pnpm/cli-utils': 'link:../cli-utils'
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/constants': 'link:../constants'
|
||||
'@pnpm/types': 'link:../types'
|
||||
find-packages: 'link:../find-packages'
|
||||
@@ -473,6 +478,7 @@ importers:
|
||||
rimraf: 3.0.0
|
||||
specifiers:
|
||||
'@pnpm/cli-utils': 'workspace:0.2.5'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/constants': 'workspace:3.0.0'
|
||||
'@pnpm/find-workspace-packages': 'link:'
|
||||
'@pnpm/types': 'workspace:4.0.0'
|
||||
@@ -1337,56 +1343,92 @@ importers:
|
||||
'@pnpm/common-cli-options-help': 'link:../common-cli-options-help'
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/constants': 'link:../constants'
|
||||
'@pnpm/core-loggers': 'link:../core-loggers'
|
||||
'@pnpm/error': 'link:../error'
|
||||
'@pnpm/filter-workspace-packages': 'link:../filter-workspace-packages'
|
||||
'@pnpm/find-workspace-dir': 'link:../find-workspace-dir'
|
||||
'@pnpm/find-workspace-packages': 'link:../find-workspace-packages'
|
||||
'@pnpm/package-store': 'link:../package-store'
|
||||
'@pnpm/plugin-commands-rebuild': 'link:../plugin-commands-rebuild'
|
||||
'@pnpm/plugin-commands-recursive': 'link:../plugin-commands-recursive'
|
||||
'@pnpm/pnpmfile': 'link:../pnpmfile'
|
||||
'@pnpm/resolver-base': 'link:../resolver-base'
|
||||
'@pnpm/sort-packages': 'link:../sort-packages'
|
||||
'@pnpm/store-connection-manager': 'link:../store-connection-manager'
|
||||
'@pnpm/types': 'link:../types'
|
||||
'@pnpm/utils': 'link:../utils'
|
||||
camelcase-keys: 6.1.1
|
||||
common-tags: 1.8.0
|
||||
is-subdir: 1.1.1
|
||||
mem: 6.0.1
|
||||
mz: 2.7.0
|
||||
p-filter: 2.1.0
|
||||
p-limit: 2.2.1
|
||||
path-absolute: 1.0.1
|
||||
path-exists: 4.0.0
|
||||
ramda: 0.26.1
|
||||
read-ini-file: 2.0.0
|
||||
render-help: 0.0.0
|
||||
supi: 'link:../supi'
|
||||
devDependencies:
|
||||
'@pnpm/lockfile-types': 'link:../lockfile-types'
|
||||
'@pnpm/logger': 3.1.0
|
||||
'@pnpm/plugin-commands-installation': 'link:'
|
||||
'@pnpm/prepare': 'link:../../privatePackages/prepare'
|
||||
'@pnpm/registry-mock': 1.11.1
|
||||
'@pnpm/test-fixtures': 'link:../../privatePackages/test-fixtures'
|
||||
'@types/common-tags': 1.8.0
|
||||
'@types/mz': 2.7.0
|
||||
'@types/ramda': 0.26.38
|
||||
make-dir: 3.0.0
|
||||
read-yaml-file: 1.1.0
|
||||
rimraf: 3.0.0
|
||||
write-json-file: 4.2.1
|
||||
write-yaml-file: 3.0.1
|
||||
specifiers:
|
||||
'@pnpm/cli-utils': 'workspace:0.2.5'
|
||||
'@pnpm/common-cli-options-help': 'workspace:0.1.2'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/constants': 'workspace:3.0.0'
|
||||
'@pnpm/core-loggers': 'workspace:4.0.0'
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'workspace:1.0.1'
|
||||
'@pnpm/find-workspace-dir': 'workspace:1.0.0'
|
||||
'@pnpm/find-workspace-packages': 'workspace:2.0.8'
|
||||
'@pnpm/lockfile-types': 'workspace:1.1.0'
|
||||
'@pnpm/logger': ^3.1.0
|
||||
'@pnpm/package-store': 'workspace:7.0.2'
|
||||
'@pnpm/plugin-commands-installation': 'link:'
|
||||
'@pnpm/plugin-commands-rebuild': 'workspace:0.0.0'
|
||||
'@pnpm/plugin-commands-recursive': 'workspace:0.1.10'
|
||||
'@pnpm/pnpmfile': 'workspace:0.1.0'
|
||||
'@pnpm/prepare': 'workspace:0.0.0'
|
||||
'@pnpm/registry-mock': 1.11.1
|
||||
'@pnpm/resolver-base': 'workspace:6.0.0'
|
||||
'@pnpm/sort-packages': 'workspace:0.0.0'
|
||||
'@pnpm/store-connection-manager': 'workspace:0.2.5'
|
||||
'@pnpm/test-fixtures': 'workspace:0.0.0'
|
||||
'@pnpm/types': 'workspace:4.0.0'
|
||||
'@pnpm/utils': 'workspace:0.12.2'
|
||||
'@types/common-tags': ^1.8.0
|
||||
'@types/mz': ^2.7.0
|
||||
'@types/ramda': ^0.26.38
|
||||
camelcase-keys: 6.1.1
|
||||
common-tags: 1.8.0
|
||||
is-subdir: 1.1.1
|
||||
make-dir: 3.0.0
|
||||
mem: 6.0.1
|
||||
mz: 2.7.0
|
||||
p-filter: 2.1.0
|
||||
p-limit: 2.2.1
|
||||
path-absolute: 1.0.1
|
||||
path-exists: 4.0.0
|
||||
ramda: 0.26.1
|
||||
read-ini-file: 2.0.0
|
||||
read-yaml-file: 1.1.0
|
||||
render-help: 0.0.0
|
||||
rimraf: 3.0.0
|
||||
supi: 'workspace:0.37.6'
|
||||
write-json-file: 4.2.1
|
||||
write-yaml-file: 3.0.1
|
||||
packages/plugin-commands-listing:
|
||||
dependencies:
|
||||
'@pnpm/cli-utils': 'link:../cli-utils'
|
||||
@@ -1399,6 +1441,9 @@ importers:
|
||||
render-help: 0.0.0
|
||||
devDependencies:
|
||||
'@pnpm/constants': 'link:../constants'
|
||||
'@pnpm/filter-workspace-packages': 'link:../filter-workspace-packages'
|
||||
'@pnpm/logger': 3.1.0
|
||||
'@pnpm/plugin-commands-installation': 'link:../plugin-commands-installation'
|
||||
'@pnpm/plugin-commands-listing': 'link:'
|
||||
'@pnpm/prepare': 'link:../../privatePackages/prepare'
|
||||
'@types/common-tags': 1.8.0
|
||||
@@ -1415,7 +1460,10 @@ importers:
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/constants': 'workspace:3.0.0'
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'workspace:1.0.1'
|
||||
'@pnpm/list': 'workspace:4.0.12'
|
||||
'@pnpm/logger': ^3.1.0
|
||||
'@pnpm/plugin-commands-installation': 'workspace:*'
|
||||
'@pnpm/plugin-commands-listing': 'link:'
|
||||
'@pnpm/prepare': 'workspace:0.0.0'
|
||||
'@types/common-tags': 1.8.0
|
||||
@@ -1449,6 +1497,8 @@ importers:
|
||||
wrap-ansi: 6.2.0
|
||||
devDependencies:
|
||||
'@pnpm/constants': 'link:../constants'
|
||||
'@pnpm/filter-workspace-packages': 'link:../filter-workspace-packages'
|
||||
'@pnpm/plugin-commands-installation': 'link:../plugin-commands-installation'
|
||||
'@pnpm/plugin-commands-outdated': 'link:'
|
||||
'@pnpm/prepare': 'link:../../privatePackages/prepare'
|
||||
'@pnpm/types': 'link:../types'
|
||||
@@ -1467,10 +1517,12 @@ importers:
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/constants': 'workspace:3.0.0'
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'workspace:1.0.1'
|
||||
'@pnpm/lockfile-file': 'workspace:3.0.1'
|
||||
'@pnpm/matcher': 'workspace:1.0.0'
|
||||
'@pnpm/modules-yaml': 'workspace:5.0.0'
|
||||
'@pnpm/outdated': 'workspace:6.0.5'
|
||||
'@pnpm/plugin-commands-installation': 'workspace:*'
|
||||
'@pnpm/plugin-commands-outdated': 'link:'
|
||||
'@pnpm/prepare': 'workspace:0.0.0'
|
||||
'@pnpm/semver-diff': 1.0.2
|
||||
@@ -1496,23 +1548,32 @@ importers:
|
||||
'@pnpm/cli-utils': 'link:../cli-utils'
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/error': 'link:../error'
|
||||
'@pnpm/npm-resolver': 'link:../npm-resolver'
|
||||
'@pnpm/read-importer-manifest': 'link:../read-importer-manifest'
|
||||
'@pnpm/resolver-base': 'link:../resolver-base'
|
||||
'@pnpm/run-npm': 'link:../run-npm'
|
||||
'@pnpm/store-path': 2.1.1
|
||||
'@pnpm/types': 'link:../types'
|
||||
'@pnpm/utils': 'link:../utils'
|
||||
'@zkochan/rimraf': 1.0.0
|
||||
cp-file: 7.0.0
|
||||
fast-glob: 3.1.1
|
||||
lru-cache: 5.1.1
|
||||
mz: 2.7.0
|
||||
p-filter: 2.1.0
|
||||
ramda: 0.26.1
|
||||
render-help: 0.0.0
|
||||
write-json-file: 4.2.1
|
||||
devDependencies:
|
||||
'@pnpm/filter-workspace-packages': 'link:../filter-workspace-packages'
|
||||
'@pnpm/plugin-commands-publishing': 'link:'
|
||||
'@pnpm/prepare': 'link:../../privatePackages/prepare'
|
||||
'@types/cross-spawn': 6.0.1
|
||||
'@types/lru-cache': 5.1.0
|
||||
'@types/mz': 2.7.0
|
||||
'@types/ramda': 0.26.38
|
||||
cross-spawn: 7.0.1
|
||||
execa: 4.0.0
|
||||
path-exists: 4.0.0
|
||||
rimraf: 3.0.0
|
||||
write-yaml-file: 3.0.1
|
||||
@@ -1520,19 +1581,28 @@ importers:
|
||||
'@pnpm/cli-utils': 'workspace:0.2.5'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'workspace:1.0.1'
|
||||
'@pnpm/npm-resolver': 'workspace:6.0.2'
|
||||
'@pnpm/plugin-commands-publishing': 'link:'
|
||||
'@pnpm/prepare': 'workspace:0.0.0'
|
||||
'@pnpm/read-importer-manifest': 'workspace:2.0.1'
|
||||
'@pnpm/resolver-base': 'workspace:6.0.0'
|
||||
'@pnpm/run-npm': 'workspace:1.0.0'
|
||||
'@pnpm/store-path': 2.1.1
|
||||
'@pnpm/types': 'workspace:4.0.0'
|
||||
'@pnpm/utils': 'workspace:0.12.2'
|
||||
'@types/cross-spawn': ^6.0.1
|
||||
'@types/lru-cache': ^5.1.0
|
||||
'@types/mz': ^2.7.0
|
||||
'@types/ramda': ^0.26.38
|
||||
'@zkochan/rimraf': 1.0.0
|
||||
cp-file: 7.0.0
|
||||
cross-spawn: 7.0.1
|
||||
execa: 4.0.0
|
||||
fast-glob: 3.1.1
|
||||
lru-cache: 5.1.1
|
||||
mz: 2.7.0
|
||||
p-filter: 2.1.0
|
||||
path-exists: 4.0.0
|
||||
ramda: 0.26.1
|
||||
render-help: 0.0.0
|
||||
@@ -1546,6 +1616,7 @@ importers:
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/constants': 'link:../constants'
|
||||
'@pnpm/core-loggers': 'link:../core-loggers'
|
||||
'@pnpm/find-workspace-packages': 'link:../find-workspace-packages'
|
||||
'@pnpm/get-context': 'link:../get-context'
|
||||
'@pnpm/lifecycle': 'link:../lifecycle'
|
||||
'@pnpm/link-bins': 5.0.1
|
||||
@@ -1553,21 +1624,26 @@ importers:
|
||||
'@pnpm/lockfile-walker': 'link:../lockfile-walker'
|
||||
'@pnpm/modules-yaml': 'link:../modules-yaml'
|
||||
'@pnpm/pkgid-to-filename': 2.0.0
|
||||
'@pnpm/sort-packages': 'link:../sort-packages'
|
||||
'@pnpm/store-connection-manager': 'link:../store-connection-manager'
|
||||
'@pnpm/store-controller-types': 'link:../store-controller-types'
|
||||
'@pnpm/types': 'link:../types'
|
||||
'@pnpm/utils': 'link:../utils'
|
||||
'@zkochan/npm-package-arg': 1.0.2
|
||||
camelcase-keys: 6.1.1
|
||||
common-tags: 1.8.0
|
||||
dependency-path: 'link:../dependency-path'
|
||||
graph-sequencer: 2.0.0
|
||||
load-json-file: 6.2.0
|
||||
mem: 6.0.1
|
||||
p-limit: 2.2.1
|
||||
ramda: 0.26.1
|
||||
read-ini-file: 2.0.0
|
||||
render-help: 0.0.0
|
||||
run-groups: 2.0.1
|
||||
semver: 7.1.1
|
||||
devDependencies:
|
||||
'@pnpm/filter-workspace-packages': 'link:../filter-workspace-packages'
|
||||
'@pnpm/logger': 3.1.0
|
||||
'@pnpm/plugin-commands-rebuild': 'link:'
|
||||
'@pnpm/prepare': 'link:../../privatePackages/prepare'
|
||||
@@ -1581,12 +1657,15 @@ importers:
|
||||
path-exists: 4.0.0
|
||||
rimraf: 3.0.0
|
||||
sinon: 8.0.0
|
||||
write-yaml-file: 3.0.1
|
||||
specifiers:
|
||||
'@pnpm/cli-utils': 'workspace:0.2.5'
|
||||
'@pnpm/common-cli-options-help': 'workspace:0.1.2'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/constants': 'workspace:3.0.0'
|
||||
'@pnpm/core-loggers': 'workspace:4.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'workspace:1.0.1'
|
||||
'@pnpm/find-workspace-packages': 'workspace:2.0.8'
|
||||
'@pnpm/get-context': 'workspace:0.0.0'
|
||||
'@pnpm/lifecycle': 'workspace:8.0.1'
|
||||
'@pnpm/link-bins': 5.0.1
|
||||
@@ -1598,6 +1677,7 @@ importers:
|
||||
'@pnpm/plugin-commands-rebuild': 'link:'
|
||||
'@pnpm/prepare': 'workspace:0.0.0'
|
||||
'@pnpm/registry-mock': 1.11.1
|
||||
'@pnpm/sort-packages': 'workspace:0.0.0'
|
||||
'@pnpm/store-connection-manager': 'workspace:0.2.5'
|
||||
'@pnpm/store-controller-types': 'workspace:6.0.0'
|
||||
'@pnpm/test-fixtures': 'workspace:0.0.0'
|
||||
@@ -1608,138 +1688,22 @@ importers:
|
||||
'@types/semver': ^6.2.0
|
||||
'@types/sinon': ^7.5.1
|
||||
'@zkochan/npm-package-arg': 1.0.2
|
||||
camelcase-keys: 6.1.1
|
||||
common-tags: 1.8.0
|
||||
dependency-path: 'workspace:4.0.2'
|
||||
execa: 4.0.0
|
||||
graph-sequencer: 2.0.0
|
||||
load-json-file: 6.2.0
|
||||
mem: 6.0.1
|
||||
p-limit: 2.2.1
|
||||
path-exists: 4.0.0
|
||||
ramda: 0.26.1
|
||||
read-ini-file: 2.0.0
|
||||
render-help: 0.0.0
|
||||
rimraf: 3.0.0
|
||||
run-groups: 2.0.1
|
||||
semver: 7.1.1
|
||||
sinon: 8.0.0
|
||||
packages/plugin-commands-recursive:
|
||||
dependencies:
|
||||
'@pnpm/cli-utils': 'link:../cli-utils'
|
||||
'@pnpm/common-cli-options-help': 'link:../common-cli-options-help'
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/constants': 'link:../constants'
|
||||
'@pnpm/core-loggers': 'link:../core-loggers'
|
||||
'@pnpm/error': 'link:../error'
|
||||
'@pnpm/filter-workspace-packages': 'link:../filter-workspace-packages'
|
||||
'@pnpm/find-workspace-packages': 'link:../find-workspace-packages'
|
||||
'@pnpm/lifecycle': 'link:../lifecycle'
|
||||
'@pnpm/lockfile-file': 'link:../lockfile-file'
|
||||
'@pnpm/matcher': 'link:../matcher'
|
||||
'@pnpm/npm-resolver': 'link:../npm-resolver'
|
||||
'@pnpm/outdated': 'link:../outdated'
|
||||
'@pnpm/plugin-commands-listing': 'link:../plugin-commands-listing'
|
||||
'@pnpm/plugin-commands-outdated': 'link:../plugin-commands-outdated'
|
||||
'@pnpm/plugin-commands-publishing': 'link:../plugin-commands-publishing'
|
||||
'@pnpm/plugin-commands-rebuild': 'link:../plugin-commands-rebuild'
|
||||
'@pnpm/pnpmfile': 'link:../pnpmfile'
|
||||
'@pnpm/resolver-base': 'link:../resolver-base'
|
||||
'@pnpm/run-npm': 'link:../run-npm'
|
||||
'@pnpm/store-connection-manager': 'link:../store-connection-manager'
|
||||
'@pnpm/store-path': 2.1.1
|
||||
'@pnpm/types': 'link:../types'
|
||||
'@pnpm/utils': 'link:../utils'
|
||||
camelcase-keys: 6.1.1
|
||||
chalk: 3.0.0
|
||||
common-tags: 1.8.0
|
||||
execa: 4.0.0
|
||||
graph-sequencer: 2.0.0
|
||||
is-subdir: 1.1.1
|
||||
lru-cache: 5.1.1
|
||||
mem: 6.0.1
|
||||
mz: 2.7.0
|
||||
p-filter: 2.1.0
|
||||
p-limit: 2.2.1
|
||||
pkgs-graph: 'link:../pkgs-graph'
|
||||
ramda: 0.26.1
|
||||
read-ini-file: 2.0.0
|
||||
render-help: 0.0.0
|
||||
supi: 'link:../supi'
|
||||
table: 5.4.6
|
||||
devDependencies:
|
||||
'@pnpm/lockfile-types': 'link:../lockfile-types'
|
||||
'@pnpm/logger': 3.1.0
|
||||
'@pnpm/plugin-commands-recursive': 'link:'
|
||||
'@pnpm/prepare': 'link:../../privatePackages/prepare'
|
||||
'@types/common-tags': 1.8.0
|
||||
'@types/lru-cache': 5.1.0
|
||||
'@types/mz': 2.7.0
|
||||
'@types/ramda': 0.26.38
|
||||
'@types/table': 4.0.7
|
||||
'@zkochan/rimraf': 1.0.0
|
||||
make-dir: 3.0.0
|
||||
path-exists: 4.0.0
|
||||
read-yaml-file: 1.1.0
|
||||
rimraf: 3.0.0
|
||||
strip-ansi: 6.0.0
|
||||
write-json-file: 4.2.1
|
||||
write-yaml-file: 3.0.1
|
||||
specifiers:
|
||||
'@pnpm/cli-utils': 'workspace:0.2.5'
|
||||
'@pnpm/common-cli-options-help': 'workspace:0.1.2'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/constants': 'workspace:3.0.0'
|
||||
'@pnpm/core-loggers': 'workspace:4.0.0'
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'workspace:1.0.1'
|
||||
'@pnpm/find-workspace-packages': 'workspace:2.0.8'
|
||||
'@pnpm/lifecycle': 'workspace:8.0.1'
|
||||
'@pnpm/lockfile-file': 'workspace:3.0.1'
|
||||
'@pnpm/lockfile-types': 'workspace:1.1.0'
|
||||
'@pnpm/logger': 3.1.0
|
||||
'@pnpm/matcher': 'workspace:1.0.0'
|
||||
'@pnpm/npm-resolver': 'workspace:6.0.2'
|
||||
'@pnpm/outdated': 'workspace:6.0.5'
|
||||
'@pnpm/plugin-commands-listing': 'workspace:0.1.9'
|
||||
'@pnpm/plugin-commands-outdated': 'workspace:0.1.9'
|
||||
'@pnpm/plugin-commands-publishing': 'workspace:0.1.9'
|
||||
'@pnpm/plugin-commands-rebuild': 'workspace:0.0.0'
|
||||
'@pnpm/plugin-commands-recursive': 'link:'
|
||||
'@pnpm/pnpmfile': 'workspace:0.1.0'
|
||||
'@pnpm/prepare': 'workspace:0.0.0'
|
||||
'@pnpm/resolver-base': 'workspace:6.0.0'
|
||||
'@pnpm/run-npm': 'workspace:1.0.0'
|
||||
'@pnpm/store-connection-manager': 'workspace:0.2.5'
|
||||
'@pnpm/store-path': 2.1.1
|
||||
'@pnpm/types': 'workspace:4.0.0'
|
||||
'@pnpm/utils': 'workspace:0.12.2'
|
||||
'@types/common-tags': ^1.8.0
|
||||
'@types/lru-cache': ^5.1.0
|
||||
'@types/mz': ^2.7.0
|
||||
'@types/ramda': ^0.26.38
|
||||
'@types/table': ^4.0.7
|
||||
'@zkochan/rimraf': 1.0.0
|
||||
camelcase-keys: 6.1.1
|
||||
chalk: 3.0.0
|
||||
common-tags: 1.8.0
|
||||
execa: 4.0.0
|
||||
graph-sequencer: 2.0.0
|
||||
is-subdir: 1.1.1
|
||||
lru-cache: 5.1.1
|
||||
make-dir: 3.0.0
|
||||
mem: 6.0.1
|
||||
mz: 2.7.0
|
||||
p-filter: 2.1.0
|
||||
p-limit: 2.2.1
|
||||
path-exists: 4.0.0
|
||||
pkgs-graph: 'workspace:5.0.1'
|
||||
ramda: 0.26.1
|
||||
read-ini-file: 2.0.0
|
||||
read-yaml-file: 1.1.0
|
||||
render-help: 0.0.0
|
||||
rimraf: 3.0.0
|
||||
strip-ansi: 6.0.0
|
||||
supi: 'workspace:0.37.6'
|
||||
table: 5.4.6
|
||||
write-json-file: 4.2.1
|
||||
write-yaml-file: 3.0.1
|
||||
packages/plugin-commands-script-runners:
|
||||
dependencies:
|
||||
@@ -1748,39 +1712,51 @@ importers:
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/error': 'link:../error'
|
||||
'@pnpm/lifecycle': 'link:../lifecycle'
|
||||
'@pnpm/sort-packages': 'link:../sort-packages'
|
||||
'@pnpm/types': 'link:../types'
|
||||
'@pnpm/utils': 'link:../utils'
|
||||
common-tags: 1.8.0
|
||||
p-limit: 2.2.1
|
||||
ramda: 0.26.1
|
||||
render-help: 0.0.0
|
||||
devDependencies:
|
||||
'@pnpm/filter-workspace-packages': 'link:../filter-workspace-packages'
|
||||
'@pnpm/logger': 3.1.0
|
||||
'@pnpm/plugin-commands-script-runners': 'link:'
|
||||
'@pnpm/prepare': 'link:../../privatePackages/prepare'
|
||||
'@types/common-tags': 1.8.0
|
||||
'@types/mz': 2.7.0
|
||||
'@types/ramda': 0.26.38
|
||||
'@zkochan/rimraf': 1.0.0
|
||||
execa: 4.0.0
|
||||
mz: 2.7.0
|
||||
rimraf: 3.0.0
|
||||
write-yaml-file: 3.0.1
|
||||
specifiers:
|
||||
'@pnpm/cli-utils': 'workspace:0.2.5'
|
||||
'@pnpm/common-cli-options-help': 'workspace:0.1.2'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/error': 'workspace:1.0.0'
|
||||
'@pnpm/filter-workspace-packages': 'workspace:1.0.1'
|
||||
'@pnpm/lifecycle': 'workspace:8.0.1'
|
||||
'@pnpm/logger': ^3.1.0
|
||||
'@pnpm/plugin-commands-script-runners': 'link:'
|
||||
'@pnpm/prepare': 'workspace:0.0.0'
|
||||
'@pnpm/sort-packages': 'workspace:0.0.0'
|
||||
'@pnpm/types': 'workspace:4.0.0'
|
||||
'@pnpm/utils': 'workspace:0.12.2'
|
||||
'@types/common-tags': ^1.8.0
|
||||
'@types/mz': ^2.7.0
|
||||
'@types/ramda': ^0.26.38
|
||||
'@zkochan/rimraf': 1.0.0
|
||||
common-tags: 1.8.0
|
||||
execa: 4.0.0
|
||||
mz: 2.7.0
|
||||
p-limit: 2.2.1
|
||||
ramda: 0.26.1
|
||||
render-help: 0.0.0
|
||||
rimraf: 3.0.0
|
||||
write-yaml-file: 3.0.1
|
||||
packages/plugin-commands-server:
|
||||
dependencies:
|
||||
'@pnpm/cli-utils': 'link:../cli-utils'
|
||||
@@ -1908,6 +1884,7 @@ importers:
|
||||
packages/pnpm:
|
||||
dependencies:
|
||||
'@pnpm/cli-utils': 'link:../cli-utils'
|
||||
'@pnpm/common-cli-options-help': 'link:../common-cli-options-help'
|
||||
'@pnpm/config': 'link:../config'
|
||||
'@pnpm/core-loggers': 'link:../core-loggers'
|
||||
'@pnpm/default-reporter': 'link:../default-reporter'
|
||||
@@ -1923,7 +1900,6 @@ importers:
|
||||
'@pnpm/plugin-commands-outdated': 'link:../plugin-commands-outdated'
|
||||
'@pnpm/plugin-commands-publishing': 'link:../plugin-commands-publishing'
|
||||
'@pnpm/plugin-commands-rebuild': 'link:../plugin-commands-rebuild'
|
||||
'@pnpm/plugin-commands-recursive': 'link:../plugin-commands-recursive'
|
||||
'@pnpm/plugin-commands-script-runners': 'link:../plugin-commands-script-runners'
|
||||
'@pnpm/plugin-commands-server': 'link:../plugin-commands-server'
|
||||
'@pnpm/plugin-commands-store': 'link:../plugin-commands-store'
|
||||
@@ -1995,6 +1971,7 @@ importers:
|
||||
specifiers:
|
||||
'@pnpm/assert-project': 'workspace:*'
|
||||
'@pnpm/cli-utils': 'workspace:0.2.5'
|
||||
'@pnpm/common-cli-options-help': 'workspace:0.1.2'
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/constants': 'workspace:3.0.0'
|
||||
'@pnpm/core-loggers': 'workspace:4.0.0'
|
||||
@@ -2014,7 +1991,6 @@ importers:
|
||||
'@pnpm/plugin-commands-outdated': 'workspace:0.1.9'
|
||||
'@pnpm/plugin-commands-publishing': 'workspace:0.1.9'
|
||||
'@pnpm/plugin-commands-rebuild': 'workspace:0.0.0'
|
||||
'@pnpm/plugin-commands-recursive': 'workspace:0.1.10'
|
||||
'@pnpm/plugin-commands-script-runners': 'workspace:0.1.9'
|
||||
'@pnpm/plugin-commands-server': 'workspace:0.0.0'
|
||||
'@pnpm/plugin-commands-store': 'workspace:0.0.0'
|
||||
@@ -2309,6 +2285,18 @@ importers:
|
||||
promise-share: 1.0.0
|
||||
rimraf: 3.0.0
|
||||
uuid: 3.3.3
|
||||
packages/sort-packages:
|
||||
dependencies:
|
||||
'@pnpm/config': 'link:../config'
|
||||
graph-sequencer: 2.0.0
|
||||
devDependencies:
|
||||
'@pnpm/sort-packages': 'link:'
|
||||
rimraf: 3.0.0
|
||||
specifiers:
|
||||
'@pnpm/config': 'workspace:6.0.0'
|
||||
'@pnpm/sort-packages': 'link:'
|
||||
graph-sequencer: 2.0.0
|
||||
rimraf: 3.0.0
|
||||
packages/store-connection-manager:
|
||||
dependencies:
|
||||
'@pnpm/cli-utils': 'link:../cli-utils'
|
||||
@@ -3281,7 +3269,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/events': 3.0.0
|
||||
'@types/minimatch': 3.0.3
|
||||
'@types/node': 12.12.21
|
||||
'@types/node': 13.1.0
|
||||
resolution:
|
||||
integrity: sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==
|
||||
/@types/graceful-fs/4.1.3:
|
||||
@@ -3338,7 +3326,7 @@ packages:
|
||||
integrity: sha512-U5icWpv7YnZYGsN4/cmh3WD2onMY0aJIiTE6+51TwJCttdHvtCYmkBNOobHlXwrJRL0nkH9jH4kD+1FAdMN4Tg==
|
||||
/@types/mz/2.7.0:
|
||||
dependencies:
|
||||
'@types/node': 12.12.21
|
||||
'@types/node': 13.1.1
|
||||
resolution:
|
||||
integrity: sha512-Q5TZYMKnH0hdV5fNstmMWL2LLw5eRRtTd73KNtsZxoQ2gtCQyET5X79uERUEwGneuxPglg441I7OSY00+9CkSw==
|
||||
/@types/ncp/2.0.3:
|
||||
@@ -3360,6 +3348,12 @@ packages:
|
||||
/@types/node/12.12.21:
|
||||
resolution:
|
||||
integrity: sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA==
|
||||
/@types/node/13.1.0:
|
||||
resolution:
|
||||
integrity: sha512-zwrxviZS08kRX40nqBrmERElF2vpw4IUTd5khkhBTfFH8AOaeoLVx48EC4+ZzS2/Iga7NevncqnsUSYjM4OWYA==
|
||||
/@types/node/13.1.1:
|
||||
resolution:
|
||||
integrity: sha512-hx6zWtudh3Arsbl3cXay+JnkvVgCKzCWKv42C9J01N2T2np4h8w5X8u6Tpz5mj38kE3M9FM0Pazx8vKFFMnjLQ==
|
||||
/@types/nopt/3.0.29:
|
||||
dev: true
|
||||
resolution:
|
||||
@@ -3623,7 +3617,7 @@ packages:
|
||||
/@zkochan/rimraf/1.0.0:
|
||||
dependencies:
|
||||
'@types/glob': 7.1.1
|
||||
'@types/node': 12.12.21
|
||||
'@types/node': 13.1.0
|
||||
rimraf: 3.0.0
|
||||
engines:
|
||||
node: '>=8.15'
|
||||
@@ -5783,7 +5777,7 @@ packages:
|
||||
human-signals: 1.1.1
|
||||
is-stream: 2.0.0
|
||||
merge-stream: 2.0.0
|
||||
npm-run-path: 4.0.0
|
||||
npm-run-path: 4.0.1
|
||||
onetime: 5.1.0
|
||||
signal-exit: 3.0.2
|
||||
strip-final-newline: 2.0.0
|
||||
@@ -8353,13 +8347,13 @@ packages:
|
||||
node: '>=4'
|
||||
resolution:
|
||||
integrity: sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=
|
||||
/npm-run-path/4.0.0:
|
||||
/npm-run-path/4.0.1:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
engines:
|
||||
node: '>=8'
|
||||
resolution:
|
||||
integrity: sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ==
|
||||
integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
|
||||
/npm/4.6.1:
|
||||
bundledDependencies:
|
||||
- abbrev
|
||||
|
||||
Reference in New Issue
Block a user