refactor(ivy): add exclusive mode to `writeFile()` (#34722)

This commit adds an `exclusive` parameter to the
`FileSystem.writeFile()` method. When this parameter is
true, the method will fail with an `EEXIST` error if the
file already exists on disk.

PR Close #34722
This commit is contained in:
Pete Bacon Darwin 2020-01-15 20:21:58 +00:00 committed by Andrew Kushnir
parent ecbc25044c
commit 3a6cb6a5d2
7 changed files with 20 additions and 8 deletions

View File

@ -48,8 +48,8 @@ export class CachedFileSystem implements FileSystem {
}
}
writeFile(path: AbsoluteFsPath, data: string): void {
this.delegate.writeFile(path, data);
writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void {
this.delegate.writeFile(path, data, exclusive);
this.readFileCache.set(path, data);
this.existsCache.set(path, true);
}

View File

@ -18,7 +18,7 @@ import {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './
export class InvalidFileSystem implements FileSystem {
exists(path: AbsoluteFsPath): boolean { throw makeError(); }
readFile(path: AbsoluteFsPath): string { throw makeError(); }
writeFile(path: AbsoluteFsPath, data: string): void { throw makeError(); }
writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void { throw makeError(); }
removeFile(path: AbsoluteFsPath): void { throw makeError(); }
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { throw makeError(); }
readdir(path: AbsoluteFsPath): PathSegment[] { throw makeError(); }

View File

@ -18,7 +18,9 @@ export class NodeJSFileSystem implements FileSystem {
private _caseSensitive: boolean|undefined = undefined;
exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); }
readFile(path: AbsoluteFsPath): string { return fs.readFileSync(path, 'utf8'); }
writeFile(path: AbsoluteFsPath, data: string): void { fs.writeFileSync(path, data, 'utf8'); }
writeFile(path: AbsoluteFsPath, data: string, exclusive: boolean = false): void {
fs.writeFileSync(path, data, exclusive ? {flag: 'wx'} : undefined);
}
removeFile(path: AbsoluteFsPath): void { fs.unlinkSync(path); }
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { fs.symlinkSync(target, path); }
readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; }

View File

@ -37,7 +37,7 @@ export type PathSegment = BrandedPath<'PathSegment'>;
export interface FileSystem {
exists(path: AbsoluteFsPath): boolean;
readFile(path: AbsoluteFsPath): string;
writeFile(path: AbsoluteFsPath, data: string): void;
writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void;
removeFile(path: AbsoluteFsPath): void;
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void;
readdir(path: AbsoluteFsPath): PathSegment[];

View File

@ -94,7 +94,10 @@ describe('CachedFileSystem', () => {
it('should call delegate', () => {
const spy = spyOn(delegate, 'writeFile');
fs.writeFile(abcPath, 'Some contents');
expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents');
expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents', undefined);
spy.calls.reset();
fs.writeFile(abcPath, 'Some contents', /* exclusive */ true);
expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents', true);
});
it('should update the exists and "readFile" caches', () => {

View File

@ -47,7 +47,10 @@ describe('NodeJSFileSystem', () => {
it('should delegate to fs.writeFileSync()', () => {
const spy = spyOn(realFs, 'writeFileSync');
fs.writeFile(abcPath, 'Some contents');
expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents', 'utf8');
expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents', undefined);
spy.calls.reset();
fs.writeFile(abcPath, 'Some contents', /* exclusive */ true);
expect(spy).toHaveBeenCalledWith(abcPath, 'Some contents', {flag: 'wx'});
});
});

View File

@ -34,13 +34,17 @@ export abstract class MockFileSystem implements FileSystem {
}
}
writeFile(path: AbsoluteFsPath, data: string): void {
writeFile(path: AbsoluteFsPath, data: string, exclusive: boolean = false): void {
const [folderPath, basename] = this.splitIntoFolderAndFile(path);
const {entity} = this.findFromPath(folderPath);
if (entity === null || !isFolder(entity)) {
throw new MockFileSystemError(
'ENOENT', path, `Unable to write file "${path}". The containing folder does not exist.`);
}
if (exclusive && entity[basename] !== undefined) {
throw new MockFileSystemError(
'EEXIST', path, `Unable to exclusively write file "${path}". The file already exists.`);
}
entity[basename] = data;
}