-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
PatchFetcher.ts
124 lines (100 loc) Β· 4.82 KB
/
PatchFetcher.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import {Fetcher, FetchOptions, MinimalFetchOptions, ReportError, MessageName, Report} from '@yarnpkg/core';
import {Locator} from '@yarnpkg/core';
import {miscUtils, structUtils} from '@yarnpkg/core';
import {ppath, xfs, CwdFS, PortablePath} from '@yarnpkg/fslib';
import {ZipFS} from '@yarnpkg/libzip';
import * as patchUtils from './patchUtils';
import {UnmatchedHunkError} from './tools/UnmatchedHunkError';
import {reportHunk} from './tools/format';
export class PatchFetcher implements Fetcher {
supports(locator: Locator, opts: MinimalFetchOptions) {
if (!patchUtils.isPatchLocator(locator))
return false;
return true;
}
getLocalPath(locator: Locator, opts: FetchOptions) {
return null;
}
async fetch(locator: Locator, opts: FetchOptions) {
const expectedChecksum = opts.checksums.get(locator.locatorHash) || null;
const [packageFs, releaseFs, checksum] = await opts.cache.fetchPackageFromCache(locator, expectedChecksum, {
onHit: () => opts.report.reportCacheHit(locator),
onMiss: () => opts.report.reportCacheMiss(locator, `${structUtils.prettyLocator(opts.project.configuration, locator)} can't be found in the cache and will be fetched from the disk`),
loader: () => this.patchPackage(locator, opts),
...opts.cacheOptions,
});
return {
packageFs,
releaseFs,
prefixPath: structUtils.getIdentVendorPath(locator),
localPath: this.getLocalPath(locator, opts),
checksum,
};
}
private async patchPackage(locator: Locator, opts: FetchOptions) {
const {parentLocator, sourceLocator, sourceVersion, patchPaths} = patchUtils.parseLocator(locator);
const patchFiles = await patchUtils.loadPatchFiles(parentLocator, patchPaths, opts);
const tmpDir = await xfs.mktempPromise();
const currentFile = ppath.join(tmpDir, `current.zip`);
const sourceFetch = await opts.fetcher.fetch(sourceLocator, opts);
const prefixPath = structUtils.getIdentVendorPath(locator);
// First we create a copy of the package that we'll be free to mutate
const initialCopy = new ZipFS(currentFile, {
create: true,
level: opts.project.configuration.get(`compressionLevel`),
});
await miscUtils.releaseAfterUseAsync(async () => {
await initialCopy.copyPromise(prefixPath, sourceFetch.prefixPath, {baseFs: sourceFetch.packageFs, stableSort: true});
}, sourceFetch.releaseFs);
initialCopy.saveAndClose();
for (const {source, optional} of patchFiles) {
if (source === null)
continue;
// Then for each patchfile, we open this copy anew, and try to apply the
// changeset. We need to open it for each patchfile (rather than only a
// single time) because it lets us easily rollback when hitting errors
// on optional patches (we just need to call `discardAndClose`).
const patchedPackage = new ZipFS(currentFile, {
level: opts.project.configuration.get(`compressionLevel`),
});
const patchFs = new CwdFS(ppath.resolve(PortablePath.root, prefixPath), {
baseFs: patchedPackage,
});
try {
await patchUtils.applyPatchFile(patchUtils.parsePatchFile(source), {
baseFs: patchFs,
version: sourceVersion,
});
} catch (err) {
if (!(err instanceof UnmatchedHunkError))
throw err;
const enableInlineHunks = opts.project.configuration.get(`enableInlineHunks`);
const suggestion = !enableInlineHunks && !optional
? ` (set enableInlineHunks for details)`
: ``;
const message = `${structUtils.prettyLocator(opts.project.configuration, locator)}: ${err.message}${suggestion}`;
const reportExtra = (report: Report) => {
if (!enableInlineHunks)
return;
reportHunk(err.hunk, {
configuration: opts.project.configuration,
report,
});
};
// By discarding the current changes, the next patch will start from
// where we were.
patchedPackage.discardAndClose();
if (optional) {
opts.report.reportWarningOnce(MessageName.PATCH_HUNK_FAILED, message, {reportExtra});
continue;
} else {
throw new ReportError(MessageName.PATCH_HUNK_FAILED, message, reportExtra);
}
}
patchedPackage.saveAndClose();
}
return new ZipFS(currentFile, {
level: opts.project.configuration.get(`compressionLevel`),
});
}
}