Skip to content

Commit ed61b3f

Browse files
committed
handle applying nested package patch files
1 parent 016e74d commit ed61b3f

File tree

10 files changed

+2321
-907
lines changed

10 files changed

+2321
-907
lines changed

package.json

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,30 +28,38 @@
2828
]
2929
},
3030
"jest": {
31+
"globals": {
32+
"ts-jest": {
33+
"diagnostics": false
34+
}
35+
},
3136
"transform": {
3237
".(ts|tsx)": "<rootDir>/node_modules/ts-jest/preprocessor.js"
3338
},
34-
"testRegex":
35-
"/(src|integration-tests|property-based-tests)/.+\\.test\\.ts$",
36-
"moduleFileExtensions": ["ts", "tsx", "js"]
39+
"testRegex": "/(src|integration-tests|property-based-tests)/.+\\.test\\.ts$",
40+
"moduleFileExtensions": [
41+
"ts",
42+
"tsx",
43+
"js"
44+
]
3745
},
3846
"devDependencies": {
3947
"@types/app-root-path": "^1.2.4",
4048
"@types/chalk": "^0.4.31",
4149
"@types/fs-extra": "^4.0.0",
42-
"@types/jest": "^20.0.6",
50+
"@types/jest": "^23.3.12",
4351
"@types/minimist": "^1.2.0",
4452
"@types/node": "^7.0.18",
4553
"@types/rimraf": "^0.0.28",
4654
"@types/tmp": "^0.0.33",
4755
"husky": "^0.13.3",
48-
"jest": "^20.0.4",
56+
"jest": "^23.6.0",
4957
"lint-staged": "^3.4.1",
5058
"np": "^2.15.0",
5159
"prettier": "^1.11.1",
5260
"randomstring": "^1.1.5",
53-
"ts-jest": "^20.0.10",
54-
"ts-node": "^6.0.3",
61+
"ts-jest": "^23.10.5",
62+
"ts-node": "^7.0.1",
5563
"tslint": "^5.2.0",
5664
"typescript": "^3.2.2"
5765
},

src/applyPatches.ts

Lines changed: 119 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -5,101 +5,104 @@ import { executeEffects } from "./patch/apply"
55
import { existsSync, readFileSync } from "fs-extra"
66
import { join, resolve } from "./path"
77
import { posix } from "path"
8+
import { getPatchDetailsFromFilename } from "./getPatchDetailsFromFilename"
89

