feat: pnpm import support yarn (#3655)

This commit is contained in:
zoomdong
2021-08-20 01:52:47 +08:00
committed by GitHub
parent 655af55baf
commit f815dabd99
14 changed files with 149 additions and 20 deletions

View File

@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-import": patch
---
feat: support pnpm import

1
fixtures/has-yarn-lock/.gitignore vendored Normal file
View File

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

View 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": "*"
}
}

View 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"

View File

@@ -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'

View File

@@ -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"
},

View File

@@ -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
}

View File

@@ -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')
)
})

View File

@@ -24,6 +24,9 @@
{
"path": "../error"
},
{
"path": "../graceful-fs"
},
{
"path": "../read-project-manifest"
},

View File

@@ -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,

View File

@@ -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}`

View File

@@ -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')

View File

@@ -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
View File

@@ -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'}