feat: support pnpm recursive import (#3909)

close #3908

Co-authored-by: Zoltan Kochan <z@kochan.io>
This commit is contained in:
foora
2021-11-03 05:40:27 +08:00
committed by GitHub
parent 6b7eb72490
commit 6a54be5a12
47 changed files with 556 additions and 1572 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/plugin-commands-installation": minor
"pnpm": minor
---
Support pnpm recursive import

View File

@@ -0,0 +1,2 @@
!package-lock.json

View File

@@ -0,0 +1,75 @@
{
"name": "workspace-has-shared-npm-shrinkwrap-json",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "workspace-has-shared-npm-shrinkwrap-json",
"version": "1.0.0",
"workspaces": [
"packages/**"
]
},
"node_modules/bar": {
"resolved": "packages/bar",
"link": true
},
"node_modules/foo": {
"resolved": "packages/foo",
"link": true
},
"node_modules/is-negative": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-negative/-/is-negative-1.0.1.tgz",
"integrity": "sha1-3GuHKO69A8db+HYIftzVDpy1aZQ=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-positive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz",
"integrity": "sha1-iACYVrZKLx632LsBeUGEJK4EUss=",
"engines": {
"node": ">=0.10.0"
}
},
"packages/bar": {
"version": "0.0.0",
"dependencies": {
"is-negative": "^1.0.0"
}
},
"packages/foo": {
"version": "0.0.0",
"dependencies": {
"is-positive": "^1.0.0"
}
}
},
"dependencies": {
"bar": {
"version": "file:packages/bar",
"requires": {
"is-negative": "^1.0.0"
}
},
"foo": {
"version": "file:packages/foo",
"requires": {
"is-positive": "^1.0.0"
}
},
"is-negative": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-negative/-/is-negative-1.0.1.tgz",
"integrity": "sha1-3GuHKO69A8db+HYIftzVDpy1aZQ="
},
"is-positive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz",
"integrity": "sha1-iACYVrZKLx632LsBeUGEJK4EUss="
}
}
}

View File

@@ -0,0 +1,8 @@
{
"private": true,
"name": "workspace-has-shared-npm-shrinkwrap-json",
"version": "1.0.0",
"workspaces": [
"packages/**"
]
}

View File

@@ -0,0 +1,7 @@
{
"name": "bar",
"version": "0.0.0",
"dependencies": {
"is-negative": "^1.0.0"
}
}

View File

@@ -0,0 +1,7 @@
{
"name": "foo",
"version": "0.0.0",
"dependencies": {
"is-positive": "^1.0.0"
}
}

View File

@@ -0,0 +1,2 @@
packages:
- 'packages/**'

View File

@@ -0,0 +1,2 @@
!package-lock.json

View File

@@ -0,0 +1,75 @@
{
"name": "workspace-has-shared-package-lock-json",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "workspace-has-shared-package-lock-json",
"version": "1.0.0",
"workspaces": [
"packages/**"
]
},
"node_modules/bar": {
"resolved": "packages/bar",
"link": true
},
"node_modules/foo": {
"resolved": "packages/foo",
"link": true
},
"node_modules/is-negative": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-negative/-/is-negative-1.0.1.tgz",
"integrity": "sha1-3GuHKO69A8db+HYIftzVDpy1aZQ=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/is-positive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz",
"integrity": "sha1-iACYVrZKLx632LsBeUGEJK4EUss=",
"engines": {
"node": ">=0.10.0"
}
},
"packages/bar": {
"version": "0.0.0",
"dependencies": {
"is-negative": "^1.0.0"
}
},
"packages/foo": {
"version": "0.0.0",
"dependencies": {
"is-positive": "^1.0.0"
}
}
},
"dependencies": {
"bar": {
"version": "file:packages/bar",
"requires": {
"is-negative": "^1.0.0"
}
},
"foo": {
"version": "file:packages/foo",
"requires": {
"is-positive": "^1.0.0"
}
},
"is-negative": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-negative/-/is-negative-1.0.1.tgz",
"integrity": "sha1-3GuHKO69A8db+HYIftzVDpy1aZQ="
},
"is-positive": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-positive/-/is-positive-1.0.0.tgz",
"integrity": "sha1-iACYVrZKLx632LsBeUGEJK4EUss="
}
}
}

View File

@@ -0,0 +1,8 @@
{
"private": true,
"name": "workspace-has-shared-package-lock-json",
"version": "1.0.0",
"workspaces": [
"packages/**"
]
}

View File

@@ -0,0 +1,7 @@
{
"name": "bar",
"version": "0.0.0",
"dependencies": {
"is-negative": "^1.0.0"
}
}

View File

@@ -0,0 +1,7 @@
{
"name": "foo",
"version": "0.0.0",
"dependencies": {
"is-positive": "^1.0.0"
}
}

