fix(dlx): dlx should work when pkg name is not same as cmd name (#4682)

close #4672
This commit is contained in:
Zoltan Kochan
2022-05-06 14:42:23 +03:00
committed by GitHub
parent 0eefba9bee
commit 8ef4db94c1
29 changed files with 398 additions and 159 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-script-runners": patch
"pnpm": patch
---
`pnpm dlx` should work when the bin name of the executed package isn't the same as the package name [#4672](https://github.com/pnpm/pnpm/issues/4672).

View File

@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-installation": patch
---
Export AddCommandOptions.

View File

@@ -37,7 +37,7 @@
"@commitlint/prompt-cli": "^16.0.0",
"@pnpm/eslint-config": "workspace:*",
"@pnpm/meta-updater": "0.0.6",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/tsconfig": "workspace:*",
"@types/jest": "^27.4.0",
"@types/node": "^14.17.32",

View File

@@ -73,7 +73,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/package-store": "workspace:13.0.2",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/store-path": "workspace:6.0.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/fs-extra": "^9.0.5",

View File

@@ -22,7 +22,7 @@
"@pnpm/package-store": "workspace:13.0.2",
"@pnpm/prepare": "workspace:*",
"@pnpm/read-projects-context": "workspace:6.0.2",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/store-path": "workspace:6.0.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/fs-extra": "^9.0.5",

View File

@@ -62,10 +62,10 @@
},
"devDependencies": {
"@pnpm/client": "workspace:7.1.1",
"@pnpm/create-cafs-store": "workspace:1.0.0",
"@pnpm/logger": "^4.0.0",
"@pnpm/package-requester": "workspace:18.0.2",
"@pnpm/create-cafs-store": "workspace:1.0.0",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/normalize-path": "^3.0.0",
"@types/ramda": "0.27.39",

View File

@@ -39,7 +39,7 @@
"@pnpm/modules-yaml": "workspace:10.0.1",
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/is-ci": "^3.0.0",
"@types/proxyquire": "^1.3.28",

View File

@@ -161,15 +161,17 @@ For options that may be used with `-r`, see "pnpm help recursive"',
})
}
export type AddCommandOptions = InstallCommandOptions & {
allowNew?: boolean
ignoreWorkspaceRootCheck?: boolean
save?: boolean
update?: boolean
useBetaCli?: boolean
workspaceRoot?: boolean
}
export async function handler (
opts: InstallCommandOptions & {
allowNew?: boolean
ignoreWorkspaceRootCheck?: boolean
save?: boolean
update?: boolean
useBetaCli?: boolean
workspaceRoot?: boolean
},
opts: AddCommandOptions,
params: string[]
) {
if (opts.cliOptions['save'] === false) {

View File

@@ -38,7 +38,7 @@
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/plugin-commands-listing": "workspace:5.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/ramda": "0.27.39",
"execa": "npm:safe-execa@^0.1.1",
"strip-ansi": "^6.0.0",

View File

@@ -38,7 +38,7 @@
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/plugin-commands-outdated": "workspace:6.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/lru-cache": "^5.1.0",
"@types/ramda": "0.27.39",
"@types/wrap-ansi": "^3.0.0",

View File

@@ -39,7 +39,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/plugin-commands-publishing": "workspace:5.0.4",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/cross-spawn": "^6.0.2",
"@types/is-ci": "^3.0.0",
"@types/is-windows": "^1.0.0",

View File

@@ -37,7 +37,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/plugin-commands-rebuild": "workspace:6.1.2",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/test-fixtures": "workspace:*",
"@types/ramda": "0.27.39",
"@types/semver": "^7.3.4",

View File

@@ -38,7 +38,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/plugin-commands-script-runners": "workspace:5.0.3",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/is-windows": "^1.0.0",
"@types/ramda": "0.27.39",
"is-windows": "^1.0.2",
@@ -51,6 +51,9 @@
"@pnpm/config": "workspace:15.1.1",
"@pnpm/error": "workspace:3.0.1",
"@pnpm/lifecycle": "workspace:13.0.2",
"@pnpm/package-bins": "workspace:6.0.1",
"@pnpm/plugin-commands-installation": "workspace:10.0.3",
"@pnpm/read-package-json": "workspace:6.0.2",
"@pnpm/read-project-manifest": "workspace:3.0.2",
"@pnpm/sort-packages": "workspace:3.0.2",
"@pnpm/store-path": "workspace:6.0.0",

View File

@@ -5,7 +5,7 @@ import * as dlx from './dlx'
export const commandNames = ['create']
export async function handler (_opts: Record<string, never>, params: string[]) {
export async function handler (_opts: dlx.DlxCommandOptions, params: string[]) {
const [packageName, ...packageArgs] = params
if (packageName === undefined) {
throw new PnpmError(
@@ -17,7 +17,7 @@ export async function handler (_opts: Record<string, never>, params: string[]) {
}
const createPackageName = convertToCreateName(packageName)
return dlx.handler({}, [createPackageName, ...packageArgs])
return dlx.handler(_opts, [createPackageName, ...packageArgs])
}
export function rcOptionsTypes () {

View File

@@ -1,10 +1,13 @@
import fs from 'fs'
import os from 'os'
import path from 'path'
import { docsUrl } from '@pnpm/cli-utils'
import { OUTPUT_OPTIONS } from '@pnpm/common-cli-options-help'
import { Config } from '@pnpm/config'
import rimraf from '@zkochan/rimraf'
import PnpmError from '@pnpm/error'
import { add } from '@pnpm/plugin-commands-installation'
import { fromDir as readPkgFromDir } from '@pnpm/read-package-json'
import packageBins from '@pnpm/package-bins'
import storePath from '@pnpm/store-path'
import execa from 'execa'
import renderHelp from 'render-help'
import { makeEnv } from './makeEnv'
@@ -40,16 +43,22 @@ export function help () {
})
}
export type DlxCommandOptions = {
package?: string[]
} & Pick<Config, 'reporter' | 'userAgent'> & add.AddCommandOptions
export async function handler (
opts: {
package?: string[]
} & Pick<Config, 'reporter' | 'userAgent'>,
params: string[]
opts: DlxCommandOptions,
[command, ...args]: string[]
) {
const prefix = path.join(fs.realpathSync(os.tmpdir()), `dlx-${process.pid.toString()}`)
const bins = process.platform === 'win32'
? prefix
: path.join(prefix, 'bin')
const dlxDir = await getDlxDir({
dir: opts.dir,
pnpmHomeDir: opts.pnpmHomeDir,
storeDir: opts.storeDir,
})
const prefix = path.join(dlxDir, `dlx-${process.pid.toString()}`)
const modulesDir = path.join(prefix, 'node_modules')
const binsDir = path.join(modulesDir, '.bin')
fs.mkdirSync(prefix, { recursive: true })
process.on('exit', () => {
try {
@@ -59,28 +68,36 @@ export async function handler (
})
} catch (err) {}
})
await rimraf(bins)
const pkgs = opts.package ?? params.slice(0, 1)
const pnpmArgs = [
'add',
...pkgs,
'--global',
`--global-dir=${prefix}`,
`--dir=${prefix}`,
`--config.global-bin-dir=${bins}`,
]
if (opts.reporter) {
pnpmArgs.push(`--reporter=${opts.reporter}`)
const pkgs = opts.package ?? [command]
const env = makeEnv({ userAgent: opts.userAgent, prependPaths: [binsDir] })
await add.handler({
...opts,
dir: prefix,
bin: binsDir,
}, pkgs)
const binName = opts.package
? command
: await getBinName(modulesDir, versionless(command))
await execa(binName, args, {
env,
stdio: 'inherit',
})
}
async function getBinName (modulesDir: string, pkgName: string): Promise<string> {
const pkgDir = path.join(modulesDir, pkgName)
const manifest = await readPkgFromDir(pkgDir)
const bins = await packageBins(manifest, pkgDir)
if (bins.length === 0) {
throw new PnpmError('DLX_NO_BIN', `No binaries found in ${pkgName}`)
}
const env = makeEnv({ userAgent: opts.userAgent, prependPaths: [bins] })
await execa('pnpm', pnpmArgs, {
env,
stdio: 'inherit',
})
await execa(versionless(scopeless(params[0])), params.slice(1), {
env,
stdio: 'inherit',
})
if (bins.length === 1) {
return bins[0].name
}
const scopelessPkgName = scopeless(manifest.name)
const defaultBin = bins.find(({ name }) => name === scopelessPkgName)
if (defaultBin) return defaultBin.name
throw new PnpmError('DLX_MULTIPLE_BINS', `Multiple binaries found in ${pkgName}`)
}
function scopeless (pkgName: string) {
@@ -90,6 +107,23 @@ function scopeless (pkgName: string) {
return pkgName
}
function versionless (scopelessPkgName: string) {
return scopelessPkgName.split('@')[0]
function versionless (pkgName: string) {
const index = pkgName.indexOf('@', 1)
if (index === -1) return pkgName
return pkgName.substring(0, index)
}
async function getDlxDir (
opts: {
dir: string
storeDir?: string
pnpmHomeDir: string
}
): Promise<string> {
const storeDir = await storePath({
pkgRoot: opts.dir,
storePath: opts.storeDir,
pnpmHomeDir: opts.pnpmHomeDir,
})
return path.join(storeDir, 'tmp')
}

View File

@@ -1,65 +1,99 @@
import PnpmError from '@pnpm/error'
import { create, dlx } from '../src'
import { DLX_DEFAULT_OPTS as DEFAULT_OPTS } from './utils'
jest.mock('../src/dlx', () => ({ handler: jest.fn() }))
beforeEach((dlx.handler as jest.Mock).mockClear)
it('throws an error if called without arguments', async () => {
await expect(create.handler({}, [])).rejects.toThrow(PnpmError)
await expect(create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, [])).rejects.toThrow(PnpmError)
expect(dlx.handler).not.toBeCalled()
})
it(
'appends `create-` to an unscoped package that doesn\'t start with `create-`',
async () => {
await create.handler({}, ['some-app'])
expect(dlx.handler).toBeCalledWith({}, ['create-some-app'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['some-app'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['create-some-app'])
await create.handler({}, ['create_no_dash'])
expect(dlx.handler).toBeCalledWith({}, ['create-create_no_dash'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['create_no_dash'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['create-create_no_dash'])
}
)
it(
'does not append `create-` to an unscoped package that starts with `create-`',
async () => {
await create.handler({}, ['create-some-app'])
expect(dlx.handler).toBeCalledWith({}, ['create-some-app'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['create-some-app'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['create-some-app'])
await create.handler({}, ['create-'])
expect(dlx.handler).toBeCalledWith({}, ['create-'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['create-'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['create-'])
}
)
it(
'appends `create-` to a scoped package that doesn\'t start with `create-`',
async () => {
await create.handler({}, ['@scope/some-app'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-some-app'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['@scope/some-app'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['@scope/create-some-app'])
await create.handler({}, ['@scope/create_no_dash'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-create_no_dash'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['@scope/create_no_dash'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['@scope/create-create_no_dash'])
}
)
it(
'does not append `create-` to a scoped package that starts with `create-`',
async () => {
await create.handler({}, ['@scope/create-some-app'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-some-app'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['@scope/create-some-app'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['@scope/create-some-app'])
await create.handler({}, ['@scope/create-'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create-'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['@scope/create-'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['@scope/create-'])
}
)
it('infers a package name from a plain scope', async () => {
await create.handler({}, ['@scope'])
expect(dlx.handler).toBeCalledWith({}, ['@scope/create'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['@scope'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['@scope/create'])
})
it('passes the remaining arguments to `dlx`', async () => {
await create.handler({}, ['some-app', 'directory/', '--silent'])
expect(dlx.handler).toBeCalledWith({}, ['create-some-app', 'directory/', '--silent'])
await create.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['some-app', 'directory/', '--silent'])
expect(dlx.handler).toBeCalledWith(expect.anything(), ['create-some-app', 'directory/', '--silent'])
})

View File

@@ -1,19 +1,58 @@
import fs from 'fs'
import { dlx } from '@pnpm/plugin-commands-script-runners'
import { prepareEmpty } from '@pnpm/prepare'
import { DLX_DEFAULT_OPTS as DEFAULT_OPTS } from './utils'
test('dlx', async () => {
prepareEmpty()
await dlx.handler({}, ['shx', 'touch', 'foo'])
await dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['shx', 'touch', 'foo'])
expect(fs.existsSync('foo')).toBeTruthy()
})
test('dlx should work when the package name differs from the bin name', async () => {
prepareEmpty()
await dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['touch-file-one-bin'])
expect(fs.existsSync('touch.txt')).toBeTruthy()
})
test('dlx should fail when the installed package has many commands and none equals the package name', async () => {
prepareEmpty()
await expect(
dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['touch-file-many-bins'])
).rejects.toThrow(/Multiple binaries found in touch-file-many-bins/)
})
test('dlx should not fail when the installed package has many commands and one equals the package name', async () => {
prepareEmpty()
await dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['touch-file-good-bin-name'])
expect(fs.existsSync('touch.txt')).toBeTruthy()
})
test('dlx --package <pkg1> [--package <pkg2>]', async () => {
prepareEmpty()
await dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
package: [
'zkochan/for-testing-pnpm-dlx',
'is-positive',
@@ -22,3 +61,14 @@ test('dlx --package <pkg1> [--package <pkg2>]', async () => {
expect(fs.existsSync('foo')).toBeTruthy()
})
test('dlx should fail when the package has no bins', async () => {
prepareEmpty()
await expect(
dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['is-positive'])
).rejects.toThrow(/No binaries found in is-positive/)
})

View File

@@ -1,6 +1,7 @@
import execa from 'execa'
import { dlx } from '@pnpm/plugin-commands-script-runners'
import { prepareEmpty } from '@pnpm/prepare'
import { DLX_DEFAULT_OPTS as DEFAULT_OPTS } from './utils'
jest.mock('execa')
@@ -10,9 +11,13 @@ test('dlx should work with scoped packages', async () => {
prepareEmpty()
const userAgent = 'pnpm/0.0.0'
await dlx.handler({ userAgent }, ['@foo/bar'])
await dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
userAgent,
}, ['@foo/touch-file-one-bin'])
expect(execa).toBeCalledWith('bar', [], expect.objectContaining({
expect(execa).toBeCalledWith('touch-file-one-bin', [], expect.objectContaining({
env: expect.objectContaining({
npm_config_user_agent: userAgent,
}),
@@ -22,12 +27,10 @@ test('dlx should work with scoped packages', async () => {
test('dlx should work with versioned packages', async () => {
prepareEmpty()
await dlx.handler({}, ['@foo/bar@next'])
await dlx.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
}, ['@foo/touch-file-one-bin@latest'])
expect(execa).toBeCalledWith(
'pnpm',
expect.arrayContaining(['add', '@foo/bar@next']),
expect.anything()
)
expect(execa).toBeCalledWith('bar', [], expect.anything())
expect(execa).toBeCalledWith('touch-file-one-bin', [], expect.anything())
})

View File

@@ -6,7 +6,7 @@ import { exec, run } from '@pnpm/plugin-commands-script-runners'
import prepare, { prepareEmpty, preparePackages } from '@pnpm/prepare'
import rimraf from '@zkochan/rimraf'
import execa from 'execa'
import { DEFAULT_OPTS, REGISTRY } from './utils'
import { DEFAULT_OPTS, REGISTRY_URL } from './utils'
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.cjs')
@@ -56,7 +56,7 @@ test('pnpm recursive exec', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -99,7 +99,7 @@ test('pnpm recursive exec finds bin files of workspace projects', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -158,7 +158,7 @@ test('exec inside a workspace package', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -239,7 +239,7 @@ test('testing the bail config with "pnpm recursive exec"', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -315,7 +315,7 @@ test('pnpm recursive exec --no-sort', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -377,7 +377,7 @@ test('pnpm recursive exec --reverse', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -528,7 +528,7 @@ test('pnpm recursive exec works with PnP', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
], {

View File

@@ -9,11 +9,10 @@ import {
test as testCommand,
} from '@pnpm/plugin-commands-script-runners'
import prepare, { preparePackages } from '@pnpm/prepare'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import execa from 'execa'
import isWindows from 'is-windows'
import writeYamlFile from 'write-yaml-file'
import { DEFAULT_OPTS, REGISTRY } from './utils'
import { DEFAULT_OPTS, REGISTRY_URL } from './utils'
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.cjs')
@@ -378,7 +377,7 @@ test('if a script is not found but is present in the root, print an info message
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -436,7 +435,7 @@ test('pnpm run with custom shell', async () => {
await execa(pnpmBin, [
'install',
`--registry=http://localhost:${REGISTRY_MOCK_PORT}`,
`--registry=${REGISTRY_URL}`,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])

View File

@@ -6,7 +6,7 @@ import PnpmError from '@pnpm/error'
import rimraf from '@zkochan/rimraf'
import execa from 'execa'
import writeYamlFile from 'write-yaml-file'
import { DEFAULT_OPTS, REGISTRY } from './utils'
import { DEFAULT_OPTS, REGISTRY_URL } from './utils'
const pnpmBin = path.join(__dirname, '../../pnpm/bin/pnpm.cjs')
@@ -62,7 +62,7 @@ test('pnpm recursive run', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -134,7 +134,7 @@ test('pnpm recursive run with enable-pre-post-scripts', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -207,7 +207,7 @@ test('pnpm recursive run reversed', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -259,7 +259,7 @@ test('pnpm recursive run concurrently', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -311,7 +311,7 @@ test('`pnpm recursive run` fails when run without filters and no package has the
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -429,7 +429,7 @@ test('`pnpm recursive run` succeeds when run against a subset of packages and no
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -486,7 +486,7 @@ test('"pnpm run --filter <pkg>" without specifying the script name', async () =>
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -588,7 +588,7 @@ test('testing the bail config with "pnpm recursive run"', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -669,7 +669,7 @@ test('pnpm recursive run with filtering', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -706,7 +706,7 @@ test('`pnpm recursive run` should always trust the scripts', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -765,7 +765,7 @@ test('`pnpm run -r` should avoid infinite recursion', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])

View File

@@ -3,7 +3,7 @@ import { filterPkgsBySelectorObjects, readProjects } from '@pnpm/filter-workspac
import { test as testCommand } from '@pnpm/plugin-commands-script-runners'
import { preparePackages } from '@pnpm/prepare'
import execa from 'execa'
import { DEFAULT_OPTS, REGISTRY } from './utils'
import { DEFAULT_OPTS, REGISTRY_URL } from './utils'
test('pnpm recursive test', async () => {
preparePackages([
@@ -55,7 +55,7 @@ test('pnpm recursive test', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -110,7 +110,7 @@ test('`pnpm recursive test` does not fail if none of the packages has a test com
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])
@@ -162,7 +162,7 @@ test('pnpm recursive test with filtering', async () => {
'install',
'-r',
'--registry',
REGISTRY,
REGISTRY_URL,
'--store-dir',
path.resolve(DEFAULT_OPTS.storeDir),
])

View File

@@ -1,4 +1,9 @@
export const REGISTRY = 'https://registry.npmjs.org/'
import path from 'path'
import { tempDir } from '@pnpm/prepare'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
export const REGISTRY_URL = `http://localhost:${REGISTRY_MOCK_PORT}`
const tmp = tempDir()
export const DEFAULT_OPTS = {
alwaysAuth: false,
@@ -33,10 +38,10 @@ export const DEFAULT_OPTS = {
pnpmfile: './.pnpmfile.cjs',
pnpmHomeDir: '',
proxy: undefined,
rawConfig: { registry: REGISTRY },
rawConfig: { registry: REGISTRY_URL },
rawLocalConfig: {},
registries: { default: REGISTRY },
registry: REGISTRY,
registries: { default: REGISTRY_URL },
registry: REGISTRY_URL,
sort: true,
storeDir: '../store',
strictSsl: false,
@@ -45,3 +50,31 @@ export const DEFAULT_OPTS = {
useStoreServer: false,
workspaceConcurrency: 4,
}
export const DLX_DEFAULT_OPTS = {
argv: {
original: [],
},
bail: false,
bin: 'node_modules/.bin',
cacheDir: path.join(tmp, 'cache'),
cliOptions: {},
include: {
dependencies: true,
devDependencies: true,
optionalDependencies: true,
},
linkWorkspacePackages: true,
lock: true,
pnpmfile: '.pnpmfile.cjs',
pnpmHomeDir: '',
rawConfig: { registry: REGISTRY_URL },
rawLocalConfig: { registry: REGISTRY_URL },
registries: {
default: REGISTRY_URL,
},
sort: true,
storeDir: path.join(tmp, 'store'),
userConfig: {},
workspaceConcurrency: 1,
}

View File

@@ -33,6 +33,15 @@
{
"path": "../lifecycle"
},
{
"path": "../package-bins"
},
{
"path": "../plugin-commands-installation"
},
{
"path": "../read-package-json"
},
{
"path": "../read-project-manifest"
},

View File

@@ -38,7 +38,7 @@
"@pnpm/logger": "^4.0.0",
"@pnpm/plugin-commands-store": "workspace:5.1.2",
"@pnpm/prepare": "workspace:*",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@types/archy": "0.0.31",
"@types/ramda": "0.27.39",
"@types/ssri": "^7.1.0",

View File

@@ -55,7 +55,7 @@
"@pnpm/prepare": "workspace:*",
"@pnpm/read-package-json": "workspace:6.0.2",
"@pnpm/read-project-manifest": "workspace:3.0.2",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/run-npm": "workspace:4.0.1",
"@pnpm/tabtab": "^0.1.2",
"@pnpm/ts-execution-runtime": "workspace:*",

137
pnpm-lock.yaml generated
View File

@@ -41,7 +41,7 @@ importers:
'@commitlint/prompt-cli': ^16.0.0
'@pnpm/eslint-config': workspace:*
'@pnpm/meta-updater': 0.0.6
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/tsconfig': workspace:*
'@types/jest': ^27.4.0
'@types/node': ^14.17.32
@@ -72,7 +72,7 @@ importers:
'@commitlint/prompt-cli': 16.2.4
'@pnpm/eslint-config': link:utils/eslint-config
'@pnpm/meta-updater': 0.0.6
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/tsconfig': link:utils/tsconfig
'@types/jest': 27.4.1
'@types/node': 14.18.16
@@ -422,7 +422,7 @@ importers:
'@pnpm/read-modules-dir': workspace:4.0.0
'@pnpm/read-package-json': workspace:6.0.2
'@pnpm/read-project-manifest': workspace:3.0.2
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/remove-bins': workspace:3.0.2
'@pnpm/resolve-dependencies': workspace:27.0.2
'@pnpm/resolver-base': workspace:9.0.1
@@ -524,7 +524,7 @@ importers:
'@pnpm/logger': 4.0.0
'@pnpm/package-store': link:../package-store
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/store-path': link:../store-path
'@pnpm/test-fixtures': link:../../privatePackages/test-fixtures
'@types/fs-extra': 9.0.13
@@ -1107,7 +1107,7 @@ importers:
'@pnpm/read-project-manifest': workspace:3.0.2
'@pnpm/read-projects-context': workspace:6.0.2
'@pnpm/real-hoist': workspace:0.2.2
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/store-controller-types': workspace:13.0.1
'@pnpm/store-path': workspace:6.0.0
'@pnpm/symlink-dependency': workspace:5.0.1
@@ -1168,7 +1168,7 @@ importers:
'@pnpm/package-store': link:../package-store
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/read-projects-context': link:../read-projects-context
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/store-path': link:../store-path
'@pnpm/test-fixtures': link:../../privatePackages/test-fixtures
'@types/fs-extra': 9.0.13
@@ -1859,7 +1859,7 @@ importers:
'@pnpm/package-is-installable': workspace:6.0.2
'@pnpm/package-requester': workspace:18.0.2
'@pnpm/read-package-json': workspace:6.0.2
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/resolver-base': workspace:9.0.1
'@pnpm/store-controller-types': workspace:13.0.1
'@pnpm/test-fixtures': workspace:*
@@ -1910,7 +1910,7 @@ importers:
'@pnpm/create-cafs-store': link:../create-cafs-store
'@pnpm/logger': 4.0.0
'@pnpm/package-requester': 'link:'
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/test-fixtures': link:../../privatePackages/test-fixtures
'@types/normalize-path': 3.0.0
'@types/ramda': 0.27.39
@@ -2182,7 +2182,7 @@ importers:
'@pnpm/pnpmfile': workspace:2.0.2
'@pnpm/prepare': workspace:*
'@pnpm/read-project-manifest': workspace:3.0.2
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/resolver-base': workspace:9.0.1
'@pnpm/semver-diff': ^1.0.2
'@pnpm/sort-packages': workspace:3.0.2
@@ -2279,7 +2279,7 @@ importers:
'@pnpm/modules-yaml': link:../modules-yaml
'@pnpm/plugin-commands-installation': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/test-fixtures': link:../../privatePackages/test-fixtures
'@types/is-ci': 3.0.0
'@types/proxyquire': 1.3.28
@@ -2310,7 +2310,7 @@ importers:
'@pnpm/plugin-commands-installation': workspace:10.0.3
'@pnpm/plugin-commands-listing': workspace:5.0.3
'@pnpm/prepare': workspace:*
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/types': workspace:8.0.1
'@types/ramda': 0.27.39
execa: npm:safe-execa@^0.1.1
@@ -2334,7 +2334,7 @@ importers:
'@pnpm/plugin-commands-installation': link:../plugin-commands-installation
'@pnpm/plugin-commands-listing': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@types/ramda': 0.27.39
execa: /safe-execa/0.1.1
strip-ansi: 6.0.1
@@ -2358,7 +2358,7 @@ importers:
'@pnpm/plugin-commands-installation': workspace:10.0.3
'@pnpm/plugin-commands-outdated': workspace:6.0.3
'@pnpm/prepare': workspace:*
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/semver-diff': ^1.0.2
'@pnpm/store-path': workspace:6.0.0
'@pnpm/types': workspace:8.0.1
@@ -2401,7 +2401,7 @@ importers:
'@pnpm/plugin-commands-installation': link:../plugin-commands-installation
'@pnpm/plugin-commands-outdated': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@types/lru-cache': 5.1.1
'@types/ramda': 0.27.39
'@types/wrap-ansi': 3.0.0
@@ -2421,7 +2421,7 @@ importers:
'@pnpm/pick-registry-for-package': workspace:3.0.1
'@pnpm/plugin-commands-publishing': workspace:5.0.4
'@pnpm/prepare': workspace:*
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/resolver-base': workspace:9.0.1
'@pnpm/run-npm': workspace:4.0.1
'@pnpm/sort-packages': workspace:3.0.2
@@ -2484,7 +2484,7 @@ importers:
'@pnpm/logger': 4.0.0
'@pnpm/plugin-commands-publishing': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@types/cross-spawn': 6.0.2
'@types/is-ci': 3.0.0
'@types/is-windows': 1.0.0
@@ -2522,7 +2522,7 @@ importers:
'@pnpm/normalize-registries': workspace:3.0.1
'@pnpm/plugin-commands-rebuild': workspace:6.1.2
'@pnpm/prepare': workspace:*
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/sort-packages': workspace:3.0.2
'@pnpm/store-connection-manager': workspace:4.1.2
'@pnpm/store-controller-types': workspace:13.0.1
@@ -2581,7 +2581,7 @@ importers:
'@pnpm/logger': 4.0.0
'@pnpm/plugin-commands-rebuild': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/test-fixtures': link:../../privatePackages/test-fixtures
'@types/ramda': 0.27.39
'@types/semver': 7.3.9
@@ -2601,10 +2601,13 @@ importers:
'@pnpm/filter-workspace-packages': workspace:5.0.3
'@pnpm/lifecycle': workspace:13.0.2
'@pnpm/logger': ^4.0.0
'@pnpm/package-bins': workspace:6.0.1
'@pnpm/plugin-commands-installation': workspace:10.0.3
'@pnpm/plugin-commands-script-runners': workspace:5.0.3
'@pnpm/prepare': workspace:*
'@pnpm/read-package-json': workspace:6.0.2
'@pnpm/read-project-manifest': workspace:3.0.2
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/sort-packages': workspace:3.0.2
'@pnpm/store-path': workspace:6.0.0
'@pnpm/types': workspace:8.0.1
@@ -2627,6 +2630,9 @@ importers:
'@pnpm/config': link:../config
'@pnpm/error': link:../error
'@pnpm/lifecycle': link:../lifecycle
'@pnpm/package-bins': link:../package-bins
'@pnpm/plugin-commands-installation': link:../plugin-commands-installation
'@pnpm/read-package-json': link:../read-package-json
'@pnpm/read-project-manifest': link:../read-project-manifest
'@pnpm/sort-packages': link:../sort-packages
'@pnpm/store-path': link:../store-path
@@ -2644,7 +2650,7 @@ importers:
'@pnpm/logger': 4.0.0
'@pnpm/plugin-commands-script-runners': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@types/is-windows': 1.0.0
'@types/ramda': 0.27.39
is-windows: 1.0.2
@@ -2732,7 +2738,7 @@ importers:
'@pnpm/pick-registry-for-package': workspace:3.0.1
'@pnpm/plugin-commands-store': workspace:5.1.2
'@pnpm/prepare': workspace:*
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/store-connection-manager': workspace:4.1.2
'@pnpm/store-controller-types': workspace:13.0.1
'@pnpm/store-path': workspace:6.0.0
@@ -2779,7 +2785,7 @@ importers:
'@pnpm/logger': 4.0.0
'@pnpm/plugin-commands-store': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@types/archy': 0.0.31
'@types/ramda': 0.27.39
'@types/ssri': 7.1.1
@@ -2827,7 +2833,7 @@ importers:
'@pnpm/prepare': workspace:*
'@pnpm/read-package-json': workspace:6.0.2
'@pnpm/read-project-manifest': workspace:3.0.2
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/run-npm': workspace:4.0.1
'@pnpm/tabtab': ^0.1.2
'@pnpm/ts-execution-runtime': workspace:*
@@ -2916,7 +2922,7 @@ importers:
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/read-package-json': link:../read-package-json
'@pnpm/read-project-manifest': link:../read-project-manifest
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/run-npm': link:../run-npm
'@pnpm/tabtab': 0.1.2
'@pnpm/ts-execution-runtime': link:../../utils/ts-execution-runtime
@@ -3515,7 +3521,7 @@ importers:
'@pnpm/constants': workspace:6.1.0
'@pnpm/lockfile-types': workspace:4.0.1
'@pnpm/modules-yaml': workspace:10.0.1
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/types': workspace:8.0.1
'@types/is-windows': ^1.0.0
'@types/isexe': 2.0.0
@@ -3530,7 +3536,7 @@ importers:
'@pnpm/constants': link:../../packages/constants
'@pnpm/lockfile-types': link:../../packages/lockfile-types
'@pnpm/modules-yaml': link:../../packages/modules-yaml
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
'@pnpm/types': link:../../packages/types
is-windows: 1.0.2
isexe: 2.0.0
@@ -3547,11 +3553,11 @@ importers:
specifiers:
'@pnpm/assert-store': workspace:*
'@pnpm/cafs': workspace:4.0.1
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
path-exists: ^4.0.0
dependencies:
'@pnpm/cafs': link:../../packages/cafs
'@pnpm/registry-mock': 2.15.1
'@pnpm/registry-mock': 2.16.0
path-exists: 4.0.0
devDependencies:
'@pnpm/assert-store': 'link:'
@@ -5154,8 +5160,8 @@ packages:
strip-bom: 4.0.0
dev: true
/@pnpm/registry-mock/2.15.1:
resolution: {integrity: sha512-bqIYJ9KBo3kqtCfABZcAyh/fOfAUvpAVhftLmXF3Wh2HJo7AiCqBxa6l+/affJZrkRwO9tl7dH/v32dfY+U5tQ==}
/@pnpm/registry-mock/2.16.0:
resolution: {integrity: sha512-Ro5tsRBR+qTnYne6B9aQJIzsyIJWhSBfARZMpEBmVVUysivYPWjoIRBNZ3EVbtt67oF6tMXTBGViPvYkwQYeyQ==}
engines: {node: '>=10.13'}
hasBin: true
dependencies:
@@ -5165,7 +5171,7 @@ packages:
read-yaml-file: 2.1.0
rimraf: 3.0.2
tempy: 1.0.1
verdaccio: 5.10.0
verdaccio: 5.10.1
write-yaml-file: 4.2.0
transitivePeerDependencies:
- bufferutil
@@ -10656,8 +10662,8 @@ packages:
nwsapi: 2.2.0
parse5: 5.1.0
pn: 1.1.0
request: 2.88.0
request-promise-native: 1.0.9_request@2.88.0
request: 2.88.2
request-promise-native: 1.0.9_request@2.88.2
saxes: 3.1.11
symbol-tree: 3.2.4
tough-cookie: 3.0.1
@@ -13043,24 +13049,24 @@ packages:
engines: {node: '>=8'}
dev: false
/request-promise-core/1.1.4_request@2.88.0:
/request-promise-core/1.1.4_request@2.88.2:
resolution: {integrity: sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==}
engines: {node: '>=0.10.0'}
peerDependencies:
request: ^2.34
dependencies:
lodash: 4.17.21
request: 2.88.0
request: 2.88.2
/request-promise-native/1.0.9_request@2.88.0:
/request-promise-native/1.0.9_request@2.88.2:
resolution: {integrity: sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==}
engines: {node: '>=0.12.0'}
deprecated: request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142
peerDependencies:
request: ^2.34
dependencies:
request: 2.88.0
request-promise-core: 1.1.4_request@2.88.0
request: 2.88.2
request-promise-core: 1.1.4_request@2.88.2
stealthy-require: 1.1.1
tough-cookie: 2.5.0
@@ -14813,6 +14819,61 @@ packages:
- encoding
- supports-color
- utf-8-validate
dev: true
/verdaccio/5.10.1:
resolution: {integrity: sha512-6odO1X1rtm5UD6+u+ngdMg3+lbL/Wn2eUQc/8GyPnk5ycBszB+hiWGZV/T4K+G/mxtt/nU/QKWKPVMa3qNZmeQ==}
engines: {node: '>=12', npm: '>=6'}
hasBin: true
dependencies:
'@verdaccio/commons-api': 10.2.0
'@verdaccio/local-storage': 10.2.1
'@verdaccio/readme': 10.3.3
'@verdaccio/streams': 10.2.0
'@verdaccio/ui-theme': 6.0.0-6-next.24
async: 3.2.3
body-parser: 1.20.0
clipanion: 3.2.0-rc.6
compression: 1.7.4
cookies: 0.8.0
cors: 2.8.5
dayjs: 1.11.1
debug: 4.3.4
envinfo: 7.8.1
eslint-import-resolver-node: 0.3.6
express: 4.17.3
express-rate-limit: 5.5.1
fast-safe-stringify: 2.1.1
handlebars: 4.7.7
http-errors: 1.8.1
js-yaml: /@zkochan/js-yaml/0.0.5
JSONStream: 1.3.5
jsonwebtoken: 8.5.1
kleur: 4.1.4
lodash: 4.17.21
lru-cache: 7.8.1
lunr-mutable-indexes: 2.3.2
marked: 4.0.14
memoizee: 0.4.15
mime: 3.0.0
minimatch: 5.0.1
mkdirp: 1.0.4
mv: 2.1.1
pino: 6.14.0
pkginfo: 0.4.1
prettier-bytes: 1.0.4
pretty-ms: 7.0.1
request: 2.88.0
semver: 7.3.7
validator: 13.7.0
verdaccio-audit: 10.2.1
verdaccio-htpasswd: 10.3.0
transitivePeerDependencies:
- bufferutil
- canvas
- encoding
- supports-color
- utf-8-validate
/verror/1.10.0:
resolution: {integrity: sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=}

View File

@@ -45,7 +45,7 @@
"@pnpm/constants": "workspace:6.1.0",
"@pnpm/lockfile-types": "workspace:4.0.1",
"@pnpm/modules-yaml": "workspace:10.0.1",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"@pnpm/types": "workspace:8.0.1",
"is-windows": "^1.0.2",
"isexe": "2.0.0",

View File

@@ -42,7 +42,7 @@
},
"dependencies": {
"@pnpm/cafs": "workspace:4.0.1",
"@pnpm/registry-mock": "2.15.1",
"@pnpm/registry-mock": "2.16.0",
"path-exists": "^4.0.0"
},
"devDependencies": {