mirror of
https://github.com/pnpm/pnpm.git
synced 2026-05-12 10:11:42 -04:00
feat: pnpm import support yarn (#3655)
This commit is contained in:
5
.changeset/brave-kids-care.md
Normal file
5
.changeset/brave-kids-care.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@pnpm/plugin-commands-import": patch
|
||||
---
|
||||
|
||||
feat: support pnpm import
|
||||
1
fixtures/has-yarn-lock/.gitignore
vendored
Normal file
1
fixtures/has-yarn-lock/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!yarn.lock
|
||||
8
fixtures/has-yarn-lock/package.json
Normal file
8
fixtures/has-yarn-lock/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "has-yarn-lock",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"dep-of-pkg-with-1-dep": "^101.0.0",
|
||||
"pkg-with-1-dep": "*"
|
||||
}
|
||||
}
|
||||
15
fixtures/has-yarn-lock/yarn.lock
Normal file
15
fixtures/has-yarn-lock/yarn.lock
Normal file
@@ -0,0 +1,15 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
dep-of-pkg-with-1-dep@^1.0.0, dep-of-pkg-with-1-dep@^101.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.nlark.com/dep-of-pkg-with-1-dep/download/dep-of-pkg-with-1-dep-1.1.0.tgz#67d3ac196cb08162e543b87b3b1829c76072c927"
|
||||
integrity sha1-Z9OsGWywgWLlQ7h7Oxgpx2ByySc=
|
||||
|
||||
pkg-with-1-dep@*:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.nlark.com/pkg-with-1-dep/download/pkg-with-1-dep-1.0.0.tgz#0d4ed129376f707bac0bf5f6a58410f663e27c80"
|
||||
integrity sha1-DU7RKTdvcHusC/X2pYQQ9mPifIA=
|
||||
dependencies:
|
||||
dep-of-pkg-with-1-dep "^1.0.0"
|
||||
@@ -7,4 +7,5 @@ packages:
|
||||
- '!has-npm-shrinkwrap-json'
|
||||
- '!has-outdated-deps'
|
||||
- '!has-package-lock-json'
|
||||
- '!hello-world-js-bin'
|
||||
- '!hello-world-js-bin'
|
||||
- '!has-yarn-lock'
|
||||
@@ -36,6 +36,8 @@
|
||||
"@pnpm/assert-project": "workspace:*",
|
||||
"@pnpm/prepare": "workspace:0.0.26",
|
||||
"@types/ncp": "^2.0.4",
|
||||
"@types/yarnpkg__lockfile": "^1.1.5",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"ncp": "^2.0.0",
|
||||
"tempy": "^1.0.0"
|
||||
},
|
||||
@@ -44,9 +46,11 @@
|
||||
"@pnpm/constants": "workspace:5.0.0",
|
||||
"@pnpm/error": "workspace:2.0.0",
|
||||
"@pnpm/read-project-manifest": "workspace:2.0.5",
|
||||
"@pnpm/graceful-fs": "workspace:1.0.0",
|
||||
"@pnpm/store-connection-manager": "workspace:3.0.8",
|
||||
"@zkochan/rimraf": "^2.1.1",
|
||||
"load-json-file": "^6.2.0",
|
||||
"path-exists": "^4.0.0",
|
||||
"render-help": "^1.0.1",
|
||||
"supi": "workspace:0.47.13"
|
||||
},
|
||||
|
||||
@@ -7,10 +7,13 @@ import {
|
||||
createOrConnectStoreController,
|
||||
CreateStoreControllerOptions,
|
||||
} from '@pnpm/store-connection-manager'
|
||||
import gfs from '@pnpm/graceful-fs'
|
||||
import { install, InstallOptions } from 'supi'
|
||||
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'
|
||||
|
||||
export const rcOptionsTypes = cliOptionsTypes
|
||||
|
||||
@@ -20,9 +23,11 @@ export function cliOptionsTypes () {
|
||||
|
||||
export function help () {
|
||||
return renderHelp({
|
||||
description: `Generates ${WANTED_LOCKFILE} from an npm package-lock.json (or npm-shrinkwrap.json) file.`,
|
||||
description: `Generates ${WANTED_LOCKFILE} from an npm package-lock.json (or npm-shrinkwrap.json, yarn.lock) file.`,
|
||||
url: docsUrl('import'),
|
||||
usages: ['pnpm import'],
|
||||
usages: [
|
||||
'pnpm import',
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -34,10 +39,21 @@ export async function handler (
|
||||
// Removing existing pnpm lockfile
|
||||
// it should not influence the new one
|
||||
await rimraf(path.join(opts.dir, WANTED_LOCKFILE))
|
||||
const npmPackageLock = await readNpmLockfile(opts.dir)
|
||||
const versionsByPackageNames = {}
|
||||
getAllVersionsByPackageNames(npmPackageLock, versionsByPackageNames)
|
||||
const preferredVersions = getPreferredVersions(versionsByPackageNames)
|
||||
let preferredVersions = {}
|
||||
if (await exists(path.join(opts.dir, 'yarn.lock'))) {
|
||||
const yarnPackgeLockFile = await readYarnLockFile(opts.dir)
|
||||
getAllVersionsFromYarnLockFile(yarnPackgeLockFile, versionsByPackageNames)
|
||||
} else if (
|
||||
await exists(path.join(opts.dir, 'package-lock.json')) ||
|
||||
await exists(path.join(opts.dir, 'npm-shrinkwrap.json'))
|
||||
) {
|
||||
const npmPackageLock = await readNpmLockfile(opts.dir)
|
||||
getAllVersionsByPackageNames(npmPackageLock, versionsByPackageNames)
|
||||
} else {
|
||||
throw new PnpmError('LOCKFILE_NOT_FOUND', 'No lockfile found')
|
||||
}
|
||||
preferredVersions = getPreferredVersions(versionsByPackageNames)
|
||||
const store = await createOrConnectStoreController(opts)
|
||||
const installOpts = {
|
||||
...opts,
|
||||
@@ -49,6 +65,21 @@ export async function handler (
|
||||
await install(await readProjectManifestOnly(opts.dir), installOpts)
|
||||
}
|
||||
|
||||
async function readYarnLockFile (dir: string) {
|
||||
try {
|
||||
const yarnLockFile = await gfs.readFile(path.join(dir, 'yarn.lock'), 'utf8')
|
||||
const lockJsonFile = await parseYarnLock(yarnLockFile)
|
||||
if (lockJsonFile.type === 'success') {
|
||||
return lockJsonFile.object
|
||||
} else {
|
||||
throw new PnpmError('GET_YARN_LOCKFILE_ERR', `Failed With ${lockJsonFile.type}`)
|
||||
}
|
||||
} catch (err) {
|
||||
if (err['code'] !== 'ENOENT') throw err // eslint-disable-line @typescript-eslint/dot-notation
|
||||
}
|
||||
throw new PnpmError('YARN_LOCKFILE_NOT_FOUND', 'No yarn.lock found')
|
||||
}
|
||||
|
||||
async function readNpmLockfile (dir: string) {
|
||||
try {
|
||||
return await loadJsonFile<LockedPackage>(path.join(dir, 'package-lock.json'))
|
||||
@@ -96,6 +127,21 @@ function getAllVersionsByPackageNames (
|
||||
}
|
||||
}
|
||||
|
||||
function getAllVersionsFromYarnLockFile (
|
||||
yarnPackageLock: YarnPackgeLock,
|
||||
versionsByPackageNames: {
|
||||
[packageName: string]: Set<string>
|
||||
}
|
||||
) {
|
||||
for (const packageName of Object.keys(yarnPackageLock)) {
|
||||
const pkgName = packageName.substring(0, packageName.lastIndexOf('@'))
|
||||
if (!versionsByPackageNames[pkgName]) {
|
||||
versionsByPackageNames[pkgName] = new Set()
|
||||
}
|
||||
versionsByPackageNames[pkgName].add(yarnPackageLock[packageName].version)
|
||||
}
|
||||
}
|
||||
|
||||
interface NpmPackageLock {
|
||||
dependencies: LockedPackagesMap
|
||||
}
|
||||
@@ -108,3 +154,15 @@ interface LockedPackage {
|
||||
interface LockedPackagesMap {
|
||||
[name: string]: LockedPackage
|
||||
}
|
||||
|
||||
interface YarnLockPackage {
|
||||
version: string
|
||||
resolved: string
|
||||
integrity: string
|
||||
dependencies?: {
|
||||
[name: string]: string
|
||||
}
|
||||
}
|
||||
interface YarnPackgeLock {
|
||||
[name: string]: YarnLockPackage
|
||||
}
|
||||
|
||||
@@ -64,6 +64,27 @@ test('import from package-lock.json', async () => {
|
||||
await project.hasNot('pkg-with-1-dep')
|
||||
})
|
||||
|
||||
test('import from yarn.lock', async () => {
|
||||
await addDistTag({ package: 'dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' })
|
||||
tempDir()
|
||||
|
||||
await ncp(path.join(fixtures, 'has-yarn-lock'), process.cwd())
|
||||
|
||||
await importCommand.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
})
|
||||
|
||||
const project = assertProject(process.cwd())
|
||||
const lockfile = await project.readLockfile()
|
||||
expect(lockfile.packages).toHaveProperty(['/dep-of-pkg-with-1-dep/100.1.0'])
|
||||
expect(lockfile.packages).not.toHaveProperty(['/dep-of-pkg-with-1-dep/100.0.0'])
|
||||
|
||||
// node_modules is not created
|
||||
await project.hasNot('dep-of-pkg-with-1-dep')
|
||||
await project.hasNot('pkg-with-1-dep')
|
||||
})
|
||||
|
||||
test('import from npm-shrinkwrap.json', async () => {
|
||||
await addDistTag({ package: 'dep-of-pkg-with-1-dep', version: '100.1.0', distTag: 'latest' })
|
||||
tempDir()
|
||||
@@ -85,18 +106,15 @@ test('import from npm-shrinkwrap.json', async () => {
|
||||
await project.hasNot('pkg-with-1-dep')
|
||||
})
|
||||
|
||||
test('import fails when no npm lockfiles are found', async () => {
|
||||
test('import fails when no lockfiles are found', async () => {
|
||||
prepare(undefined)
|
||||
|
||||
let err!: PnpmError
|
||||
try {
|
||||
await importCommand.handler({
|
||||
await expect(
|
||||
importCommand.handler({
|
||||
...DEFAULT_OPTS,
|
||||
dir: process.cwd(),
|
||||
})
|
||||
} catch (_err) {
|
||||
err = _err
|
||||
}
|
||||
|
||||
expect(err.message.toString()).toMatch(/No package-lock.json or npm-shrinkwrap.json found/)
|
||||
).rejects.toThrow(
|
||||
new PnpmError('LOCKFILE_NOT_FOUND', 'No lockfile found')
|
||||
)
|
||||
})
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
{
|
||||
"path": "../error"
|
||||
},
|
||||
{
|
||||
"path": "../graceful-fs"
|
||||
},
|
||||
{
|
||||
"path": "../read-project-manifest"
|
||||
},
|
||||
|
||||
@@ -16,13 +16,13 @@ import {
|
||||
mutateModules,
|
||||
WorkspacePackages,
|
||||
} from 'supi'
|
||||
import logger from '@pnpm/logger'
|
||||
import { sequenceGraph } from '@pnpm/sort-packages'
|
||||
import getPinnedVersion from './getPinnedVersion'
|
||||
import getSaveType from './getSaveType'
|
||||
import recursive, { createMatcher, matchDependencies } from './recursive'
|
||||
import updateToLatestSpecsFromManifest, { createLatestSpecs } from './updateToLatestSpecsFromManifest'
|
||||
import { createWorkspaceSpecs, updateToWorkspacePackagesFromManifest } from './updateWorkspaceDependencies'
|
||||
import logger from '@pnpm/logger'
|
||||
import { sequenceGraph } from '@pnpm/sort-packages'
|
||||
|
||||
const OVERWRITE_UPDATE_OPTIONS = {
|
||||
allowNew: true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import path from 'path'
|
||||
import { add, install } from '@pnpm/plugin-commands-installation'
|
||||
import prepare from '@pnpm/prepare'
|
||||
import { REGISTRY_MOCK_PORT } from '@pnpm/registry-mock'
|
||||
import path from 'path'
|
||||
import tempy from 'tempy'
|
||||
|
||||
const REGISTRY_URL = `http://localhost:${REGISTRY_MOCK_PORT}`
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { install } from '@pnpm/plugin-commands-installation'
|
||||
import { readProjects } from '@pnpm/filter-workspace-packages'
|
||||
import { preparePackages } from '@pnpm/prepare'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
import logger from '@pnpm/logger'
|
||||
import { DEFAULT_OPTS } from './utils'
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(logger, 'warn')
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
"bundle:pnpm": "cross-var esbuild lib/pnpm.js --bundle --platform=node --outfile=dist/pnpm.cjs --external:node-gyp --define:process.env.npm_package_name=\\\"$npm_package_name\\\" --define:process.env.npm_package_version=\\\"$npm_package_version\\\"",
|
||||
"bundle:pnpx": "esbuild lib/pnpx.js --bundle --platform=node --outfile=dist/pnpx.cjs",
|
||||
"bundle": "pnpm bundle:pnpm && pnpm bundle:pnpx",
|
||||
"start": "pnpm tsc -- --watch",
|
||||
"start": "pnpm tsc --watch",
|
||||
"lint": "eslint -c ../../eslint.json src/**/*.ts test/**/*.ts",
|
||||
"registry-mock": "registry-mock",
|
||||
"test:jest": "jest",
|
||||
|
||||
16
pnpm-lock.yaml
generated
16
pnpm-lock.yaml
generated
@@ -1827,14 +1827,18 @@ importers:
|
||||
'@pnpm/cli-utils': workspace:0.6.16
|
||||
'@pnpm/constants': workspace:5.0.0
|
||||
'@pnpm/error': workspace:2.0.0
|
||||
'@pnpm/graceful-fs': workspace:1.0.0
|
||||
'@pnpm/plugin-commands-import': 'link:'
|
||||
'@pnpm/prepare': workspace:0.0.26
|
||||
'@pnpm/read-project-manifest': workspace:2.0.5
|
||||
'@pnpm/store-connection-manager': workspace:3.0.8
|
||||
'@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
|
||||
supi: workspace:0.47.13
|
||||
tempy: ^1.0.0
|
||||
@@ -1842,10 +1846,12 @@ importers:
|
||||
'@pnpm/cli-utils': link:../cli-utils
|
||||
'@pnpm/constants': link:../constants
|
||||
'@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
|
||||
supi: link:../supi
|
||||
devDependencies:
|
||||
@@ -1853,6 +1859,8 @@ importers:
|
||||
'@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
|
||||
|
||||
@@ -5074,6 +5082,10 @@ packages:
|
||||
'@types/yargs-parser': 20.2.1
|
||||
dev: true
|
||||
|
||||
/@types/yarnpkg__lockfile/1.1.5:
|
||||
resolution: {integrity: sha512-8NYnGOctzsI4W0ApsP/BIHD/LnxpJ6XaGf2AZmz4EyDYJMxtprN4279dLNI1CPZcwC9H18qYcaFv4bXi0wmokg==}
|
||||
dev: true
|
||||
|
||||
/@typescript-eslint/eslint-plugin/4.29.2_eslint@7.32.0+typescript@4.3.5:
|
||||
resolution: {integrity: sha512-x4EMgn4BTfVd9+Z+r+6rmWxoAzBaapt4QFqE+d8L8sUtYZYLDTK6VG/y/SMMWA5t1/BVU5Kf+20rX4PtWzUYZg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
@@ -5246,6 +5258,10 @@ packages:
|
||||
tslib: 1.14.1
|
||||
dev: false
|
||||
|
||||
/@yarnpkg/lockfile/1.1.0:
|
||||
resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
|
||||
dev: true
|
||||
|
||||
/@yarnpkg/parsers/2.4.0:
|
||||
resolution: {integrity: sha512-XWgiNGh4MkhdBTJVEbXEqzk66JKjvxTtKGeLPqo3rnJ7JiJnRaK2n9MLTKQB0uoRMWYzPlISdIlok6H9OdlOVQ==}
|
||||
engines: {node: '>=12 <14 || 14.2 - 14.9 || >14.10.0'}
|
||||
|
||||
Reference in New Issue
Block a user