View File

@@ -0,0 +1,2 @@
packages:
- 'packages/**'

View File

@@ -0,0 +1 @@
!yarn.lock

View File

@@ -0,0 +1,8 @@
{
"private": true,
"name": "workspace-has-shared-yarn-lock",
"version": "1.0.0",
"workspaces": [
"packages/**"
]
}

View File

@@ -0,0 +1,7 @@
{
"name": "bar",
"version": "0.0.0",
"dependencies": {
"is-negative": "^1.0.0"
}
}

View File

@@ -0,0 +1,7 @@
{
"name": "foo",
"version": "0.0.0",
"dependencies": {
"is-positive": "^1.0.0"
}
}

View File

@@ -0,0 +1,2 @@
packages:
- 'packages/**'

View File

@@ -0,0 +1,13 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
is-negative@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-negative/-/is-negative-1.0.1.tgz#dc6b8728eebd03c75bf876087edcd50e9cb56994"
integrity sha1-3GuHKO69A8db+HYIftzVDpy1aZQ=
is-positive@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-positive/-/is-positive-1.0.0.tgz#88009856b64a2f1eb7d8bb0179418424ae0452cb"
integrity sha1-iACYVrZKLx632LsBeUGEJK4EUss=

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +0,0 @@
# @pnpm/plugin-commands-import
> The import command
[![npm version](https://img.shields.io/npm/v/@pnpm/plugin-commands-import.svg)](https://www.npmjs.com/package/@pnpm/plugin-commands-import)
## Installation
```sh
<pnpm|npm|yarn> add @pnpm/plugin-commands-import
```
## License
MIT

View File

@@ -1,4 +0,0 @@
const config = require('../../jest.config.js')
module.exports = config

View File

@@ -1,59 +0,0 @@
{
"name": "@pnpm/plugin-commands-import",
"version": "3.1.17",
"description": "The import command",
"main": "lib/index.js",
"typings": "lib/index.d.ts",
"files": [
"lib",
"!*.map"
],
"engines": {
"node": ">=12.17"
},
"scripts": {
"lint": "eslint src/**/*.ts test/**/*.ts",
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7773 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"
},
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/plugin-commands-import",
"keywords": [
"pnpm6",
"pnpm",
"pack",
"publish"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/pnpm/pnpm/issues"
},
"homepage": "https://github.com/pnpm/pnpm/blob/master/packages/plugin-commands-import#readme",
"devDependencies": {
"@pnpm/assert-project": "workspace:*",
"@pnpm/prepare": "workspace:0.0.27",
"@types/ncp": "^2.0.4",
"@types/yarnpkg__lockfile": "^1.1.5",
"@yarnpkg/lockfile": "^1.1.0",
"ncp": "^2.0.0",
"tempy": "^1.0.0"
},
"dependencies": {
"@pnpm/cli-utils": "workspace:0.6.29",
"@pnpm/constants": "workspace:5.0.0",
"@pnpm/error": "workspace:2.0.0",
"@pnpm/graceful-fs": "workspace:1.0.0",
"@pnpm/read-project-manifest": "workspace:2.0.6",
"@pnpm/store-connection-manager": "workspace:3.1.3",
"@zkochan/rimraf": "^2.1.1",
"load-json-file": "^6.2.0",
"path-exists": "^4.0.0",
"render-help": "^1.0.1",
"@pnpm/core": "workspace:1.1.0"
},
"funding": "https://opencollective.com/pnpm"
}

View File

@@ -1,3 +0,0 @@
import * as importCommand from './import'
export { importCommand }

View File

@@ -1,40 +0,0 @@
{
"extends": "@pnpm/tsconfig",
"compilerOptions": {
"outDir": "lib",
"rootDir": "src"
},
"include": [
"src/**/*.ts",
"../../typings/**/*.d.ts"
],
"references": [
{
"path": "../../privatePackages/assert-project"
},
{
"path": "../../privatePackages/prepare"
},
{
"path": "../cli-utils"
},
{
"path": "../constants"
},
{
"path": "../core"
},
{
"path": "../error"
},
{
"path": "../graceful-fs"
},
{
"path": "../read-project-manifest"
},
{
"path": "../store-connection-manager"
}
]
}

View File

@@ -1,8 +0,0 @@
{
"extends": "./tsconfig.json",
"include": [
"src/**/*.ts",
"test/**/*.ts",
"../../typings/**/*.d.ts"
]
}

View File

@@ -16,7 +16,7 @@
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7774 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7773 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"
@@ -40,11 +40,13 @@
"@pnpm/prepare": "workspace:0.0.27",
"@pnpm/test-fixtures": "workspace:*",
"@types/is-ci": "^3.0.0",
"@types/ncp": "^2.0.4",
"@types/proxyquire": "^1.3.28",
"@types/ramda": "0.27.39",
"@types/sinon": "^9.0.11",
"@types/yarnpkg__lockfile": "^1.1.5",
"@types/zkochan__table": "npm:@types/table@6.0.0",
"load-json-file": "^6.2.0",
"ncp": "^2.0.0",
"path-name": "^1.0.0",
"proxyquire": "^2.1.3",
"read-yaml-file": "^2.1.0",
@@ -61,21 +63,26 @@
"@pnpm/common-cli-options-help": "workspace:0.7.1",
"@pnpm/config": "workspace:13.4.1",
"@pnpm/constants": "workspace:5.0.0",
"@pnpm/core": "workspace:1.1.0",
"@pnpm/error": "workspace:2.0.0",
"@pnpm/filter-workspace-packages": "workspace:4.4.1",
"@pnpm/find-workspace-dir": "workspace:3.0.1",
"@pnpm/find-workspace-packages": "workspace:3.1.21",
"@pnpm/graceful-fs": "workspace:1.0.0",
"@pnpm/manifest-utils": "workspace:2.1.1",
"@pnpm/outdated": "workspace:9.0.12",
"@pnpm/package-store": "workspace:12.1.0",
"@pnpm/parse-wanted-dependency": "workspace:2.0.0",
"@pnpm/plugin-commands-rebuild": "workspace:5.2.0",
"@pnpm/pnpmfile": "workspace:1.1.1",
"@pnpm/read-project-manifest": "workspace:2.0.6",
"@pnpm/resolver-base": "workspace:8.1.0",
"@pnpm/semver-diff": "^1.0.2",
"@pnpm/sort-packages": "workspace:2.1.2",
"@pnpm/store-connection-manager": "workspace:3.1.3",
"@pnpm/types": "workspace:7.5.0",
"@yarnpkg/lockfile": "^1.1.0",
"@zkochan/rimraf": "^2.1.1",
"@zkochan/table": "^1.0.0",
"@zkochan/which": "^2.0.3",
"camelcase-keys": "^6.2.2",
@@ -83,6 +90,7 @@
"enquirer": "^2.3.6",
"is-ci": "^3.0.0",
"is-subdir": "^1.1.1",
"load-json-file": "^6.2.0",
"mem": "^8.0.0",
"p-filter": "^2.1.0",
"p-limit": "^3.1.0",
@@ -91,7 +99,6 @@
"ramda": "^0.27.1",
"read-ini-file": "^3.1.0",
"render-help": "^1.0.1",
"@pnpm/core": "workspace:1.1.0",
"version-selector-type": "^3.0.0"
},
"peerDependencies": {

View File

@@ -9,11 +9,17 @@ import {
} from '@pnpm/store-connection-manager'
import gfs from '@pnpm/graceful-fs'
import { install, InstallOptions } from '@pnpm/core'
import { Config } from '@pnpm/config'
import findWorkspacePackages from '@pnpm/find-workspace-packages'
import { Project } from '@pnpm/types'
import logger from '@pnpm/logger'
import { sequenceGraph } from '@pnpm/sort-packages'
import rimraf from '@zkochan/rimraf'
import loadJsonFile from 'load-json-file'
import renderHelp from 'render-help'
import { parse as parseYarnLock } from '@yarnpkg/lockfile'
import exists from 'path-exists'
import recursive from './recursive'
export const rcOptionsTypes = cliOptionsTypes
@@ -33,8 +39,15 @@ export function help () {
export const commandNames = ['import']
export type ImportCommandOptions = Pick<Config,
| 'allProjects'
| 'selectedProjectsGraph'
| 'workspaceDir'
> & CreateStoreControllerOptions & Omit<InstallOptions, 'storeController' | 'lockfileOnly' | 'preferredVersions'>
export async function handler (
opts: CreateStoreControllerOptions & Omit<InstallOptions, 'storeController' | 'lockfileOnly' | 'preferredVersions'>
opts: ImportCommandOptions,
params: string[]
) {
// Removing existing pnpm lockfile
// it should not influence the new one
@@ -54,6 +67,39 @@ export async function handler (
throw new PnpmError('LOCKFILE_NOT_FOUND', 'No lockfile found')
}
preferredVersions = getPreferredVersions(versionsByPackageNames)
// For a workspace with shared lockfile
if (opts.workspaceDir) {
const allProjects = opts.allProjects ?? await findWorkspacePackages(opts.workspaceDir, opts)
const selectedProjectsGraph = opts.selectedProjectsGraph ?? selectProjectByDir(allProjects, opts.dir)
if (selectedProjectsGraph != null) {
const sequencedGraph = sequenceGraph(selectedProjectsGraph)
// Check and warn if there are cyclic dependencies
if (!sequencedGraph.safe) {
const cyclicDependenciesInfo = sequencedGraph.cycles.length > 0
? `: ${sequencedGraph.cycles.map(deps => deps.join(', ')).join('; ')}`
: ''
logger.warn({
message: `There are cyclic workspace dependencies${cyclicDependenciesInfo}`,
prefix: opts.workspaceDir,
})
}
await recursive(allProjects,
params,
// @ts-expect-error
{
...opts,
lockfileOnly: true,
selectedProjectsGraph,
preferredVersions,
workspaceDir: opts.workspaceDir,
},
'import'
)
}
return
}
const store = await createOrConnectStoreController(opts)
const installOpts = {
...opts,
@@ -166,3 +212,9 @@ interface YarnLockPackage {
interface YarnPackgeLock {
[name: string]: YarnLockPackage
}
function selectProjectByDir (projects: Project[], searchedDir: string) {
const project = projects.find(({ dir }) => path.relative(dir, searchedDir) === '')
if (project == null) return undefined
return { [searchedDir]: { dependencies: [], package: project } }
}

View File

@@ -6,5 +6,6 @@ import * as prune from './prune'
import * as remove from './remove'
import * as unlink from './unlink'
import * as update from './update'
import * as importCommand from './import'
export { add, fetch, install, link, prune, remove, unlink, update }
export { add, fetch, install, link, prune, remove, unlink, update, importCommand }

View File

@@ -38,6 +38,7 @@ import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './u
import updateToLatestSpecsFromManifest, { createLatestSpecs } from './updateToLatestSpecsFromManifest'
import getSaveType from './getSaveType'
import getPinnedVersion from './getPinnedVersion'
import { PreferredVersions } from '@pnpm/resolver-base'
type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
| 'bail'
@@ -70,21 +71,28 @@ type RecursiveOptions = CreateStoreControllerOptions & Pick<Config,
latest?: boolean
pending?: boolean
workspace?: boolean
} & Partial<Pick<Config, 'sort' | 'workspaceConcurrency'>>
allowNew?: boolean
forceHoistPattern?: boolean
forcePublicHoistPattern?: boolean
ignoredPackages?: Set<string>
update?: boolean
useBetaCli?: boolean
selectedProjectsGraph: ProjectsGraph
preferredVersions?: PreferredVersions
} & Partial<
Pick<Config,
| 'sort'
| 'workspaceConcurrency'
>
> & Required<
Pick<Config, 'workspaceDir'>
>
export default async function recursive (
allProjects: Project[],
params: string[],
opts: RecursiveOptions & {
allowNew?: boolean
forceHoistPattern?: boolean
forcePublicHoistPattern?: boolean
ignoredPackages?: Set<string>
update?: boolean
useBetaCli?: boolean
selectedProjectsGraph: ProjectsGraph
} & Required<Pick<Config, 'workspaceDir'>>,
cmdFullName: 'install' | 'add' | 'remove' | 'unlink' | 'update'
opts: RecursiveOptions,
cmdFullName: 'install' | 'add' | 'remove' | 'unlink' | 'update' | 'import'
): Promise<boolean | string> {
if (allProjects.length === 0) {
// It might make sense to throw an exception in this case
@@ -124,8 +132,8 @@ export default async function recursive (
targetDependenciesField,
workspacePackages,
forceHoistPattern: typeof opts.rawLocalConfig['hoist-pattern'] !== 'undefined' || typeof opts.rawLocalConfig['hoist'] !== 'undefined',
forceShamefullyHoist: typeof opts.rawLocalConfig['shamefully-hoist'] !== 'undefined',
forceHoistPattern: typeof opts.rawLocalConfig?.['hoist-pattern'] !== 'undefined' || typeof opts.rawLocalConfig?.['hoist'] !== 'undefined',
forceShamefullyHoist: typeof opts.rawLocalConfig?.['shamefully-hoist'] !== 'undefined',
}) as InstallOptions
const result = {
@@ -164,13 +172,24 @@ export default async function recursive (
const updateMatch = cmdFullName === 'update' && (params.length > 0) ? createMatcher(params) : null
// For a workspace with shared lockfile
if (opts.lockfileDir && ['add', 'install', 'remove', 'update'].includes(cmdFullName)) {
if (opts.lockfileDir && ['add', 'install', 'remove', 'update', 'import'].includes(cmdFullName)) {
let importers = await getImporters()
const calculatedRepositoryRoot = calculateRepositoryRoot(opts.workspaceDir, importers.map(x => x.rootDir))
const isFromWorkspace = isSubdir.bind(null, calculatedRepositoryRoot)
importers = await pFilter(importers, async ({ rootDir }: { rootDir: string }) => isFromWorkspace(await fs.realpath(rootDir)))
if (importers.length === 0) return true
const mutation = cmdFullName === 'remove' ? 'uninstallSome' : (params.length === 0 && !updateToLatest ? 'install' : 'installSome')
let mutation!: string
switch (cmdFullName) {
case 'remove':
mutation = 'uninstallSome'
break
case 'import':
mutation = 'install'
break
default:
mutation = (params.length === 0 && !updateToLatest ? 'install' : 'installSome')
break
}
const writeProjectManifests = [] as Array<(manifest: ProjectManifest) => Promise<void>>
const mutatedImporters = [] as MutatedProject[]
await Promise.all(importers.map(async ({ buildIndex, rootDir }) => {

View File

@@ -0,0 +1,8 @@
import getPinnedVersion from '@pnpm/plugin-commands-installation/lib/getPinnedVersion'
test('getPinnedVersion()', () => {
expect(getPinnedVersion({ saveExact: true })).toEqual('patch')
expect(getPinnedVersion({ savePrefix: '' })).toEqual('patch')
expect(getPinnedVersion({ savePrefix: '~' })).toEqual('minor')
expect(getPinnedVersion({ savePrefix: '^' })).toEqual('major')
})

View File

@@ -0,0 +1,9 @@
import getSaveType from '@pnpm/plugin-commands-installation/lib/getSaveType'
test('getSaveType()', () => {
expect(getSaveType({ saveDev: true })).toEqual('devDependencies')
expect(getSaveType({ savePeer: true })).toEqual('devDependencies')
expect(getSaveType({ saveOptional: true })).toEqual('optionalDependencies')
expect(getSaveType({ saveProd: true })).toEqual('dependencies')
expect(getSaveType({})).toBeUndefined()
})

View File

@@ -3,7 +3,7 @@ import { promisify } from 'util'
import path from 'path'
import assertProject from '@pnpm/assert-project'
import PnpmError from '@pnpm/error'
import { importCommand } from '@pnpm/plugin-commands-import'
import { importCommand } from '@pnpm/plugin-commands-installation'
import prepare, { tempDir } from '@pnpm/prepare'
import { addDistTag, REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import tempy from 'tempy'
@@ -52,7 +52,7 @@ test('import from package-lock.json', async () => {
await importCommand.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
})
}, [])
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
@@ -73,7 +73,7 @@ test('import from yarn.lock', async () => {
await importCommand.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
})
}, [])
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
@@ -94,7 +94,7 @@ test('import from npm-shrinkwrap.json', async () => {
await importCommand.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
})
}, [])
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
@@ -113,7 +113,7 @@ test('import fails when no lockfiles are found', async () => {
importCommand.handler({
...DEFAULT_OPTS,
dir: process.cwd(),
})
}, [])
).rejects.toThrow(
new PnpmError('LOCKFILE_NOT_FOUND', 'No lockfile found')
)

View File

@@ -0,0 +1,116 @@
/// <reference path="../../../typings/index.d.ts" />
import { promisify } from 'util'
import path from 'path'
import assertProject from '@pnpm/assert-project'
import { importCommand } from '@pnpm/plugin-commands-installation'
import { tempDir } from '@pnpm/prepare'
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
import { readProjects } from '@pnpm/filter-workspace-packages'
import tempy from 'tempy'
import ncpCB from 'ncp'
const ncp = promisify(ncpCB)
const fixtures = path.join(__dirname, '../../../fixtures')
const REGISTRY = `http://localhost:${REGISTRY_MOCK_PORT}`
const TMP = tempy.directory()
const DEFAULT_OPTS = {
alwaysAuth: false,
ca: undefined,
cacheDir: path.join(TMP, 'cache'),
cert: undefined,
fetchRetries: 2,
fetchRetryFactor: 90,
fetchRetryMaxtimeout: 90,
fetchRetryMintimeout: 10,
httpsProxy: undefined,
key: undefined,
localAddress: undefined,
lock: false,
lockStaleDuration: 90,
networkConcurrency: 16,
offline: false,
proxy: undefined,
rawConfig: { registry: REGISTRY },
registries: { default: REGISTRY },
registry: REGISTRY,
storeDir: path.join(TMP, 'store'),
strictSsl: false,
userAgent: 'pnpm',
useRunningStoreServer: false,
useStoreServer: false,
}
test('import from shared yarn.lock of monorepo', async () => {
tempDir()
await ncp(path.join(fixtures, 'workspace-has-shared-yarn-lock'), process.cwd())
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await importCommand.handler({
...DEFAULT_OPTS,
allProjects,
selectedProjectsGraph,
workspaceDir: process.cwd(),
lockfileDir: process.cwd(),
dir: process.cwd(),
}, [])
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
expect(lockfile.packages).toHaveProperty(['/is-positive/1.0.0'])
expect(lockfile.packages).toHaveProperty(['/is-negative/1.0.1'])
// node_modules is not created
await project.hasNot('is-positive')
await project.hasNot('is-negative')
})
test('import from shared package-lock.json of monorepo', async () => {
tempDir()
await ncp(path.join(fixtures, 'workspace-has-shared-package-lock-json'), process.cwd())
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await importCommand.handler({
...DEFAULT_OPTS,
allProjects,
selectedProjectsGraph,
workspaceDir: process.cwd(),
lockfileDir: process.cwd(),
dir: process.cwd(),
}, [])
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
expect(lockfile.packages).toHaveProperty(['/is-positive/1.0.0'])
expect(lockfile.packages).toHaveProperty(['/is-negative/1.0.1'])
// node_modules is not created
await project.hasNot('is-positive')
await project.hasNot('is-negative')
})
test('import from shared npm-shrinkwrap.json of monorepo', async () => {
tempDir()
await ncp(path.join(fixtures, 'workspace-has-shared-npm-shrinkwrap-json'), process.cwd())
const { allProjects, selectedProjectsGraph } = await readProjects(process.cwd(), [])
await importCommand.handler({
...DEFAULT_OPTS,
allProjects,
selectedProjectsGraph,
workspaceDir: process.cwd(),
lockfileDir: process.cwd(),
dir: process.cwd(),
}, [])
const project = assertProject(process.cwd())
const lockfile = await project.readLockfile()
expect(lockfile.packages).toHaveProperty(['/is-positive/1.0.0'])
expect(lockfile.packages).toHaveProperty(['/is-negative/1.0.1'])
// node_modules is not created
await project.hasNot('is-positive')
await project.hasNot('is-negative')
})

View File

@@ -0,0 +1,42 @@
import updateToLatestSpecsFromManifest, { createLatestSpecs } from '@pnpm/plugin-commands-installation/lib/updateToLatestSpecsFromManifest'
const MAINFEST = {
dependencies: {
'dep-of-pkg-with-1-dep': 'npm:pkg-with-1-dep@1.0.0',
},
devDependencies: {
foo: 'github:pnpm/foo',
},
optionalDependencies: {
'is-positive': '^1.0.0',
},
}
test('updateToLatestSpecsFromManifest()', () => {
const updateResult1 = expect(updateToLatestSpecsFromManifest(MAINFEST, {
optionalDependencies: true,
dependencies: true,
devDependencies: true,
}))
updateResult1.toHaveLength(2)
updateResult1.toContain('dep-of-pkg-with-1-dep@npm:pkg-with-1-dep@latest')
updateResult1.toContain('is-positive@latest')
const updateResult2 = expect(updateToLatestSpecsFromManifest(MAINFEST, {
optionalDependencies: false,
dependencies: true,
devDependencies: false,
}))
updateResult2.toHaveLength(1)
updateResult2.toStrictEqual(['dep-of-pkg-with-1-dep@npm:pkg-with-1-dep@latest'])
})
test('createLatestSpecs()', () => {
expect(
createLatestSpecs(['dep-of-pkg-with-1-dep', 'is-positive@2.0.0', 'foo'], MAINFEST)
).toStrictEqual(['dep-of-pkg-with-1-dep@npm:pkg-with-1-dep@latest', 'is-positive@2.0.0', 'foo'])
expect(
createLatestSpecs(['dep-of-pkg-with-1-dep', 'is-positive', 'foo', 'bar'], MAINFEST)
).toStrictEqual(['dep-of-pkg-with-1-dep@npm:pkg-with-1-dep@latest', 'is-positive@latest', 'foo'])
})

View File

@@ -48,6 +48,9 @@
{
"path": "../find-workspace-packages"
},
{
"path": "../graceful-fs"
},
{
"path": "../lockfile-types"
},
@@ -75,6 +78,9 @@
{
"path": "../pnpmfile"
},
{
"path": "../read-project-manifest"
},
{
"path": "../resolver-base"
},

View File

@@ -16,7 +16,7 @@
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7775 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7774 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"

View File

@@ -16,7 +16,7 @@
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7776 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7775 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"

View File

@@ -16,7 +16,7 @@
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7777 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7776 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"

View File

@@ -16,7 +16,7 @@
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7778 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7777 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"

View File

@@ -16,7 +16,7 @@
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7779 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7778 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"

View File

@@ -16,7 +16,7 @@
"registry-mock": "registry-mock",
"test:jest": "jest",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7780 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7779 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm run compile",
"compile": "rimraf lib tsconfig.tsbuildinfo && tsc --build && pnpm run lint -- --fix"

View File

@@ -42,7 +42,6 @@
"@pnpm/pick-registry-for-package": "workspace:2.0.5",
"@pnpm/plugin-commands-audit": "workspace:5.1.19",
"@pnpm/plugin-commands-env": "workspace:1.2.3",
"@pnpm/plugin-commands-import": "workspace:3.1.17",
"@pnpm/plugin-commands-installation": "workspace:7.1.0",
"@pnpm/plugin-commands-listing": "workspace:4.0.20",
"@pnpm/plugin-commands-outdated": "workspace:5.0.25",
@@ -158,7 +157,7 @@
"test:jest": "jest",
"pretest:e2e": "rimraf node_modules/.bin/pnpm",
"test:e2e": "registry-mock prepare && run-p -r registry-mock test:jest",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7781 pnpm run test:e2e",
"_test": "cross-env PNPM_REGISTRY_MOCK_PORT=7780 pnpm run test:e2e",
"test": "pnpm run compile && pnpm run _test",
"prepublishOnly": "pnpm compile && npm cache clear --force && publish-packed --prune --npm-client yarn --dest dist",
"postpublish": "publish-packed",

View File

@@ -2,8 +2,7 @@ import { CompletionFunc } from '@pnpm/command'
import { types as allTypes } from '@pnpm/config'
import { audit } from '@pnpm/plugin-commands-audit'
import { env } from '@pnpm/plugin-commands-env'
import { importCommand } from '@pnpm/plugin-commands-import'
import { add, fetch, install, link, prune, remove, unlink, update } from '@pnpm/plugin-commands-installation'
import { add, fetch, install, link, prune, remove, unlink, update, importCommand } from '@pnpm/plugin-commands-installation'
import { list, ll, why } from '@pnpm/plugin-commands-listing'
import { outdated } from '@pnpm/plugin-commands-outdated'
import { pack, publish } from '@pnpm/plugin-commands-publishing'

View File

@@ -149,7 +149,7 @@ export default async function run (inputArgv: string[]) {
}
if (
cmd === 'install' &&
(cmd === 'install' || cmd === 'import') &&
typeof workspaceDir === 'string'
) {
cliOptions['recursive'] = true

View File

@@ -78,9 +78,6 @@
{
"path": "../plugin-commands-env"
},
{
"path": "../plugin-commands-import"
},
{
"path": "../plugin-commands-installation"
},

63
pnpm-lock.yaml generated
View File

@@ -2059,49 +2059,6 @@ importers:
execa: /safe-execa/0.1.1
path-name: 1.0.0
packages/plugin-commands-import:
specifiers:
'@pnpm/assert-project': workspace:*
'@pnpm/cli-utils': workspace:0.6.29
'@pnpm/constants': workspace:5.0.0
'@pnpm/core': workspace:1.1.0
'@pnpm/error': workspace:2.0.0
'@pnpm/graceful-fs': workspace:1.0.0
'@pnpm/plugin-commands-import': 'link:'
'@pnpm/prepare': workspace:0.0.27
'@pnpm/read-project-manifest': workspace:2.0.6
'@pnpm/store-connection-manager': workspace:3.1.3
'@types/ncp': ^2.0.4
'@types/yarnpkg__lockfile': ^1.1.5
'@yarnpkg/lockfile': ^1.1.0
'@zkochan/rimraf': ^2.1.1
load-json-file: ^6.2.0
ncp: ^2.0.0
path-exists: ^4.0.0
render-help: ^1.0.1
tempy: ^1.0.0
dependencies:
'@pnpm/cli-utils': link:../cli-utils
'@pnpm/constants': link:../constants
'@pnpm/core': link:../core
'@pnpm/error': link:../error
'@pnpm/graceful-fs': link:../graceful-fs
'@pnpm/read-project-manifest': link:../read-project-manifest
'@pnpm/store-connection-manager': link:../store-connection-manager
'@zkochan/rimraf': 2.1.1
load-json-file: 6.2.0
path-exists: 4.0.0
render-help: 1.0.2
devDependencies:
'@pnpm/assert-project': link:../../privatePackages/assert-project
'@pnpm/plugin-commands-import': 'link:'
'@pnpm/prepare': link:../../privatePackages/prepare
'@types/ncp': 2.0.5
'@types/yarnpkg__lockfile': 1.1.5
'@yarnpkg/lockfile': 1.1.0
ncp: 2.0.0
tempy: 1.0.1
packages/plugin-commands-installation:
specifiers:
'@pnpm/assert-project': workspace:*
@@ -2116,6 +2073,7 @@ importers:
'@pnpm/filter-workspace-packages': workspace:4.4.1
'@pnpm/find-workspace-dir': workspace:3.0.1
'@pnpm/find-workspace-packages': workspace:3.1.21
'@pnpm/graceful-fs': workspace:1.0.0
'@pnpm/lockfile-types': workspace:3.1.0
'@pnpm/logger': ^4.0.0
'@pnpm/manifest-utils': workspace:2.1.1
@@ -2128,6 +2086,7 @@ importers:
'@pnpm/plugin-commands-rebuild': workspace:5.2.0
'@pnpm/pnpmfile': workspace:1.1.1
'@pnpm/prepare': workspace:0.0.27
'@pnpm/read-project-manifest': workspace:2.0.6
'@pnpm/resolver-base': workspace:8.1.0
'@pnpm/semver-diff': ^1.0.2
'@pnpm/sort-packages': workspace:2.1.2
@@ -2135,10 +2094,14 @@ importers:
'@pnpm/test-fixtures': workspace:*
'@pnpm/types': workspace:7.5.0
'@types/is-ci': ^3.0.0
'@types/ncp': ^2.0.4
'@types/proxyquire': ^1.3.28
'@types/ramda': 0.27.39
'@types/sinon': ^9.0.11
'@types/yarnpkg__lockfile': ^1.1.5
'@types/zkochan__table': npm:@types/table@6.0.0
'@yarnpkg/lockfile': ^1.1.0
'@zkochan/rimraf': ^2.1.1
'@zkochan/table': ^1.0.0
'@zkochan/which': ^2.0.3
camelcase-keys: ^6.2.2
@@ -2148,6 +2111,7 @@ importers:
is-subdir: ^1.1.1
load-json-file: ^6.2.0
mem: ^8.0.0
ncp: ^2.0.0
p-filter: ^2.1.0
p-limit: ^3.1.0
path-absolute: ^1.0.1
@@ -2176,17 +2140,21 @@ importers:
'@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/graceful-fs': link:../graceful-fs
'@pnpm/manifest-utils': link:../manifest-utils
'@pnpm/outdated': link:../outdated
'@pnpm/package-store': link:../package-store
'@pnpm/parse-wanted-dependency': link:../parse-wanted-dependency
'@pnpm/plugin-commands-rebuild': link:../plugin-commands-rebuild
'@pnpm/pnpmfile': link:../pnpmfile
'@pnpm/read-project-manifest': link:../read-project-manifest
'@pnpm/resolver-base': link:../resolver-base
'@pnpm/semver-diff': 1.0.2
'@pnpm/sort-packages': link:../sort-packages
'@pnpm/store-connection-manager': link:../store-connection-manager
'@pnpm/types': link:../types
'@yarnpkg/lockfile': 1.1.0
'@zkochan/rimraf': 2.1.1
'@zkochan/table': 1.0.0
'@zkochan/which': 2.0.3
camelcase-keys: 6.2.2
@@ -2194,6 +2162,7 @@ importers:
enquirer: 2.3.6
is-ci: 3.0.1
is-subdir: 1.2.0
load-json-file: 6.2.0
mem: 8.1.1
p-filter: 2.1.0
p-limit: 3.1.0
@@ -2213,11 +2182,13 @@ importers:
'@pnpm/prepare': link:../../privatePackages/prepare
'@pnpm/test-fixtures': link:../../privatePackages/test-fixtures
'@types/is-ci': 3.0.0
'@types/ncp': 2.0.5
'@types/proxyquire': 1.3.28
'@types/ramda': 0.27.39
'@types/sinon': 9.0.11
'@types/yarnpkg__lockfile': 1.1.5
'@types/zkochan__table': /@types/table/6.0.0
load-json-file: 6.2.0
ncp: 2.0.0
path-name: 1.0.0
proxyquire: 2.1.3
read-yaml-file: 2.1.0
@@ -2728,7 +2699,6 @@ importers:
'@pnpm/pick-registry-for-package': workspace:2.0.5
'@pnpm/plugin-commands-audit': workspace:5.1.19
'@pnpm/plugin-commands-env': workspace:1.2.3
'@pnpm/plugin-commands-import': workspace:3.1.17
'@pnpm/plugin-commands-installation': workspace:7.1.0
'@pnpm/plugin-commands-listing': workspace:4.0.20
'@pnpm/plugin-commands-outdated': workspace:5.0.25
@@ -2821,7 +2791,6 @@ importers:
'@pnpm/pick-registry-for-package': link:../pick-registry-for-package
'@pnpm/plugin-commands-audit': link:../plugin-commands-audit
'@pnpm/plugin-commands-env': link:../plugin-commands-env
'@pnpm/plugin-commands-import': link:../plugin-commands-import
'@pnpm/plugin-commands-installation': link:../plugin-commands-installation
'@pnpm/plugin-commands-listing': link:../plugin-commands-listing
'@pnpm/plugin-commands-outdated': link:../plugin-commands-outdated
@@ -5599,7 +5568,7 @@ packages:
/@yarnpkg/lockfile/1.1.0:
resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
dev: true
dev: false
/@yarnpkg/parsers/2.3.0:
resolution: {integrity: sha512-qgz0QUgOvnhtF92kaluIhIIKBUHlYlHUBQxqh5v9+sxEQvUeF6G6PKiFlzo3E6O99XwvNEGpVu1xZPoSGyGscQ==}