Skip to content

Commit 9f7b185

Browse files
authored
Adds lutimes support for Node (#1357)
* Fixes lutimes for tgz extraction * Versions * Don't set lutimes if lutimes isn't there * Fixes definition * Fixes attachment * Restores the bind * Fixes tests w/ graceful-fs
1 parent 49bd85c commit 9f7b185

File tree

5 files changed

+84
-11
lines changed

5 files changed

+84
-11
lines changed

.yarn/versions/ec8c3499.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
releases:
2+
"@yarnpkg/cli": prerelease
3+
"@yarnpkg/core": prerelease
4+
"@yarnpkg/fslib": prerelease
5+
6+
declined:
7+
- "@yarnpkg/plugin-compat"
8+
- "@yarnpkg/plugin-constraints"
9+
- "@yarnpkg/plugin-dlx"
10+
- "@yarnpkg/plugin-essentials"
11+
- "@yarnpkg/plugin-exec"
12+
- "@yarnpkg/plugin-file"
13+
- "@yarnpkg/plugin-git"
14+
- "@yarnpkg/plugin-github"
15+
- "@yarnpkg/plugin-http"
16+
- "@yarnpkg/plugin-init"
17+
- "@yarnpkg/plugin-interactive-tools"
18+
- "@yarnpkg/plugin-link"
19+
- "@yarnpkg/plugin-node-modules"
20+
- "@yarnpkg/plugin-npm"
21+
- "@yarnpkg/plugin-npm-cli"
22+
- "@yarnpkg/plugin-pack"
23+
- "@yarnpkg/plugin-patch"
24+
- "@yarnpkg/plugin-pnp"
25+
- "@yarnpkg/plugin-stage"
26+
- "@yarnpkg/plugin-typescript"
27+
- "@yarnpkg/plugin-version"
28+
- "@yarnpkg/plugin-workspace-tools"
29+
- vscode-zipfs
30+
- "@yarnpkg/builder"
31+
- "@yarnpkg/doctor"
32+
- "@yarnpkg/json-proxy"
33+
- "@yarnpkg/pnp"
34+
- "@yarnpkg/pnpify"
35+
- "@yarnpkg/shell"

packages/yarnpkg-core/sources/tgzUtils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export async function extractArchiveTo<T extends FakeFS<PortablePath>>(tgz: Buff
108108
targetFs.mkdirpSync(ppath.dirname(mappedPath), {chmod: 0o755, utimes: [defaultTime, defaultTime]});
109109

110110
targetFs.symlinkSync(entry.linkpath, mappedPath);
111-
targetFs.lutimesSync!(mappedPath, defaultTime, defaultTime);
111+
targetFs.lutimesSync?.(mappedPath, defaultTime, defaultTime);
112112
} break;
113113
}
114114
});

packages/yarnpkg-fslib/sources/NodeFS.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {CreateReadStreamOptions, CreateWriteStreamOptions} from './FakeFS';
44
import {Dirent, SymlinkType} from './FakeFS';
55
import {BasePortableFakeFS, WriteFileOptions} from './FakeFS';
66
import {MkdirOptions, WatchOptions, WatchCallback, Watcher} from './FakeFS';
7+
import {ENOSYS} from './errors';
78
import {FSPath, PortablePath, Filename, npath} from './path';
89

