Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 66 additions & 5 deletions src/applyPatches.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import { logPatchSequenceError } from "./makePatch"
import { PackageDetails, PatchedPackageDetails } from "./PackageDetails"
import { packageIsDevDependency } from "./packageIsDevDependency"
import { executeEffects } from "./patch/apply"
import { PatchFilePart } from "./patch/parse"
import { readPatch } from "./patch/read"
import { reversePatch } from "./patch/reverse"
import { getGroupedPatches } from "./patchFs"
import { join, relative } from "./path"
import { resolvePackagePath } from "./resolvePackagePath"
import {
clearPatchApplicationState,
getPatchApplicationState,
Expand All @@ -28,17 +30,19 @@ class PatchApplicationError extends Error {
function getInstalledPackageVersion({
appPath,
path,
resolvedPath,
pathSpecifier,
isDevOnly,
patchFilename,
}: {
appPath: string
path: string
resolvedPath: string
pathSpecifier: string
isDevOnly: boolean
patchFilename: string
}): null | string {
const packageDir = join(appPath, path)
const packageDir = join(appPath, resolvedPath)
if (!existsSync(packageDir)) {
if (process.env.NODE_ENV === "production" && isDevOnly) {
return null
Expand All @@ -47,13 +51,13 @@ function getInstalledPackageVersion({
let err =
`${chalk.red("Error:")} Patch file found for package ${posix.basename(
pathSpecifier,
)}` + ` which is not present at ${relative(".", packageDir)}`
)}` + ` which is not present at ${relative(".", join(appPath, path))}`

if (!isDevOnly && process.env.NODE_ENV === "production") {
err += `

If this package is a dev dependency, rename the patch file to

${chalk.bold(patchFilename.replace(".patch", ".dev.patch"))}
`
}
Expand Down Expand Up @@ -180,7 +184,20 @@ export function applyPatchesForPackage({
bestEffort: boolean
}) {
const pathSpecifier = patches[0].pathSpecifier
const state = patches.length > 1 ? getPatchApplicationState(patches[0]) : null

// Resolve actual package path, handling npm install-strategy=linked (.store)
const firstPatch = patches[0]
const resolvedPackagePath = resolvePackagePath({
appPath,
packagePath: firstPatch.path,
packageName: firstPatch.name,
version: firstPatch.version,
})

const state =
patches.length > 1
? getPatchApplicationState(patches[0], resolvedPackagePath)
: null
const unappliedPatches = patches.slice(0)
const appliedPatches: PatchedPackageDetails[] = []
// if there are multiple patches to apply, we can't rely on the reverse-patch-dry-run behavior to make this operation
Expand Down Expand Up @@ -232,9 +249,18 @@ export function applyPatchesForPackage({
try {
const { name, version, path, isDevOnly, patchFilename } = patchDetails

// Resolve the actual package path, handling npm install-strategy=linked
const resolvedPath = resolvePackagePath({
appPath,
packagePath: path,
packageName: name,
version,
})

const installedPackageVersion = getInstalledPackageVersion({
appPath,
path,
resolvedPath,
pathSpecifier,
isDevOnly:
isDevOnly ||
Expand Down Expand Up @@ -264,6 +290,7 @@ export function applyPatchesForPackage({
patchDir,
cwd: process.cwd(),
bestEffort,
resolvedPath,
})
) {
appliedPatches.push(patchDetails)
Expand Down Expand Up @@ -338,7 +365,7 @@ export function applyPatchesForPackage({
}
// if we removed all the patches that were previously applied we can delete the state file
if (appliedPatches.length === patches.length) {
clearPatchApplicationState(patches[0])
clearPatchApplicationState(patches[0], resolvedPackagePath)
} else {
// We failed while reversing patches and some are still in the applied state.
// We need to update the state file to reflect that.
Expand All @@ -364,6 +391,7 @@ export function applyPatchesForPackage({
patchFilename: patch.patchFilename,
})),
isRebasing: false,
resolvedPath: resolvedPackagePath,
})
}
} else {
Expand All @@ -390,6 +418,7 @@ export function applyPatchesForPackage({
packageDetails: patches[0],
patches: nextState,
isRebasing: !!failedPatch,
resolvedPath: resolvedPackagePath,
})
}
if (failedPatch) {
Expand All @@ -405,20 +434,27 @@ export function applyPatch({
patchDir,
cwd,
bestEffort,
resolvedPath,
}: {
patchFilePath: string
reverse: boolean
patchDetails: PackageDetails
patchDir: string
cwd: string
bestEffort: boolean
resolvedPath?: string
}): boolean {
const patch = readPatch({
patchFilePath,
patchDetails,
patchDir,
})

// Remap paths if package resolved to a different location (e.g. .store)
if (resolvedPath && resolvedPath !== patchDetails.path) {
remapPatchPaths(patch, patchDetails.path, resolvedPath)
}

const forward = reverse ? reversePatch(patch) : patch
try {
if (!bestEffort) {
Expand Down Expand Up @@ -450,6 +486,31 @@ export function applyPatch({
return true
}

/**
* Remaps paths in parsed patch effects when the package resolved to a
* different location than expected (e.g. npm install-strategy=linked .store).
*/
function remapPatchPaths(
patch: PatchFilePart[],
originalPrefix: string,
newPrefix: string,
): void {
for (const part of patch) {
switch (part.type) {
case "patch":
case "file deletion":
case "file creation":
case "mode change":
part.path = part.path.replace(originalPrefix, newPrefix)
break
case "rename":
part.fromPath = part.fromPath.replace(originalPrefix, newPrefix)
part.toPath = part.toPath.replace(originalPrefix, newPrefix)
break
}
}
}

function createVersionMismatchWarning({
packageName,
actualVersion,
Expand Down
17 changes: 14 additions & 3 deletions src/makePatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
import { parsePatchFile } from "./patch/parse"
import { getGroupedPatches } from "./patchFs"
import { dirname, join, resolve } from "./path"
import { resolvePackagePath } from "./resolvePackagePath"
import { resolveRelativeFileDependencies } from "./resolveRelativeFileDependencies"
import { spawnSafeSync } from "./spawnSafe"
import {
Expand Down Expand Up @@ -145,11 +146,21 @@ export function makePatch({
mode.type !== "append"

const appPackageJson = require(join(appPath, "package.json"))
const packagePath = join(appPath, packageDetails.path)

// Resolve actual package path, handling npm install-strategy=linked (.store)
const resolvedPackageDetails = resolvePackagePath({
appPath,
packagePath: packageDetails.path,
packageName: packageDetails.name,
})
const packagePath = join(appPath, resolvedPackageDetails)
const packageJsonPath = join(packagePath, "package.json")

if (!existsSync(packageJsonPath)) {
printNoPackageFoundError(packagePathSpecifier, packageJsonPath)
printNoPackageFoundError(
packagePathSpecifier,
join(appPath, packageDetails.path, "package.json"),
)
process.exit(1)
}

Expand Down Expand Up @@ -187,7 +198,7 @@ export function makePatch({
)

const packageVersion = getPackageVersion(
join(resolve(packageDetails.path), "package.json"),
join(resolve(resolvedPackageDetails), "package.json"),
)

// copy .npmrc/.yarnrc in case packages are hosted in private registry
Expand Down
Loading