Skip to content

Commit

Permalink
Use lockfiles to determine package resolutions.
Browse files Browse the repository at this point in the history
  • Loading branch information
ds300 committed Feb 27, 2019
1 parent c30be71 commit 9dd98c3
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 30 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"typescript": "^3.2.2"
},
"dependencies": {
"@yarnpkg/lockfile": "^1.1.0",
"chalk": "^1.1.3",
"cross-spawn": "^6.0.5",
"fs-extra": "^4.0.1",
Expand Down
2 changes: 1 addition & 1 deletion src/PackageDetails.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { join } from "path"

interface PackageDetails {
export interface PackageDetails {
humanReadablePathSpecifier: string
pathSpecifier: string
path: string
Expand Down
100 changes: 100 additions & 0 deletions src/getPackageResolution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { join, resolve } from "./path"
import { PackageDetails, getPatchDetailsFromCliString } from "./PackageDetails"
import { PackageManager, detectPackageManager } from "./detectPackageManager"
import { readFileSync } from "fs-extra"
import { parse as parseYarnLockFile } from "@yarnpkg/lockfile"

export function getPackageResolution({
packageDetails,
packageManager,
appPath,
}: {
packageDetails: PackageDetails
packageManager: PackageManager
appPath: string
}) {
if (packageManager === "yarn") {
const appLockFile = parseYarnLockFile(readFileSync("yarn.lock").toString())
if (appLockFile.type !== "success") {
throw new Error("Can't parse lock file")
}

const installedVersion = require(join(
resolve(appPath, packageDetails.path),
"package.json",
)).version as string

const entries = Object.entries(appLockFile.object).filter(
([k, v]) =>
k.startsWith(packageDetails.name + "@") &&
v.version === installedVersion,
)

const resolutions = entries.map(([_, v]) => {
return v.resolved
})

if (resolutions.length === 0) {
throw new Error(
`Can't find lockfile entry for ${packageDetails.pathSpecifier}`,
)
}

if (new Set(resolutions).size !== 1) {
console.warn(
`Ambigious lockfile entries for ${
packageDetails.pathSpecifier
}. Using version ${installedVersion}`,
)
return installedVersion
}

if (resolutions[0]) {
return resolutions[0]
}

const resolution = entries[0][0].slice(packageDetails.name.length + 1)

// resolve relative file path
if (resolution.startsWith("file:.")) {
return `file:${resolve(appPath, resolution.slice("file:".length))}`
}

return resolution
} else {
const lockfile = require(join(
appPath,
packageManager === "npm-shrinkwrap"
? "npm-shrinkwrap.json"
: "package-lock.json",
))
const lockFileStack = [lockfile]
for (const name of packageDetails.packageNames.slice(0, -1)) {
const child = lockFileStack[0].dependencies
if (child && name in child) {
lockFileStack.push(child[name])
}
}
lockFileStack.reverse()
const relevantStackEntry = lockFileStack.find(
entry => entry.dependencies && packageDetails.name in entry.dependencies,
)
return relevantStackEntry.dependencies[packageDetails.name].resolved
}
}

if (require.main === module) {
const packageDetails = getPatchDetailsFromCliString(process.argv[2])
if (!packageDetails) {
console.error(`Can't find package ${process.argv[2]}`)
process.exit(1)
throw new Error()
}
console.log(
getPackageResolution({
appPath: process.cwd(),
packageDetails,
packageManager: detectPackageManager(process.cwd(), null),
}),
)
}
39 changes: 11 additions & 28 deletions src/makePatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
getPackageDetailsFromPatchFilename,
} from "./PackageDetails"
import { resolveRelativeFileDependencies } from "./resolveRelativeFileDependencies"
import { getPackageResolution } from "./getPackageResolution"

function printNoPackageFoundError(
packageName: string,
Expand Down Expand Up @@ -54,33 +55,6 @@ export const makePatch = (
process.exit(1)
}

const packageVersion = require(packageJsonPath).version as string

// packageVersionSpecifier is the version string used by the app package.json
// it won't be present for nested deps.
// We need it only for patching deps specified with file:./
// which I think only happens in tests
// but might happen in real life too.
let packageVersionSpecifier: null | string = null
if (!packageDetails.isNested) {
const { devDependencies = {}, dependencies = {} } = appPackageJson
packageVersionSpecifier =
dependencies[packageDetails.name] ||
devDependencies[packageDetails.name] ||
null
}

if (
packageVersionSpecifier &&
packageVersionSpecifier.startsWith("file:") &&
packageVersionSpecifier[5] !== "/"
) {
packageVersionSpecifier =
"file:" + resolve(appPath, packageVersionSpecifier.slice(5))
} else {
packageVersionSpecifier = null
}

const tmpRepo = dirSync({ unsafeCleanup: true })
const tmpRepoPackagePath = join(tmpRepo.name, packageDetails.path)
const tmpRepoNpmRoot = tmpRepoPackagePath.slice(
Expand All @@ -101,7 +75,11 @@ export const makePatch = (
tmpRepoPackageJsonPath,
JSON.stringify({
dependencies: {
[packageDetails.name]: packageVersionSpecifier || packageVersion,
[packageDetails.name]: getPackageResolution({
packageDetails,
packageManager,
appPath,
}),
},
resolutions: resolveRelativeFileDependencies(
appPath,
Expand All @@ -110,6 +88,11 @@ export const makePatch = (
}),
)

const packageVersion = require(join(
resolve(packageDetails.path),
"package.json",
)).version as string

if (packageManager === "yarn") {
console.info(
grey("•"),
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2015"],
"lib": ["es2015", "es2016", "es2017"],
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
Expand Down
13 changes: 13 additions & 0 deletions typings/@yarnpkg/lockfile.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
declare module "@yarnpkg/lockfile" {
export function parse(
s: string,
): {
type: "success" | "error"
object: {
[identifier: string]: {
resolved?: string
version: string
}
}
}
}
6 changes: 6 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@
version "0.0.33"
resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.0.33.tgz#1073c4bc824754ae3d10cfab88ab0237ba964e4d"

"@yarnpkg/lockfile@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==

abab@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f"
Expand Down Expand Up @@ -3390,6 +3395,7 @@ path-key@^1.0.0:
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=

path-parse@^1.0.5:
version "1.0.5"
Expand Down

0 comments on commit 9dd98c3

Please sign in to comment.