910
export class NodeFS extends BasePortableFakeFS {
@@ -13,6 +14,12 @@ export class NodeFS extends BasePortableFakeFS {
1314
super();
1415

1516
this.realFs = realFs;
17+
18+
// @ts-ignore
19+
if (typeof this.realFs.lutimes !== `undefined`) {
20+
this.lutimesPromise = this.lutimesPromiseImpl;
21+
this.lutimesSync = this.lutimesSyncImpl;
22+
}
1623
}
1724

1825
getExtractHint() {
@@ -233,6 +240,26 @@ export class NodeFS extends BasePortableFakeFS {
233240
this.realFs.utimesSync(npath.fromPortablePath(p), atime, mtime);
234241
}
235242

243+
private async lutimesPromiseImpl(this: NodeFS, p: PortablePath, atime: Date | string | number, mtime: Date | string | number) {
244+
// @ts-ignore: Not yet in DefinitelyTyped
245+
const lutimes = this.realFs.lutimes;
246+
if (typeof lutimes === `undefined`)
247+
throw ENOSYS(`unavailable Node binding`, `lutimes '${p}'`);
248+
249+
return await new Promise<void>((resolve, reject) => {
250+
lutimes.call(this.realFs, npath.fromPortablePath(p), atime, mtime, this.makeCallback(resolve, reject));
251+
});
252+
}
253+
254+
private lutimesSyncImpl(this: NodeFS, p: PortablePath, atime: Date | string | number, mtime: Date | string | number) {
255+
// @ts-ignore: Not yet in DefinitelyTyped
256+
const lutimesSync = this.realFs.lutimesSync;
257+
if (typeof lutimesSync === `undefined`)
258+
throw ENOSYS(`unavailable Node binding`, `lutimes '${p}'`);
259+
260+
lutimesSync.call(this.realFs, npath.fromPortablePath(p), atime, mtime);
261+
}
262+
236263
async mkdirPromise(p: PortablePath, opts?: MkdirOptions) {
237264
return await new Promise<void>((resolve, reject) => {
238265
this.realFs.mkdir(npath.fromPortablePath(p), opts, this.makeCallback(resolve, reject));

packages/yarnpkg-fslib/sources/algorithms/copyPromise.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ export async function copyPromise<P1 extends Path, P2 extends Path>(destinationF
3232
: destinationFs.utimesPromise.bind(destinationFs);
3333

3434
for (const [p, atime, mtime] of lutimes) {
35-
// await destinationFs.utimesPromise(p, atime, mtime);
3635
await updateTime!(p, atime, mtime);
3736
}
3837
}

packages/yarnpkg-fslib/sources/index.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export function patchFs(patchedFs: typeof fs, fakeFs: FakeFS<NativePath>): void
5353
`closeSync`,
5454
`copyFileSync`,
5555
`lstatSync`,
56+
`lutimesSync`,
5657
`mkdirSync`,
5758
`openSync`,
5859
`readSync`,
@@ -79,6 +80,7 @@ export function patchFs(patchedFs: typeof fs, fakeFs: FakeFS<NativePath>): void
7980
`closePromise`,
8081
`copyFilePromise`,
8182
`lstatPromise`,
83+
`lutimesPromise`,
8284
`mkdirPromise`,
8385
`openPromise`,
8486
`readdirPromise`,
@@ -98,10 +100,8 @@ export function patchFs(patchedFs: typeof fs, fakeFs: FakeFS<NativePath>): void
98100

99101
const setupFn = (target: any, name: string, replacement: any) => {
100102
const orig = target[name];
101-
if (typeof orig === `undefined`)
102-
return;
103-
104103
target[name] = replacement;
104+
105105
if (typeof orig[promisify.custom] !== `undefined`) {
106106
replacement[promisify.custom] = orig[promisify.custom];
107107
}
@@ -142,28 +142,40 @@ export function patchFs(patchedFs: typeof fs, fakeFs: FakeFS<NativePath>): void
142142
});
143143

144144
for (const fnName of ASYNC_IMPLEMENTATIONS) {
145-
const fakeImpl: Function = (fakeFs as any)[fnName].bind(fakeFs);
146145
const origName = fnName.replace(/Promise$/, ``);
146+
if (typeof (patchedFs as any)[origName] === `undefined`)
147+
continue;
148+
149+
const fakeImpl: Function = (fakeFs as any)[fnName];
150+
if (typeof fakeImpl === `undefined`)
151+
continue;
147152

148-
setupFn(patchedFs, origName, (...args: Array<any>) => {
153+
const wrapper = (...args: Array<any>) => {
149154
const hasCallback = typeof args[args.length - 1] === `function`;
150155
const callback = hasCallback ? args.pop() : () => {};
151156

152157
process.nextTick(() => {
153-
fakeImpl(...args).then((result: any) => {
158+
fakeImpl.apply(fakeFs, args).then((result: any) => {
154159
callback(null, result);
155160
}, (error: Error) => {
156161
callback(error);
157162
});
158163
});
159-
});
164+
};
165+
166+
setupFn(patchedFs, origName, wrapper);
160167
}
161168

162169
for (const fnName of SYNC_IMPLEMENTATIONS) {
163-
const fakeImpl: Function = (fakeFs as any)[fnName].bind(fakeFs);
164170
const origName = fnName;
171+
if (typeof (patchedFs as any)[origName] === `undefined`)
172+
continue;
173+
174+
const fakeImpl: Function = (fakeFs as any)[fnName];
175+
if (typeof fakeImpl === `undefined`)
176+
continue;
165177

166-
setupFn(patchedFs, origName, fakeImpl);
178+
setupFn(patchedFs, origName, fakeImpl.bind(fakeFs));
167179
}
168180

169181
patchedFs.realpathSync.native = patchedFs.realpathSync;

0 commit comments

Comments
 (0)