diff --git a/packages/list/src/index.ts b/packages/list/src/index.ts index 8f37fb83a9..8d00413f7e 100644 --- a/packages/list/src/index.ts +++ b/packages/list/src/index.ts @@ -80,12 +80,14 @@ export default async function ( ) { const opts = { ...DEFAULTS, ...maybeOpts } - const tree = await dh(projectPath, { - depth: opts.depth, - only: opts.only, - registries: opts.registries, - shrinkwrapDirectory: maybeOpts && maybeOpts.shrinkwrapDirectory, - }) + const tree = opts.depth === -1 + ? [] + : await dh(projectPath, { + depth: opts.depth, + only: opts.only, + registries: opts.registries, + shrinkwrapDirectory: maybeOpts && maybeOpts.shrinkwrapDirectory, + }) const print = getPrinter(opts.parseable) const entryPkg = await readPkg(path.resolve(projectPath, 'package.json')) diff --git a/packages/list/src/renderParseable.ts b/packages/list/src/renderParseable.ts index 9245b05c03..bb3ca00b29 100644 --- a/packages/list/src/renderParseable.ts +++ b/packages/list/src/renderParseable.ts @@ -1,7 +1,5 @@ import { PackageNode } from 'dependencies-hierarchy' -import path = require('path') import R = require('ramda') -import readPkg from './readPkg' const sortPackages = R.sortBy(R.prop('name')) @@ -30,13 +28,11 @@ export default async function ( return [ firstLine, ...pkgs.map((pkg) => `${pkg.path}:${pkg.name}@${pkg.version}`), - '', ].join('\n') } return [ project.path, ...pkgs.map((pkg) => pkg.path), - '', ].join('\n') } diff --git a/packages/list/src/renderTree.ts b/packages/list/src/renderTree.ts index 1bab4d63c0..3da2e9d196 100644 --- a/packages/list/src/renderTree.ts +++ b/packages/list/src/renderTree.ts @@ -38,7 +38,7 @@ export default async function ( }), }) - return s + return s.replace(/\n$/, '') } async function toArchyTree ( diff --git a/packages/list/test/index.ts b/packages/list/test/index.ts index 7e433e3e71..b233adadec 100644 --- a/packages/list/test/index.ts +++ b/packages/list/test/index.ts @@ -19,7 +19,7 @@ test('list all deps of a package that has an external shrinkwrap.yaml', async (t }), stripIndent` pkg@1.0.0 ${fixtureWithExternalShrinkwrap} └── is-positive@1.0.0 - ` + '\n') + `) t.end() }) @@ -30,7 +30,7 @@ test('list with default parameters', async t => { ├── is-negative@2.1.0 ├── is-positive@3.1.0 └── write-json-file@2.2.0 - ` + '\n') + `) t.end() }) @@ -41,7 +41,7 @@ test('list with default parameters in pkg that has no name and version', async t ├── is-negative@2.1.0 ├── is-positive@3.1.0 └── write-json-file@2.2.0 - ` + '\n') + `) t.end() }) @@ -52,7 +52,7 @@ test('list with default parameters in pkg that has no version', async t => { ├── is-negative@2.1.0 ├── is-positive@3.1.0 └── write-json-file@2.2.0 - ` + '\n') + `) t.end() }) @@ -61,7 +61,7 @@ test('list dev only', async t => { t.equal(await list(fixture, { only: 'dev' }), stripIndent` fixture@1.0.0 ${fixture} └── is-positive@3.1.0 - ` + '\n') + `) t.end() }) @@ -70,7 +70,7 @@ test('list prod only', async t => { t.equal(await list(fixture, { only: 'prod' }), stripIndent` fixture@1.0.0 ${fixture} └── write-json-file@2.2.0 - ` + '\n') + `) t.end() }) @@ -90,7 +90,7 @@ test('list prod only with depth 2', async t => { ├── graceful-fs@4.1.11 ├── imurmurhash@0.1.4 └── slide@1.1.6 - ` + '\n') + `) t.end() }) @@ -107,7 +107,13 @@ test('list with depth 1', async t => { ├── pify@2.3.0 ├── sort-keys@1.1.2 └── write-file-atomic@2.1.0 - ` + '\n') + `) + + t.end() +}) + +test('list with depth -1', async t => { + t.equal(await list(fixture, { depth: -1 }), `fixture@1.0.0 ${fixture}`) t.end() }) @@ -119,7 +125,7 @@ test('list with depth 1 and selected packages', async t => { └─┬ write-json-file@2.2.0 ├── ${highlighted('make-dir@1.0.0')} └── ${highlighted('pify@2.3.0')} - ` + '\n') + `) t.end() }) @@ -139,7 +145,7 @@ test('list in long format', async t => { Stringify and write JSON to a file atomically git+https://github.com/sindresorhus/write-json-file.git https://github.com/sindresorhus/write-json-file#readme - ` + '\n') + `) t.end() }) @@ -156,7 +162,7 @@ test('parseable list with depth 1', async t => { ${path.join(fixture, 'node_modules/.registry.npmjs.org/sort-keys/1.1.2')} ${path.join(fixture, 'node_modules/.registry.npmjs.org/write-file-atomic/2.1.0')} ${path.join(fixture, 'node_modules/.registry.npmjs.org/write-json-file/2.2.0')} - ` + '\n') + `) t.end() }) @@ -165,7 +171,7 @@ test('parseable list with depth 1 and dev only', async t => { t.equal(await list(fixture, { parseable: true, depth: 1, only: 'dev' }), stripIndent` ${fixture} ${path.join(fixture, 'node_modules/.registry.npmjs.org/is-positive/3.1.0')} - ` + '\n') + `) t.end() }) @@ -182,7 +188,7 @@ test('long parseable list with depth 1', async t => { ${path.join(fixture, 'node_modules/.registry.npmjs.org/sort-keys/1.1.2')}:sort-keys@1.1.2 ${path.join(fixture, 'node_modules/.registry.npmjs.org/write-file-atomic/2.1.0')}:write-file-atomic@2.1.0 ${path.join(fixture, 'node_modules/.registry.npmjs.org/write-json-file/2.2.0')}:write-json-file@2.2.0 - ` + '\n') + `) t.end() }) @@ -199,7 +205,7 @@ test('long parseable list with depth 1 when package has no version', async t => ${path.join(fixtureWithNoPkgVersion, 'node_modules/.registry.npmjs.org/sort-keys/1.1.2')}:sort-keys@1.1.2 ${path.join(fixtureWithNoPkgVersion, 'node_modules/.registry.npmjs.org/write-file-atomic/2.1.0')}:write-file-atomic@2.1.0 ${path.join(fixtureWithNoPkgVersion, 'node_modules/.registry.npmjs.org/write-json-file/2.2.0')}:write-json-file@2.2.0 - ` + '\n') + `) t.end() }) @@ -216,13 +222,13 @@ test('long parseable list with depth 1 when package has no name and no version', ${path.join(fixtureWithNoPkgNameAndNoVersion, 'node_modules/.registry.npmjs.org/sort-keys/1.1.2')}:sort-keys@1.1.2 ${path.join(fixtureWithNoPkgNameAndNoVersion, 'node_modules/.registry.npmjs.org/write-file-atomic/2.1.0')}:write-file-atomic@2.1.0 ${path.join(fixtureWithNoPkgNameAndNoVersion, 'node_modules/.registry.npmjs.org/write-json-file/2.2.0')}:write-json-file@2.2.0 - ` + '\n') + `) t.end() }) test('print empty', async t => { - t.equal(await list(emptyFixture), `empty@1.0.0 ${emptyFixture}\n`) + t.equal(await list(emptyFixture), `empty@1.0.0 ${emptyFixture}`) t.end() }) @@ -255,6 +261,6 @@ test('unsaved dependencies are marked', async (t) => { ), stripIndent` fixture@1.0.0 ${fixture} └── foo@1.0.0 ${chalk.whiteBright.bgBlack('not saved')} - ` + '\n') + `) t.end() }) diff --git a/packages/pnpm/src/cmd/help.ts b/packages/pnpm/src/cmd/help.ts index ed15abce59..ac57bd834f 100644 --- a/packages/pnpm/src/cmd/help.ts +++ b/packages/pnpm/src/cmd/help.ts @@ -171,6 +171,9 @@ function getHelpText (command: string) { --parseable show parseable output instead of tree view -g, --global list packages in the global install prefix instead of in the current project --depth max display depth of the dependency tree + --depth 0 display only direct dependencies + --depth -1 display only projects. Useful in a monorepo. + \`pnpm recursive ls --depth -1\` lists all projects in a monorepo. --prod, --production display only the dependency tree for packages in \`dependencies\`. --dev display only the dependency tree for packages in \`devDependencies\`. ` diff --git a/packages/pnpm/src/cmd/list.ts b/packages/pnpm/src/cmd/list.ts index b5d06ccb58..4228950897 100644 --- a/packages/pnpm/src/cmd/list.ts +++ b/packages/pnpm/src/cmd/list.ts @@ -13,6 +13,25 @@ export default async function ( shrinkwrapDirectory?: string, }, command: string, +) { + const output = await render(args, opts, command) + + if (output) console.log(output) +} + +export async function render ( + args: string[], + opts: { + alwaysPrintRootPackage?: boolean, + depth?: number, + development: boolean, + long?: boolean, + parseable?: boolean, + prefix: string, + production: boolean, + shrinkwrapDirectory?: string, + }, + command: string, ) { opts.long = opts.long || command === 'll' || command === 'la' const only = (opts.production && opts.development ? undefined : (opts.production ? 'prod' : 'dev')) as ('prod' | 'dev' | undefined) // tslint:disable-line:no-unnecessary-type-assertion @@ -24,9 +43,7 @@ export default async function ( parseable: opts.parseable, shrinkwrapDirectory: opts.shrinkwrapDirectory, } - const output = args.length - ? await listForPackages(args, opts.prefix, listOpts) - : await list(opts.prefix, listOpts) - - if (output) console.log(output) + return args.length + ? listForPackages(args, opts.prefix, listOpts) + : list(opts.prefix, listOpts) } diff --git a/packages/pnpm/src/cmd/recursive/list.ts b/packages/pnpm/src/cmd/recursive/list.ts index 9021d72322..cc283f6278 100644 --- a/packages/pnpm/src/cmd/recursive/list.ts +++ b/packages/pnpm/src/cmd/recursive/list.ts @@ -1,6 +1,6 @@ import logger from '@pnpm/logger' import { PackageJson } from '@pnpm/types' -import list from '../list' +import { render as renderList } from '../list' export default async ( pkgs: Array<{path: string, manifest: PackageJson}>, @@ -15,13 +15,21 @@ export default async ( shrinkwrapDirectory?: string, }, ) => { + const outputs = [] for (const pkg of pkgs) { try { - await list(args, { ...opts, prefix: pkg.path, alwaysPrintRootPackage: false }, cmd) + const output = await renderList(args, { ...opts, prefix: pkg.path, alwaysPrintRootPackage: opts.depth === -1 }, cmd) + if (!output) continue + outputs.push(output) } catch (err) { logger.info(err) err['prefix'] = pkg.path // tslint:disable-line:no-string-literal throw err } } + if (outputs.length === 0) return + + const joiner = opts.depth && opts.depth > -1 ? '\n\n' : '\n' + const allOutput = outputs.join(joiner) + console.log(allOutput) } diff --git a/packages/pnpm/test/list.ts b/packages/pnpm/test/list.ts index 7390da5ef8..e8c7960387 100644 --- a/packages/pnpm/test/list.ts +++ b/packages/pnpm/test/list.ts @@ -34,7 +34,7 @@ test('listing global packages', async (t: tape.Test) => { t.equal(result.stdout.toString(), stripIndent` ${globalPrefix} └── is-positive@3.1.0 - ` + '\n\n') + ` + '\n') }) test('listing global packages installed with independent-leaves = true', async (t: tape.Test) => { @@ -57,7 +57,7 @@ test('listing global packages installed with independent-leaves = true', async ( t.equal(result.stdout.toString(), stripIndent` ${globalPrefix} └── is-positive@3.1.0 - ` + '\n\n') + ` + '\n') }) test('listing packages', async (t: tape.Test) => { @@ -80,7 +80,7 @@ test('listing packages', async (t: tape.Test) => { t.equal(result.stdout.toString(), stripIndent` project@0.0.0 ${process.cwd()} └── is-positive@1.0.0 - ` + '\n\n', 'prints prod deps only') + ` + '\n', 'prints prod deps only') } { @@ -91,7 +91,7 @@ test('listing packages', async (t: tape.Test) => { t.equal(result.stdout.toString(), stripIndent` project@0.0.0 ${process.cwd()} └── is-positive@1.0.0 - ` + '\n\n', 'prints prod deps only using --only prod') + ` + '\n', 'prints prod deps only using --only prod') } { @@ -102,7 +102,7 @@ test('listing packages', async (t: tape.Test) => { t.equal(result.stdout.toString(), stripIndent` project@0.0.0 ${process.cwd()} └── is-negative@1.0.0 - ` + '\n\n', 'prints dev deps only') + ` + '\n', 'prints dev deps only') } { @@ -113,7 +113,7 @@ test('listing packages', async (t: tape.Test) => { t.equal(result.stdout.toString(), stripIndent` project@0.0.0 ${process.cwd()} └── is-negative@1.0.0 - ` + '\n\n', 'prints dev deps only using --only dev') + ` + '\n', 'prints dev deps only using --only dev') } { @@ -125,7 +125,7 @@ test('listing packages', async (t: tape.Test) => { project@0.0.0 ${process.cwd()} ├── is-negative@1.0.0 └── is-positive@1.0.0 - ` + '\n\n', 'prints all deps') + ` + '\n', 'prints all deps') } }) @@ -155,5 +155,5 @@ test('listing packages of a project that has an external shrinkwrap.yaml', async t.equal(result.stdout.toString(), stripIndent` pkg@1.0.0 ${process.cwd()} └── is-positive@1.0.0 - ` + '\n\n', 'prints all deps') + ` + '\n', 'prints all deps') }) diff --git a/packages/pnpm/test/recursive/list.ts b/packages/pnpm/test/recursive/list.ts index ad67698789..b40b04f087 100644 --- a/packages/pnpm/test/recursive/list.ts +++ b/packages/pnpm/test/recursive/list.ts @@ -49,7 +49,7 @@ test('recursive list', async (t: tape.Test) => { project-2@1.0.0 ${path.resolve('project-2')} └── is-negative@1.0.0 - ` + '\n\n') + ` + '\n') }) test('recursive list with shared-workspace-shrinkwrap', async (t: tape.Test) => { @@ -92,7 +92,7 @@ test('recursive list with shared-workspace-shrinkwrap', async (t: tape.Test) => project-2@1.0.0 ${path.resolve('project-2')} └── is-negative@1.0.0 - ` + '\n\n') + ` + '\n') }) test('recursive list --scope', async (t: tape.Test) => { @@ -138,5 +138,5 @@ test('recursive list --scope', async (t: tape.Test) => { project-2@1.0.0 ${path.resolve('project-2')} └── is-negative@1.0.0 - ` + '\n\n') + ` + '\n') })