Skip to content

Commit e79e7dd

Browse files
alan-agius4clydin
authored andcommitted
refactor(@angular-devkit/core): refactor NodeJsAsyncHost to use FS promises
1 parent aedfcc1 commit e79e7dd

File tree

2 files changed

+71
-93
lines changed

2 files changed

+71
-93
lines changed

etc/api/angular_devkit/core/node/_golden-api.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ export declare function isDirectory(filePath: string): boolean;
44

55
export declare function isFile(filePath: string): boolean;
66

7-
export declare class NodeJsAsyncHost implements virtualFs.Host<fs.Stats> {
7+
export declare class NodeJsAsyncHost implements virtualFs.Host<Stats> {
88
get capabilities(): virtualFs.HostCapabilities;
99
delete(path: Path): Observable<void>;
1010
exists(path: Path): Observable<boolean>;
@@ -13,12 +13,12 @@ export declare class NodeJsAsyncHost implements virtualFs.Host<fs.Stats> {
1313
list(path: Path): Observable<PathFragment[]>;
1414
read(path: Path): Observable<virtualFs.FileBuffer>;
1515
rename(from: Path, to: Path): Observable<void>;
16-
stat(path: Path): Observable<virtualFs.Stats<fs.Stats>> | null;
16+
stat(path: Path): Observable<virtualFs.Stats<Stats>>;
1717
watch(path: Path, _options?: virtualFs.HostWatchOptions): Observable<virtualFs.HostWatchEvent> | null;
1818
write(path: Path, content: virtualFs.FileBuffer): Observable<void>;
1919
}
2020

21-
export declare class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
21+
export declare class NodeJsSyncHost implements virtualFs.Host<Stats> {
2222
get capabilities(): virtualFs.HostCapabilities;
2323
delete(path: Path): Observable<void>;
2424
exists(path: Path): Observable<boolean>;
@@ -27,7 +27,7 @@ export declare class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
2727
list(path: Path): Observable<PathFragment[]>;
2828
read(path: Path): Observable<virtualFs.FileBuffer>;
2929
rename(from: Path, to: Path): Observable<void>;
30-
stat(path: Path): Observable<virtualFs.Stats<fs.Stats>>;
30+
stat(path: Path): Observable<virtualFs.Stats<Stats>>;
3131
watch(path: Path, _options?: virtualFs.HostWatchOptions): Observable<virtualFs.HostWatchEvent> | null;
3232
write(path: Path, content: virtualFs.FileBuffer): Observable<void>;
3333
}

packages/angular_devkit/core/node/host.ts

+67-89
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,25 @@
55
* Use of this source code is governed by an MIT-style license that can be
66
* found in the LICENSE file at https://angular.io/license
77
*/
8-
import * as fs from 'fs';
9-
import * as path from 'path';
8+
import {
9+
PathLike,
10+
Stats,
11+
constants,
12+
existsSync,
13+
mkdirSync,
14+
promises as fsPromises,
15+
readFileSync,
16+
readdirSync,
17+
renameSync,
18+
rmdirSync,
19+
statSync,
20+
unlinkSync,
21+
writeFileSync,
22+
} from 'fs';
23+
import { dirname as pathDirname, join as pathJoin } from 'path';
1024
import { Observable, concat, from as observableFrom, of, throwError } from 'rxjs';
1125
import {
1226
concatMap,
13-
ignoreElements,
1427
map,
1528
mergeMap,
1629
publish,
@@ -39,6 +52,16 @@ interface ChokidarWatcher {
3952
close(): void;
4053
}
4154

55+
async function exists(path: PathLike): Promise<boolean> {
56+
try {
57+
await fsPromises.access(path, constants.F_OK);
58+
59+
return true;
60+
} catch {
61+
return false;
62+
}
63+
}
64+
4265
// This will only be initialized if the watch() method is called.
4366
// Otherwise chokidar appears only in type positions, and shouldn't be referenced
4467
// in the JavaScript output.
@@ -58,128 +81,83 @@ function loadFSWatcher() {
5881
}
5982
}
6083

61-
type FsFunction0<R> = (cb: (err?: Error|null, result?: R) => void) => void;
62-
type FsFunction1<R, T1> = (p1: T1, cb: (err?: Error|null, result?: R) => void) => void;
63-
type FsFunction2<R, T1, T2>
64-
= (p1: T1, p2: T2, cb: (err?: Error|null, result?: R) => void) => void;
65-
66-
67-
function _callFs<R>(fn: FsFunction0<R>): Observable<R>;
68-
function _callFs<R, T1>(fn: FsFunction1<R, T1>, p1: T1): Observable<R>;
69-
function _callFs<R, T1, T2>(fn: FsFunction2<R, T1, T2>, p1: T1, p2: T2): Observable<R>;
70-
71-
function _callFs<ResultT>(fn: Function, ...args: {}[]): Observable<ResultT> {
72-
return new Observable(obs => {
73-
fn(...args, (err?: Error|null, result?: ResultT) => {
74-
if (err) {
75-
obs.error(err);
76-
} else {
77-
obs.next(result);
78-
obs.complete();
79-
}
80-
});
81-
});
82-
}
83-
84-
8584
/**
8685
* An implementation of the Virtual FS using Node as the background. There are two versions; one
8786
* synchronous and one asynchronous.
8887
*/
89-
export class NodeJsAsyncHost implements virtualFs.Host<fs.Stats> {
88+
export class NodeJsAsyncHost implements virtualFs.Host<Stats> {
9089
get capabilities(): virtualFs.HostCapabilities {
9190
return { synchronous: false };
9291
}
9392

9493
write(path: Path, content: virtualFs.FileBuffer): Observable<void> {
95-
return _callFs<void, string, fs.MakeDirectoryOptions>(
96-
fs.mkdir,
97-
getSystemPath(dirname(path)),
98-
{ recursive: true },
99-
).pipe(
100-
mergeMap(() => _callFs<void, string, Uint8Array>(
101-
fs.writeFile,
102-
getSystemPath(path),
103-
new Uint8Array(content),
104-
)),
105-
);
94+
return observableFrom(fsPromises.mkdir(getSystemPath(dirname(path)), { recursive: true }))
95+
.pipe(
96+
mergeMap(() => fsPromises.writeFile(getSystemPath(path), content)),
97+
);
10698
}
10799

108100
read(path: Path): Observable<virtualFs.FileBuffer> {
109-
return _callFs(fs.readFile, getSystemPath(path)).pipe(
110-
map(buffer => new Uint8Array(buffer).buffer as virtualFs.FileBuffer),
111-
);
101+
return observableFrom(fsPromises.readFile(getSystemPath(path)))
102+
.pipe(
103+
map(buffer => new Uint8Array(buffer).buffer as virtualFs.FileBuffer),
104+
);
112105
}
113106

114107
delete(path: Path): Observable<void> {
115108
return this.isDirectory(path).pipe(
116-
mergeMap(isDirectory => {
109+
mergeMap(async isDirectory => {
117110
if (isDirectory) {
118-
const allFiles: Path[] = [];
119-
const allDirs: Path[] = [];
120-
const _recurseList = (path: Path) => {
121-
for (const fragment of fs.readdirSync(getSystemPath(path))) {
122-
if (fs.statSync(getSystemPath(join(path, fragment))).isDirectory()) {
123-
_recurseList(join(path, fragment));
124-
allDirs.push(join(path, fragment));
111+
const recursiveDelete = async (dirPath: string) => {
112+
for (const fragment of (await fsPromises.readdir(dirPath))) {
113+
const sysPath = pathJoin(dirPath, fragment);
114+
const stats = await fsPromises.stat(sysPath);
115+
116+
if (stats.isDirectory()) {
117+
await recursiveDelete(sysPath);
118+
await fsPromises.rmdir(sysPath);
125119
} else {
126-
allFiles.push(join(path, fragment));
120+
await fsPromises.unlink(sysPath);
127121
}
128122
}
129123
};
130-
_recurseList(path);
131124

132-
return concat(
133-
observableFrom(allFiles).pipe(
134-
mergeMap(p => _callFs(fs.unlink, getSystemPath(p))),
135-
ignoreElements(),
136-
),
137-
observableFrom(allDirs).pipe(
138-
concatMap(p => _callFs(fs.rmdir, getSystemPath(p))),
139-
),
140-
);
125+
await recursiveDelete(getSystemPath(path));
141126
} else {
142-
return _callFs(fs.unlink, getSystemPath(path));
127+
await fsPromises.unlink(getSystemPath(path));
143128
}
144129
}),
145-
map(() => undefined),
146130
);
147131
}
148132

149133
rename(from: Path, to: Path): Observable<void> {
150-
return _callFs(fs.rename, getSystemPath(from), getSystemPath(to));
134+
return observableFrom(fsPromises.rename(getSystemPath(from), getSystemPath(to)));
151135
}
152136

153137
list(path: Path): Observable<PathFragment[]> {
154-
return _callFs<string[], string>(fs.readdir, getSystemPath(path)).pipe(
138+
return observableFrom(fsPromises.readdir(getSystemPath(path))).pipe(
155139
map((names) => names.map(name => fragment(name))),
156140
);
157141
}
158142

159143
exists(path: Path): Observable<boolean> {
160-
// Exists is a special case because it cannot error.
161-
return new Observable(obs => {
162-
fs.exists(path, exists => {
163-
obs.next(exists);
164-
obs.complete();
165-
});
166-
});
144+
return observableFrom(exists(path));
167145
}
168146

169147
isDirectory(path: Path): Observable<boolean> {
170-
return _callFs(fs.stat, getSystemPath(path)).pipe(
148+
return this.stat(path).pipe(
171149
map(stat => stat.isDirectory()),
172150
);
173151
}
174152
isFile(path: Path): Observable<boolean> {
175-
return _callFs(fs.stat, getSystemPath(path)).pipe(
153+
return this.stat(path).pipe(
176154
map(stat => stat.isFile()),
177155
);
178156
}
179157

180158
// Some hosts may not support stat.
181-
stat(path: Path): Observable<virtualFs.Stats<fs.Stats>> | null {
182-
return _callFs(fs.stat, getSystemPath(path));
159+
stat(path: Path): Observable<virtualFs.Stats<Stats>> {
160+
return observableFrom(fsPromises.stat(getSystemPath(path)));
183161
}
184162

185163
// Some hosts may not support watching.
@@ -226,23 +204,23 @@ export class NodeJsAsyncHost implements virtualFs.Host<fs.Stats> {
226204
/**
227205
* An implementation of the Virtual FS using Node as the backend, synchronously.
228206
*/
229-
export class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
207+
export class NodeJsSyncHost implements virtualFs.Host<Stats> {
230208
get capabilities(): virtualFs.HostCapabilities {
231209
return { synchronous: true };
232210
}
233211

234212
write(path: Path, content: virtualFs.FileBuffer): Observable<void> {
235213
return new Observable(obs => {
236-
fs.mkdirSync(getSystemPath(dirname(path)), { recursive: true });
237-
fs.writeFileSync(getSystemPath(path), new Uint8Array(content));
214+
mkdirSync(getSystemPath(dirname(path)), { recursive: true });
215+
writeFileSync(getSystemPath(path), new Uint8Array(content));
238216
obs.next();
239217
obs.complete();
240218
});
241219
}
242220

243221
read(path: Path): Observable<virtualFs.FileBuffer> {
244222
return new Observable(obs => {
245-
const buffer = fs.readFileSync(getSystemPath(path));
223+
const buffer = readFileSync(getSystemPath(path));
246224

247225
obs.next(new Uint8Array(buffer).buffer as virtualFs.FileBuffer);
248226
obs.complete();
@@ -253,9 +231,9 @@ export class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
253231
return this.isDirectory(path).pipe(
254232
concatMap(isDir => {
255233
if (isDir) {
256-
const dirPaths = fs.readdirSync(getSystemPath(path));
234+
const dirPaths = readdirSync(getSystemPath(path));
257235
const rmDirComplete = new Observable<void>((obs) => {
258-
fs.rmdirSync(getSystemPath(path));
236+
rmdirSync(getSystemPath(path));
259237
obs.complete();
260238
});
261239

@@ -265,7 +243,7 @@ export class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
265243
);
266244
} else {
267245
try {
268-
fs.unlinkSync(getSystemPath(path));
246+
unlinkSync(getSystemPath(path));
269247
} catch (err) {
270248
return throwError(err);
271249
}
@@ -279,24 +257,24 @@ export class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
279257
rename(from: Path, to: Path): Observable<void> {
280258
return new Observable(obs => {
281259
const toSystemPath = getSystemPath(to);
282-
fs.mkdirSync(path.dirname(toSystemPath), { recursive: true });
283-
fs.renameSync(getSystemPath(from), toSystemPath);
260+
mkdirSync(pathDirname(toSystemPath), { recursive: true });
261+
renameSync(getSystemPath(from), toSystemPath);
284262
obs.next();
285263
obs.complete();
286264
});
287265
}
288266

289267
list(path: Path): Observable<PathFragment[]> {
290268
return new Observable(obs => {
291-
const names = fs.readdirSync(getSystemPath(path));
269+
const names = readdirSync(getSystemPath(path));
292270
obs.next(names.map(name => fragment(name)));
293271
obs.complete();
294272
});
295273
}
296274

297275
exists(path: Path): Observable<boolean> {
298276
return new Observable(obs => {
299-
obs.next(fs.existsSync(getSystemPath(path)));
277+
obs.next(existsSync(getSystemPath(path)));
300278
obs.complete();
301279
});
302280
}
@@ -311,9 +289,9 @@ export class NodeJsSyncHost implements virtualFs.Host<fs.Stats> {
311289
}
312290

313291
// Some hosts may not support stat.
314-
stat(path: Path): Observable<virtualFs.Stats<fs.Stats>> {
292+
stat(path: Path): Observable<virtualFs.Stats<Stats>> {
315293
return new Observable(obs => {
316-
obs.next(fs.statSync(getSystemPath(path)));
294+
obs.next(statSync(getSystemPath(path)));
317295
obs.complete();
318296
});
319297
}

0 commit comments

Comments
 (0)