pfs.ts ×32

Frontier kind: Code frontier

unlabeled · c_bc15141a6258

1371 tests · 7251 LOC · 33 files · introduces 0 tests · 306 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
32 ranges306 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1037 ranges7251 lines · 33 files · Browse complete extent
All tests (intent)
1371 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 306 introduced LOC across 32 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/base/node/pfs.ts 306 introduced LOC · 32 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- pfs.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as fs from 'fs';
7 > import { tmpdir } from 'os';
8 > import { promisify } from 'util';
9 > import { ResourceQueue, timeout } from '../common/async.js';
10 > import { isEqualOrParent, isRootOrDriveLetter, randomPath } from '../common/extpath.js';
11 > import { normalizeNFC } from '../common/normalization.js';
12 > import { basename, dirname, join, normalize, sep } from '../common/path.js';
13 > import { isLinux, isMacintosh, isWindows } from '../common/platform.js';
14 > import { extUriBiasedIgnorePathCase } from '../common/resources.js';
15 > import { URI } from '../common/uri.js';
16 > import { CancellationToken } from '../common/cancellation.js';
17 > import { rtrim } from '../common/strings.js';
18 >
19 > //#region rimraf
20 >
21 > export enum RimRafMode {
22 >
23 > /**
24 > * Slow version that unlinks each file and folder.
25 > */
26 > UNLINK,
27 >
28 > /**
29 > * Fast version that first moves the file/folder
30 > * into a temp directory and then deletes that
31 > * without waiting for it.
32 > */
33 > MOVE
34 > }
35 >
36 > /**
37 > * Allows to delete the provided path (either file or folder) recursively
38 > * with the options:
39 > * - `UNLINK`: direct removal from disk
40 > * - `MOVE`: faster variant that first moves the target to temp dir and then
41 > * deletes it in the background without waiting for that to finish.
42 > * the optional `moveToPath` allows to override where to rename the
43 > * path to before deleting it.
44 > */
45 > async function rimraf(path: string, mode: RimRafMode.UNLINK): Promise<void>;
46 > async function rimraf(path: string, mode: RimRafMode.MOVE, moveToPath?: string): Promise<void>;
47 > async function rimraf(path: string, mode?: RimRafMode, moveToPath?: string): Promise<void>;
48 async function rimraf(path: string, mode = RimRafMode.UNLINK, moveToPath?: string): Promise<void> {
49 if (isRootOrDriveLetter(path)) {
59 return rimrafMove(path, moveToPath);
60 }
61 > pfs.ts
62 async function rimrafMove(path: string, moveToPath = randomPath(tmpdir())): Promise<void> {
63 try {
80 }
81 }
82 > pfs.ts
83 async function rimrafUnlink(path: string): Promise<void> {
84 return fs.promises.rm(path, { recursive: true, force: true, maxRetries: 3 });
85 }
86 > pfs.ts
87 > //#endregion
88 >
89 > //#region readdir with NFC support (macos)
90 >
91 > export interface IDirent {
92 > name: string;
93 >
94 > isFile(): boolean;
95 > isDirectory(): boolean;
96 > isSymbolicLink(): boolean;
97 > }
98 >
99 > /**
100 > * Drop-in replacement of `fs.readdir` with support
101 > * for converting from macOS NFD unicon form to NFC
102 > * (https://github.com/nodejs/node/issues/2165)
103 > */
104 > async function readdir(path: string): Promise<string[]>;
105 > async function readdir(path: string, options: { withFileTypes: true }): Promise<IDirent[]>;
106 async function readdir(path: string, options?: { withFileTypes: true }): Promise<(string | IDirent)[]> {
107 try {
121 }
122 }
123 > pfs.ts
124 async function doReaddir(path: string, options?: { withFileTypes: true }): Promise<(string | IDirent)[]> {
125 return handleDirectoryChildren(await (options ? safeReaddirWithFileTypes(path) : fs.promises.readdir(path)));
126 }
127 > pfs.ts
128 async function safeReaddirWithFileTypes(path: string): Promise<IDirent[]> {
129 try {
170 return result;
171 }
172 > pfs.ts
173 > function handleDirectoryChildren(children: string[]): string[];
174 > function handleDirectoryChildren(children: IDirent[]): IDirent[];
175 > function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[];
176 function handleDirectoryChildren(children: (string | IDirent)[]): (string | IDirent)[] {
177 return children.map(child => {
189 });
190 }
191 > pfs.ts
192 > /**
193 > * A convenience method to read all children of a path that
194 > * are directories.
195 > */
196 async function readDirsInDir(dirPath: string): Promise<string[]> {
197 const children = await readdir(dirPath);
206 return directories;
207 }
208 > pfs.ts
209 > //#endregion
210 >
211 > //#region whenDeleted()
212 >
213 > /**
214 > * A `Promise` that resolves when the provided `path`
215 > * is deleted from disk.
216 > */
217 > export function whenDeleted(path: string, intervalMs = 1000): Promise<void> {
218 return new Promise<void>(resolve => {
219 let running = false;
233 });
234 }
235 > pfs.ts
236 > //#endregion
237 >
238 > //#region Methods with symbolic links support
239 >
240 > export namespace SymlinkSupport {
241 >
242 > export interface IStats {
243 >
244 > // The stats of the file. If the file is a symbolic
245 > // link, the stats will be of that target file and
246 > // not the link itself.
247 > // If the file is a symbolic link pointing to a non
248 > // existing file, the stat will be of the link and
249 > // the `dangling` flag will indicate this.
250 > stat: fs.Stats;
251 >
252 > // Will be provided if the resource is a symbolic link
253 > // on disk. Use the `dangling` flag to find out if it
254 > // points to a resource that does not exist on disk.
255 > symbolicLink?: { dangling: boolean };
256 > }
257 >
258 > /**
259 > * Resolves the `fs.Stats` of the provided path. If the path is a
260 > * symbolic link, the `fs.Stats` will be from the target it points
261 > * to. If the target does not exist, `dangling: true` will be returned
262 > * as `symbolicLink` value.
263 > */
264 > export async function stat(path: string): Promise<IStats> {
265
266 // First stat the link
313 }
314 }
315 > pfs.ts
316 > /**
317 > * Figures out if the `path` exists and is a file with support
318 > * for symlinks.
319 > *
320 > * Note: this will return `false` for a symlink that exists on
321 > * disk but is dangling (pointing to a nonexistent path).
322 > *
323 > * Use `exists` if you only care about the path existing on disk
324 > * or not without support for symbolic links.
325 > */
326 > export async function existsFile(path: string): Promise<boolean> {
327 try {
328 const { stat, symbolicLink } = await SymlinkSupport.stat(path);
335 return false;
336 }
337 > pfs.ts
338 > /**
339 > * Figures out if the `path` exists and is a directory with support for
340 > * symlinks.
341 > *
342 > * Note: this will return `false` for a symlink that exists on
343 > * disk but is dangling (pointing to a nonexistent path).
344 > *
345 > * Use `exists` if you only care about the path existing on disk
346 > * or not without support for symbolic links.
347 > */
348 > export async function existsDirectory(path: string): Promise<boolean> {
349 try {
350 const { stat, symbolicLink } = await SymlinkSupport.stat(path);
357 return false;
358 }
359 > } pfs.ts
360 >
361 > //#endregion
362 >
363 > //#region Write File
364 >
365 > // According to node.js docs (https://nodejs.org/docs/v14.16.0/api/fs.html#fs_fs_writefile_file_data_options_callback)
366 > // it is not safe to call writeFile() on the same path multiple times without waiting for the callback to return.
367 > // Therefor we use a Queue on the path that is given to us to sequentialize calls to the same path properly.
368 > const writeQueues = new ResourceQueue();
369 >
370 > /**
371 > * Same as `fs.writeFile` but with an additional call to
372 > * `fs.fdatasync` after writing to ensure changes are
373 > * flushed to disk.
374 > *
375 > * In addition, multiple writes to the same path are queued.
376 > */
377 > function writeFile(path: string, data: string, options?: IWriteFileOptions): Promise<void>;
378 > function writeFile(path: string, data: Buffer, options?: IWriteFileOptions): Promise<void>;
379 > function writeFile(path: string, data: Uint8Array, options?: IWriteFileOptions): Promise<void>;
380 > function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void>;
381 function writeFile(path: string, data: string | Buffer | Uint8Array, options?: IWriteFileOptions): Promise<void> {
382 return writeQueues.queueFor(URI.file(path), () => {
386 }, extUriBiasedIgnorePathCase);
387 }
388 > pfs.ts
389 > interface IWriteFileOptions {
390 > mode?: number;
391 > flag?: string;
392 > }
393 >
394 > interface IEnsuredWriteFileOptions extends IWriteFileOptions {
395 > mode: number;
396 > flag: string;
397 > }
398 >
399 > let canFlush = true;
400 > export function configureFlushOnWrite(enabled: boolean): void {
401 canFlush = enabled;
402 }
403 > pfs.ts
404 > // Calls fs.writeFile() followed by a fs.sync() call to flush the changes to disk
405 > // We do this in cases where we want to make sure the data is really on disk and
406 > // not in some cache.
407 > //
408 > // See https://github.com/nodejs/node/blob/v5.10.0/lib/fs.js#L1194
409 function doWriteFileAndFlush(path: string, data: string | Buffer | Uint8Array, options: IEnsuredWriteFileOptions, callback: (error: Error | null) => void): void {
410 if (!canFlush) {
440 });
441 }
442 > pfs.ts
443 > /**
444 > * Same as `fs.writeFileSync` but with an additional call to
445 > * `fs.fdatasyncSync` after writing to ensure changes are
446 > * flushed to disk.
447 > *
448 > * @deprecated always prefer async variants over sync!
449 > */
450 > export function writeFileSync(path: string, data: string | Buffer, options?: IWriteFileOptions): void {
451 const ensuredOptions = ensureWriteOptions(options);
452
474 }
475 }
476 > pfs.ts
477 function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptions {
478 if (!options) {
485 };
486 }
487 > pfs.ts
488 > //#endregion
489 >
490 > //#region Move / Copy
491 >
492 > /**
493 > * A drop-in replacement for `fs.rename` that:
494 > * - allows to move across multiple disks
495 > * - attempts to retry the operation for certain error codes on Windows
496 > */
497 async function rename(source: string, target: string, windowsRetryTimeout: number | false = 60000): Promise<void> {
498 if (source === target) {
525 }
526 }
527 > pfs.ts
528 async function renameWithRetry(source: string, target: string, startTime: number, retryTimeout: number, attempt = 0): Promise<void> {
529 try {
563 }
564 }
565 > pfs.ts
566 > interface ICopyPayload {
567 > readonly root: { source: string; target: string };
568 > readonly options: { preserveSymlinks: boolean };
569 > readonly handledSourcePaths: Set<string>;
570 > }
571 >
572 > /**
573 > * Recursively copies all of `source` to `target`.
574 > *
575 > * The options `preserveSymlinks` configures how symbolic
576 > * links should be handled when encountered. Set to
577 > * `false` to not preserve them and `true` otherwise.
578 > */
579 async function copy(source: string, target: string, options: { preserveSymlinks: boolean }): Promise<void> {
580 return doCopy(source, target, { root: { source, target }, options, handledSourcePaths: new Set<string>() });
581 }
582 > pfs.ts
583 > // When copying a file or folder, we want to preserve the mode
584 > // it had and as such provide it when creating. However, modes
585 > // can go beyond what we expect (see link below), so we mask it.
586 > // (https://github.com/nodejs/node-v0.x-archive/issues/3045#issuecomment-4862588)
587 > const COPY_MODE_MASK = 0o777;
588 >
589 async function doCopy(source: string, target: string, payload: ICopyPayload): Promise<void> {
590
626 }
627 }
628 > pfs.ts
629 async function doCopyDirectory(source: string, target: string, mode: number, payload: ICopyPayload): Promise<void> {
630
638 }
639 }
640 > pfs.ts
641 async function doCopyFile(source: string, target: string, mode: number): Promise<void> {
642
647 await fs.promises.chmod(target, mode);
648 }
649 > pfs.ts
650 async function doCopySymlink(source: string, target: string, payload: ICopyPayload): Promise<void> {
651
664 await fs.promises.symlink(linkTarget, target);
665 }
666 > pfs.ts
667 > //#endregion
668 >
669 > //#region Path resolvers
670 >
671 > /**
672 > * Given an absolute, normalized, and existing file path 'realcase' returns the
673 > * exact path that the file has on disk.
674 > * On a case insensitive file system, the returned path might differ from the original
675 > * path by character casing.
676 > * On a case sensitive file system, the returned path will always be identical to the
677 > * original path.
678 > * In case of errors, null is returned. But you cannot use this function to verify that
679 > * a path exists.
680 > *
681 > * realcase does not handle '..' or '.' path segments and it does not take the locale into account.
682 > */
683 export async function realcase(path: string, token?: CancellationToken): Promise<string | null> {
684 if (isLinux) {
724 return null;
725 }
726 > pfs.ts
727 async function realpath(path: string): Promise<string> {
728 try {
746 }
747 }
748 > pfs.ts
749 > /**
750 > * @deprecated always prefer async variants over sync!
751 > */
752 > export function realpathSync(path: string): string {
753 try {
754 return fs.realpathSync(path);
767 }
768 }
769 > pfs.ts
770 function normalizePath(path: string): string {
771 return rtrim(normalize(path), sep);
772 }
773 > pfs.ts
774 > //#endregion
775 >
776 > //#region Promise based fs methods
777 >
778 > /**
779 > * Some low level `fs` methods provided as `Promises` similar to
780 > * `fs.promises` but with notable differences, either implemented
781 > * by us or by restoring the original callback based behavior.
782 > *
783 > * At least `realpath` is implemented differently in the promise
784 > * based implementation compared to the callback based one. The
785 > * promise based implementation actually calls `fs.realpath.native`.
786 > * (https://github.com/microsoft/vscode/issues/118562)
787 > */
788 > export const Promises = new class {
789 >
790 > //#region Implemented by node.js
791 >
792 > get read() {
793
794 // Not using `promisify` here for a reason: the return
808 };
809 }
810 > pfs.ts
811 > get write() {
812
813 // Not using `promisify` here for a reason: the return
827 };
828 }
829 > pfs.ts
830 > get fdatasync() { return promisify(fs.fdatasync); } // not exposed as API in 22.x yet
831 >
832 > get open() { return promisify(fs.open); } // changed to return `FileHandle` in promise API
833 > get close() { return promisify(fs.close); } // not exposed as API due to the `FileHandle` return type of `open`
834 >
835 > get ftruncate() { return promisify(fs.ftruncate); } // not exposed as API in 22.x yet
836 >
837 > //#endregion
838 >
839 > //#region Implemented by us
840 >
841 > async exists(path: string): Promise<boolean> {
842 try {
843 await fs.promises.access(path);
848 }
849 }
850 > pfs.ts
851 > get readdir() { return readdir; }
852 > get readDirsInDir() { return readDirsInDir; }
853 >
854 > get writeFile() { return writeFile; }
855 >
856 > get rm() { return rimraf; }
857 >
858 > get rename() { return rename; }
859 > get copy() { return copy; }
860 >
861 > get realpath() { return realpath; } // `fs.promises.realpath` will use `fs.realpath.native` which we do not want
862 >
863 > //#endregion
864 > };
865 >
866 > //#endregion