9-
type OpaqueString<S extends string> = string & { type: S }
10-
export type AppPath = OpaqueString<"AppPath">
11-
type PatchesDirectory = OpaqueString<"PatchesDirectory">
12-
type FileName = OpaqueString<"FileName">
13-
type PackageName = OpaqueString<"PackageName">
14-
type PackageVersion = OpaqueString<"PackageVersion">
15-
16-
function findPatchFiles(patchesDirectory: PatchesDirectory): FileName[] {
10+
function findPatchFiles(patchesDirectory: string): string[] {
1711
if (!existsSync(patchesDirectory)) {
1812
return []
1913
}
2014

21-
return getPatchFiles(patchesDirectory) as FileName[]
22-
}
23-
24-
function getPatchDetailsFromFilename(filename: FileName) {
25-
// ok to coerce this, since we already filtered for valid package file names
26-
// in getPatchFiles
27-
const match = filename.match(/^(.+?)(:|\+)(.+)\.patch$/) as string[]
28-
const packageName = match[1] as PackageName
29-
const version = match[3] as PackageVersion
30-
31-
return {
32-
packageName,
33-
version,
34-
}
15+
return getPatchFiles(patchesDirectory) as string[]
3516
}
3617

37-
function getInstalledPackageVersion(
38-
appPath: AppPath,
39-
packageName: PackageName,
40-
) {
41-
const packageDir = join(appPath, "node_modules", packageName)
18+
function getInstalledPackageVersion({
19+
appPath,
20+
path,
21+
pathSpecifier,
22+
}: {
23+
appPath: string
24+
path: string
25+
pathSpecifier: string
26+
}): string | null {
27+
const packageDir = join(appPath, "node_modules", path)
4228
if (!existsSync(packageDir)) {
4329
console.warn(
4430
`${yellow("Warning:")} Patch file found for package ${posix.basename(
45-
packageDir,
46-
)}` + ` which is not present at ${packageDir}`,
31+
pathSpecifier,
32+
)}` + ` which is not present at ${path}`,
4733
)
4834

4935
return null
5036
}
5137

52-
return require(join(packageDir, "package.json")).version as PackageVersion
38+
return require(join(packageDir, "package.json")).version
5339
}
5440

5541
export const applyPatchesForApp = (
56-
appPath: AppPath,
42+
appPath: string,
5743
reverse: boolean,
5844
patchDir: string = "patches",
5945
): void => {
60-
const patchesDirectory = join(appPath, patchDir) as PatchesDirectory
46+
const patchesDirectory = join(appPath, patchDir)
6147
const files = findPatchFiles(patchesDirectory)
6248

6349
if (files.length === 0) {
6450
console.log(cyan("No patch files found"))
6551
}
6652

6753
files.forEach(filename => {
68-
const { packageName, version } = getPatchDetailsFromFilename(filename)
54+
const details = getPatchDetailsFromFilename(filename)
6955

70-
const installedPackageVersion = getInstalledPackageVersion(
56+
if (!details) {
57+
console.warn(`Unrecognized patch file in patches directory ${filename}`)
58+
return
59+
}
60+
61+
const { name, version, path, pathSpecifier } = details
62+
63+
const installedPackageVersion = getInstalledPackageVersion({
7164
appPath,
72-
packageName,
73-
)
65+
path,
66+
pathSpecifier,
67+
})
7468

7569
if (!installedPackageVersion) {
7670
return
7771
}
7872

79-
if (applyPatch(resolve(patchesDirectory, filename) as FileName, reverse)) {
73+
if (applyPatch(resolve(patchesDirectory, filename) as string, reverse)) {
8074
// yay patch was applied successfully
8175
// print warning if version mismatch
8276
if (installedPackageVersion !== version) {
83-
printVersionMismatchWarning(
84-
packageName,
85-
installedPackageVersion,
86-
version,
87-
)
77+
printVersionMismatchWarning({
78+
packageName: name,
79+
actualVersion: installedPackageVersion,
80+
originalVersion: version,
81+
pathSpecifier,
82+
path,
83+
})
8884
} else {
89-
console.log(`${bold(packageName)}@${version} ${green("✔")}`)
85+
console.log(`${bold(pathSpecifier)}@${version} ${green("✔")}`)
9086
}
9187
} else {
9288
// completely failed to apply patch
9389
// TODO: propagate useful error messages from patch application
9490
if (installedPackageVersion === version) {
95-
printBrokenPatchFileError(packageName, filename)
91+
printBrokenPatchFileError({
92+
packageName: name,
93+
patchFileName: filename,
94+
pathSpecifier,
95+
path,
96+
})
9697
} else {
97-
printPatchApplictionFailureError(
98-
packageName,
99-
installedPackageVersion,
100-
version,
101-
filename,
102-
)
98+
printPatchApplictionFailureError({
99+
packageName: name,
100+
actualVersion: installedPackageVersion,
101+
originalVersion: version,
102+
patchFileName: filename,
103+
path,
104+
pathSpecifier,
105+
})
103106
}
104107
process.exit(1)
105108
}
@@ -127,11 +130,19 @@ export const applyPatch = (
127130
return true
128131
}
129132

130-
function printVersionMismatchWarning(
131-
packageName: PackageName,
132-
actualVersion: PackageVersion,
133-
originalVersion: PackageVersion,
134-
) {
133+
function printVersionMismatchWarning({
134+
packageName,
135+
actualVersion,
136+
originalVersion,
137+
pathSpecifier,
138+
path,
139+
}: {
140+
packageName: string
141+
actualVersion: string
142+
originalVersion: string
143+
pathSpecifier: string
144+
path: string
145+
}) {
135146
console.warn(`
136147
${red("Warning:")} patch-package detected a patch file version mismatch
137148
@@ -145,49 +156,78 @@ ${red("Warning:")} patch-package detected a patch file version mismatch
145156
applied to
146157
147158
${packageName}@${bold(actualVersion)}
159+
160+
At path
161+
162+
${path}
148163
149164
This warning is just to give you a heads-up. There is a small chance of
150165
breakage even though the patch was applied successfully. Make sure the package
151166
still behaves like you expect (you wrote tests, right?) and then run
152167
153-
${bold(`patch-package ${packageName}`)}
168+
${bold(`patch-package ${pathSpecifier}`)}
154169
155170
to update the version in the patch file name and make this warning go away.
156171
`)
157172
}
158173

159-
function printBrokenPatchFileError(
160-
packageName: PackageName,
161-
patchFileName: FileName,
162-
) {
174+
function printBrokenPatchFileError({
175+
packageName,
176+
patchFileName,
177+
path,
178+
pathSpecifier,
179+
}: {
180+
packageName: string
181+
patchFileName: string
182+
path: string
183+
pathSpecifier: string
184+
}) {
163185
console.error(`
164186
${red.bold("**ERROR**")} ${red(
165-
`Failed to apply patch for package ${bold(packageName)}`,
187+
`Failed to apply patch for package ${bold(packageName)} at path`,
166188
)}
189+
190+
${path}
167191
168192
This error was caused because patch-package cannot apply the following patch file:
169193
170194
patches/${patchFileName}
171195
172-
If removing node_modules and trying again doesn't fix this, maybe there was
173-
an accidental change made to the patch file? If not, then it's probably a bug
174-
in patch-package, so please submit a bug report. Thanks!
196+
Try removing node_modules and trying again. If that doesn't work, maybe there was
197+
an accidental change made to the patch file? Try recreating it by manually
198+
editing the appropriate files and running:
199+
200+
patch-package ${pathSpecifier}
201+
202+
If that doesn't work, then it's a bug in patch-package, so please submit a bug
203+
report. Thanks!
175204
176205
https://github.com/ds300/patch-package/issues
177-
206+
178207
`)
179208
}
180209

181-
function printPatchApplictionFailureError(
182-
packageName: PackageName,
183-
actualVersion: PackageVersion,
184-
originalVersion: PackageVersion,
185-
patchFileName: FileName,
186-
) {
210+
function printPatchApplictionFailureError({
211+
packageName,
212+
actualVersion,
213+
originalVersion,
214+
patchFileName,
215+
path,
216+
pathSpecifier,
217+
}: {
218+
packageName: string
219+
actualVersion: string
220+
originalVersion: string
221+
patchFileName: string
222+
path: string
223+
pathSpecifier: string
224+
}) {
187225
console.error(`
188226
${red.bold("**ERROR**")} ${red(
189-
`Failed to apply patch for package ${bold(packageName)}`,
227+
`Failed to apply patch for package ${bold(packageName)} at path`,
190228
)}
229+
230+
${path}
191231
192232
This error was caused because ${bold(packageName)} has changed since you
193233
made the patch file for it. This introduced conflicts with your patch,
@@ -197,35 +237,18 @@ ${red.bold("**ERROR**")} ${red(
197237
Maybe this means your patch file is no longer necessary, in which case
198238
hooray! Just delete it!
199239
200-
Otherwise, you need to manually fix the patch file. Or generate a new one
240+
Otherwise, you need generate a new patch file.
201241
202242
To generate a new one, just repeat the steps you made to generate the first
203-
one, but accounting for the changes in ${packageName}.
204-
205-
i.e. make changes, run \`patch-package ${packageName}\`, and commit.
206-
207-
To manually fix a patch file, Run:
208-
209-
${bold(`patch -p1 -i patches/${patchFileName} --verbose --dry-run`)}
243+
one.
210244
211-
To list rejected hunks. A 'hunk' is a section of patch file that describes
212-
one contiguous area of changes. They are numbered from 1 and begin with lines
213-
that look like this:
245+
i.e. manually make the appropriate file changes, then run
214246
215-
@@ -48,5 +49,6 @@ function foo(bar) {
216-
217-
Remove the conflicting hunks, then manually edit files in
218-
219-
node_modules/${packageName}
220-
221-
to reflect the changes that the conflicting hunks were supposed to make.
222-
223-
Then run \`patch-package ${packageName}\`
247+
patch-package ${pathSpecifier}
224248
225249
Info:
226-
Patch was made for version ${green.bold(originalVersion)}
227-
Meanwhile node_modules/${bold(packageName)} is version ${red.bold(
228-
actualVersion,
229-
)}
250+
Patch file: patches/${patchFileName}
251+
Patch was made for version: ${green.bold(originalVersion)}
252+
Installed version ${red.bold(actualVersion)}
230253
`)
231254
}

src/getAppRootPath.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { join, resolve } from "./path"
22
import process from "process"
3-
import { AppPath } from "./applyPatches"
43
import { existsSync } from "fs-extra"
54

6-
export const getAppRootPath = (): AppPath => {
5+
export const getAppRootPath = (): string => {
76
let cwd = process.cwd()
87
while (!existsSync(join(cwd, "package.json"))) {
98
cwd = resolve(cwd, "../")
109
}
11-
return cwd as AppPath
10+
return cwd
1211
}

0 commit comments

Comments
 (0)