Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: pnpm import support yarn2 transform #3997

Merged
merged 5 commits into from Nov 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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.9",
"@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
}