From 1086cdfd7af4763399d4136d34989d325b4f8871 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Sat, 13 Jul 2019 19:49:15 +0300 Subject: [PATCH] feat: "pnpm i" in a workspace is "pnpm i -r" When `use-beta-cli` is true, `pnpm i` is the same as `pnpm i -r` inside a workspace. Also, adding deps to the root workspace package is only allowed with the `--ignore-workspace-root-check` or `-W` flag. close #1444 PR #1913 --- packages/config/src/PnpmConfigs.ts | 2 + packages/config/src/index.ts | 4 ++ packages/pnpm/src/main.ts | 25 +++++++ packages/pnpm/test/recursive/misc.ts | 98 +++++++++++++++++++++++++++- 4 files changed, 128 insertions(+), 1 deletion(-) diff --git a/packages/config/src/PnpmConfigs.ts b/packages/config/src/PnpmConfigs.ts index 675cacdda9..7542a0c833 100644 --- a/packages/config/src/PnpmConfigs.ts +++ b/packages/config/src/PnpmConfigs.ts @@ -3,6 +3,7 @@ import { Registries } from '@pnpm/types' export interface PnpmConfigs extends Record { // tslint:disable-line bail: boolean, cliArgs: Record, // tslint:disable-line + useBetaCli: boolean, extraBinPaths: string[], filter: string[], rawNpmConfig: Record, // tslint:disable-line @@ -81,4 +82,5 @@ export interface PnpmConfigs extends Record { // tslint:disable-lin resolutionStrategy: 'fast' | 'fewer-dependencies', registries: Registries, + ignoreWorkspaceRootCheck: boolean, } diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index f59e61c03e..b4a5c22486 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -32,6 +32,7 @@ export const types = Object.assign({ 'ignore-pnpmfile': Boolean, 'ignore-stop-requests': Boolean, 'ignore-upload-requests': Boolean, + 'ignore-workspace-root-check': Boolean, 'independent-leaves': Boolean, 'latest': Boolean, 'link-workspace-packages': Boolean, @@ -64,6 +65,7 @@ export const types = Object.assign({ 'sort': Boolean, 'store': path, 'strict-peer-dependencies': Boolean, + 'use-beta-cli': Boolean, 'use-running-store-server': Boolean, 'use-store-server': Boolean, 'verify-store-integrity': Boolean, @@ -129,6 +131,7 @@ export default async ( 'fetch-retry-maxtimeout': 60000, 'fetch-retry-mintimeout': 10000, 'globalconfig': npmDefaults.globalconfig, + 'ignore-workspace-root-check': false, 'latest': false, 'link-workspace-packages': true, 'lock': true, @@ -143,6 +146,7 @@ export default async ( 'sort': true, 'strict-peer-dependencies': false, 'unsafe-perm': npmDefaults['unsafe-perm'], + 'use-beta-cli': false, 'userconfig': npmDefaults.userconfig, 'workspace-concurrency': 4, 'workspace-prefix': workspacePrefix, diff --git a/packages/pnpm/src/main.ts b/packages/pnpm/src/main.ts index 287d5fca82..75b7e5351e 100644 --- a/packages/pnpm/src/main.ts +++ b/packages/pnpm/src/main.ts @@ -136,6 +136,7 @@ export default async function run (argv: string[]) { 'shared-workspace-lockfile': ['--shared-workspace-shrinkwrap'], 'frozen-lockfile': ['--frozen-shrinkwrap'], 'prefer-frozen-lockfile': ['--prefer-frozen-shrinkwrap'], + 'W': ['--ignore-workspace-root-check'], } // tslint:enable const cliConf = nopt(types, shortHands, argv, 0) @@ -201,6 +202,30 @@ export default async function run (argv: string[]) { return } + if ( + opts.useBetaCli && + cmd === 'install' && + typeof opts.workspacePrefix === 'string' + ) { + if (cliArgs.length === 0) { + subCmd = cmd + cmd = 'recursive' + cliArgs.unshift(subCmd) + } else if ( + opts.workspacePrefix === opts.prefix && + !opts.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 = opts.global && (cmd === 'install' || cmd === 'update') && cliConf.argv.remain.includes(packageManager.name) // Don't check for updates diff --git a/packages/pnpm/test/recursive/misc.ts b/packages/pnpm/test/recursive/misc.ts index a3a05a6d68..8b126593cf 100644 --- a/packages/pnpm/test/recursive/misc.ts +++ b/packages/pnpm/test/recursive/misc.ts @@ -1,5 +1,5 @@ import { Lockfile } from '@pnpm/lockfile-types' -import { preparePackages } from '@pnpm/prepare' +import prepare, { preparePackages } from '@pnpm/prepare' import isCI = require('is-ci') import isWindows = require('is-windows') import makeDir = require('make-dir') @@ -12,6 +12,7 @@ import writeJsonFile = require('write-json-file') import writeYamlFile = require('write-yaml-file') import { execPnpm, + execPnpmSync, retryLoadJsonFile, spawn, } from '../utils' @@ -107,6 +108,66 @@ test('recursive install using "install --recursive"', async (t: tape.Test) => { t.ok(projects['project-2'].requireModule('is-negative')) }) +test('installation in the root of a workspace with "install" when the "use-beta-cli" config is true', async (t: tape.Test) => { + const projects = preparePackages(t, [ + { + name: 'project-1', + version: '1.0.0', + + dependencies: { + 'is-positive': '1.0.0', + }, + }, + { + name: 'project-2', + version: '1.0.0', + + dependencies: { + 'is-negative': '1.0.0', + }, + }, + ]) + + await fs.writeFile('pnpm-workspace.yaml', '', 'utf8') + await fs.writeFile('.npmrc', 'use-beta-cli=true', 'utf8') + + await execPnpm('install') + + t.ok(projects['project-1'].requireModule('is-positive')) + t.ok(projects['project-2'].requireModule('is-negative')) +}) + +test('installation in a subdirectory of a workspace with "install" when the "use-beta-cli" config is true', async (t: tape.Test) => { + const projects = preparePackages(t, [ + { + name: 'project-1', + version: '1.0.0', + + dependencies: { + 'is-positive': '1.0.0', + }, + }, + { + name: 'project-2', + version: '1.0.0', + + dependencies: { + 'is-negative': '1.0.0', + }, + }, + ]) + + await fs.writeFile('pnpm-workspace.yaml', '', 'utf8') + await fs.writeFile('.npmrc', 'use-beta-cli=true', 'utf8') + + process.chdir('project-1') + + await execPnpm('install') + + t.ok(projects['project-1'].requireModule('is-positive')) + t.ok(projects['project-2'].requireModule('is-negative')) +}) + test('recursive install should install in whole workspace even when executed in a subdirectory', async (t: tape.Test) => { const projects = preparePackages(t, [ { @@ -1153,3 +1214,38 @@ test('recursive install --no-bail', async (t: tape.Test) => { t.ok(projects['project-2'].requireModule('is-negative')) }) + +test('adding new dependency in the root should fail if --ignore-workspace-root-check is not used', async (t: tape.Test) => { + const project = prepare(t) + + await fs.writeFile('pnpm-workspace.yaml', '', 'utf8') + await fs.writeFile('.npmrc', 'use-beta-cli=true', 'utf8') + + { + const { status, stderr } = execPnpmSync('add', 'is-positive') + + t.equal(status, 1) + + t.ok( + stderr.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).' + ) + ) + } + + { + const { status } = execPnpmSync('add', 'is-positive', '--ignore-workspace-root-check') + + t.equal(status, 0) + await project.has('is-positive') + } + + { + const { status } = execPnpmSync('add', 'is-negative', '-W') + + t.equal(status, 0) + await project.has('is-negative') + } +})