Skip to content

Commit

Permalink
feat: pnpm import support yarn2 transform (#3997)
Browse files Browse the repository at this point in the history
close: #3993
  • Loading branch information
fireairforce committed Nov 24, 2021
1 parent 69b56f1 commit 3b4dc17
Show file tree
Hide file tree
Showing 13 changed files with 722 additions and 45 deletions.
5 changes: 5 additions & 0 deletions .changeset/khaki-wolves-admire.md
@@ -0,0 +1,5 @@
---
"@pnpm/plugin-commands-installation": minor
---

feat: support yarn v2 lockfile transform
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
@@ -1 +1,2 @@
* @zkochan
/packages/plugin-commands-installation/src/import/* @fireairforce
1 change: 1 addition & 0 deletions fixtures/has-yarn2-lock/.gitignore
@@ -0,0 +1 @@
!yarn.lock
14 changes: 14 additions & 0 deletions fixtures/has-yarn2-lock/package.json
@@ -0,0 +1,14 @@
{
"name": "has-yarn2-lock",
"version": "0.0.0",
"dependencies": {
"minimatch": "^3.0.4",
"rimraf": "2.5.1"
},
"devDependencies": {
"is-positive": "1.0.0"
},
"optionalDependencies": {
"is-negative": "1.0.0"
}
}
131 changes: 131 additions & 0 deletions fixtures/has-yarn2-lock/yarn.lock
@@ -0,0 +1,131 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

__metadata:
version: 5
cacheKey: 8

"balanced-match@npm:^1.0.0":
version: 1.0.2
resolution: "balanced-match@npm:1.0.2"
checksum: 9706c088a283058a8a99e0bf91b0a2f75497f185980d9ffa8b304de1d9e58ebda7c72c07ebf01dadedaac5b2907b2c6f566f660d62bd336c3468e960403b9d65
languageName: node
linkType: hard

"brace-expansion@npm:^1.1.7":
version: 1.1.11
resolution: "brace-expansion@npm:1.1.11"
dependencies:
balanced-match: ^1.0.0
concat-map: 0.0.1
checksum: faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07
languageName: node
linkType: hard

"concat-map@npm:0.0.1":
version: 0.0.1
resolution: "concat-map@npm:0.0.1"
checksum: 902a9f5d8967a3e2faf138d5cb784b9979bad2e6db5357c5b21c568df4ebe62bcb15108af1b2253744844eb964fc023fbd9afbbbb6ddd0bcc204c6fb5b7bf3af
languageName: node
linkType: hard

"glob@npm:^6.0.1":
version: 6.0.4
resolution: "glob@npm:6.0.4"
dependencies:
inflight: ^1.0.4
inherits: 2
minimatch: 2 || 3
once: ^1.3.0
path-is-absolute: ^1.0.0
checksum: c4946c3d015ac81f704d185f2b3a55eb670100693c2cf7bc833d0efd970ec727d860d4839a5178e46a7e594b34a34661bae2f4c3405727c9fd189f84954ca3c0
languageName: node
linkType: hard

"has-yarn2-lock@workspace:.":
version: 0.0.0-use.local
resolution: "has-yarn2-lock@workspace:."
dependencies:
is-negative: 1.0.0
is-positive: 1.0.0
minimatch: ^3.0.4
rimraf: 2.5.1
dependenciesMeta:
is-negative:
optional: true
languageName: unknown
linkType: soft

"inflight@npm:^1.0.4":
version: 1.0.6
resolution: "inflight@npm:1.0.6"
dependencies:
once: ^1.3.0
wrappy: 1
checksum: f4f76aa072ce19fae87ce1ef7d221e709afb59d445e05d47fba710e85470923a75de35bfae47da6de1b18afc3ce83d70facf44cfb0aff89f0a3f45c0a0244dfd
languageName: node
linkType: hard

"inherits@npm:2":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1
languageName: node
linkType: hard

"is-negative@npm:1.0.0":
version: 1.0.0
resolution: "is-negative@npm:1.0.0"
checksum: f95b7f6544a3588b2f6fe60e7128eb4ce5ba1601b903e3fb5106313f440ef5735fe88d51ae0472f6e5847bdbbac5a719952586f71eb166d38c638b2b445e08d2
languageName: node
linkType: hard

"is-positive@npm:1.0.0":
version: 1.0.0
resolution: "is-positive@npm:1.0.0"
checksum: 66bf242370ca03cc89ef1ac680a806e1716cd564001c019570dc90b0af4f288ad2a4e84f2df8f0f6fca207df20b508c9a1e6f0e4b4bbaeed84103ae84d4f0f44
languageName: node
linkType: hard

"minimatch@npm:2 || 3, minimatch@npm:^3.0.4":
version: 3.0.4
resolution: "minimatch@npm:3.0.4"
dependencies:
brace-expansion: ^1.1.7
checksum: 66ac295f8a7b59788000ea3749938b0970344c841750abd96694f80269b926ebcafad3deeb3f1da2522978b119e6ae3a5869b63b13a7859a456b3408bd18a078
languageName: node
linkType: hard

"once@npm:^1.3.0":
version: 1.4.0
resolution: "once@npm:1.4.0"
dependencies:
wrappy: 1
checksum: cd0a88501333edd640d95f0d2700fbde6bff20b3d4d9bdc521bdd31af0656b5706570d6c6afe532045a20bb8dc0849f8332d6f2a416e0ba6d3d3b98806c7db68
languageName: node
linkType: hard

"path-is-absolute@npm:^1.0.0":
version: 1.0.1
resolution: "path-is-absolute@npm:1.0.1"
checksum: 060840f92cf8effa293bcc1bea81281bd7d363731d214cbe5c227df207c34cd727430f70c6037b5159c8a870b9157cba65e775446b0ab06fd5ecc7e54615a3b8
languageName: node
linkType: hard

"rimraf@npm:2.5.1":
version: 2.5.1
resolution: "rimraf@npm:2.5.1"
dependencies:
glob: ^6.0.1
bin:
rimraf: ./bin.js
checksum: 6184bbf729b08c557d042ae729d9061c5301bc4b01cd2e580f86e6694a5e6bacbcb58db2c58230128341f25cc50d0f2c84232cbed4b709292688b623b647fc09
languageName: node
linkType: hard

"wrappy@npm:1":
version: 1.0.2
resolution: "wrappy@npm:1.0.2"
checksum: 159da4805f7e84a3d003d8841557196034155008f817172d4e986bd591f74aa82aa7db55929a54222309e01079a65a92a9e6414da5a6aa4b01ee44a511ac3ee5
languageName: node
linkType: hard
3 changes: 2 additions & 1 deletion fixtures/pnpm-workspace.yaml
Expand Up @@ -8,4 +8,5 @@ packages:
- '!has-outdated-deps'
- '!has-package-lock-json'
- '!hello-world-js-bin'
- '!has-yarn-lock'
- '!has-yarn-lock'
- '!has-yarn2-lock'
5 changes: 5 additions & 0 deletions package.json
Expand Up @@ -86,6 +86,11 @@
"@babel/types": "*"
}
},
"@yarnpkg/core": {
"dependencies": {
"@types/lodash": "^4.14.136"
}
},
"jest-circus": {
"dependencies": {
"slash": "3"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Expand Up @@ -131,7 +131,7 @@
},
"repository": "https://github.com/pnpm/pnpm/blob/master/packages/core",
"scripts": {
"start": "pnpm run tsc -- --watch",
"start": "pnpm tsc --watch",
"?commit": "echo 'Run Git commit wizard'",
"commit": "commit",
"commitmsg": "commitlint -e",
Expand Down
3 changes: 3 additions & 0 deletions packages/plugin-commands-installation/package.json
Expand Up @@ -12,6 +12,7 @@
"node": ">=12.17"
},
"scripts": {
"start": "pnpm tsc --watch",
"lint": "eslint src/**/*.ts test/**/*.ts",
"registry-mock": "registry-mock",
"test:jest": "jest",
Expand Down Expand Up @@ -81,7 +82,9 @@
"@pnpm/sort-packages": "workspace:2.1.3",
"@pnpm/store-connection-manager": "workspace:3.1.10",
"@pnpm/types": "workspace:7.6.0",
"@yarnpkg/core": "^3.2.0-rc.4",
"@yarnpkg/lockfile": "^1.1.0",
"@yarnpkg/parsers": "^2.5.0-rc.3",
"@zkochan/rimraf": "^2.1.1",
"@zkochan/table": "^1.0.0",
"@zkochan/which": "^2.0.3",
Expand Down
Expand Up @@ -18,8 +18,50 @@ import rimraf from '@zkochan/rimraf'
import loadJsonFile from 'load-json-file'
import renderHelp from 'render-help'
import { parse as parseYarnLock } from '@yarnpkg/lockfile'
import * as yarnCore from '@yarnpkg/core'
import { parseSyml } from '@yarnpkg/parsers'
import exists from 'path-exists'
import recursive from './recursive'
import recursive from '../recursive'
import { yarnLockFileKeyNormalizer } from './yarnUtil'

interface NpmPackageLock {
dependencies: LockedPackagesMap
}

interface LockedPackage {
version: string
dependencies?: LockedPackagesMap
}

interface LockedPackagesMap {
[name: string]: LockedPackage
}

interface YarnLockPackage {
version: string
resolved: string
integrity: string
dependencies?: {
[name: string]: string
}
optionalDependencies?: {
[depName: string]: string
}
}
interface YarnPackgeLock {
[name: string]: YarnLockPackage
}

enum YarnLockType {
yarn = 'yarn',
yarn2 = 'yarn2',
}

// copy from yarn v1
interface YarnLock2Struct {
type: YarnLockType.yarn2
object: YarnPackgeLock
}

export const rcOptionsTypes = cliOptionsTypes

Expand Down Expand Up @@ -114,18 +156,55 @@ export async function handler (
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}`)
let lockJsonFile
const yarnLockFileType = getYarnLockfileType(yarnLockFile)
if (yarnLockFileType === YarnLockType.yarn) {
lockJsonFile = await parseYarnLock(yarnLockFile)
if (lockJsonFile.type === 'success') {
return lockJsonFile.object
} else {
throw new PnpmError('YARN_LOCKFILE_PARSE_FAILED', `Yarn.lock file was ${lockJsonFile.type}`)
}
} else if (yarnLockFileType === YarnLockType.yarn2) {
lockJsonFile = parseYarn2Lock(yarnLockFile)
if (lockJsonFile.type === YarnLockType.yarn2) {
return lockJsonFile.object
}
}
} catch (err: any) { // eslint-disable-line
if (err['code'] !== 'ENOENT') throw err // eslint-disable-line @typescript-eslint/dot-notation
}
throw new PnpmError('YARN_LOCKFILE_NOT_FOUND', 'No yarn.lock found')
}

function parseYarn2Lock (lockFileContents: string): YarnLock2Struct {
// eslint-disable-next-line
const parseYarnLock: any = parseSyml(lockFileContents)

delete parseYarnLock.__metadata
const dependencies: YarnPackgeLock = {}

const { structUtils } = yarnCore
const { parseDescriptor, parseRange } = structUtils
const keyNormalizer = yarnLockFileKeyNormalizer(
parseDescriptor,
parseRange
)

Object.entries(parseYarnLock).forEach(
// eslint-disable-next-line
([fullDescriptor, versionData]: [string, any]) => {
keyNormalizer(fullDescriptor).forEach((descriptor) => {
dependencies[descriptor] = versionData
})
}
)
return {
object: dependencies,
type: YarnLockType.yarn2,
}
}

async function readNpmLockfile (dir: string) {
try {
return await loadJsonFile<LockedPackage>(path.join(dir, 'package-lock.json'))
Expand Down Expand Up @@ -188,33 +267,16 @@ function getAllVersionsFromYarnLockFile (
}
}

interface NpmPackageLock {
dependencies: LockedPackagesMap
}

interface LockedPackage {
version: string
dependencies?: LockedPackagesMap
}

interface LockedPackagesMap {
[name: string]: LockedPackage
}

interface YarnLockPackage {
version: string
resolved: string
integrity: string
dependencies?: {
[name: string]: string
}
}
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 } }
}

function getYarnLockfileType (
lockFileContents: string
): YarnLockType {
return lockFileContents.includes('__metadata')
? YarnLockType.yarn2
: YarnLockType.yarn
}

0 comments on commit 3b4dc17

Please sign in to comment.