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 { writeFile(path: AbsoluteFsPath, data: string, exclusive?: boolean): void {
this.delegate.writeFile(path, data); this.delegate.writeFile(path, data, exclusive);
this.readFileCache.set(path, data); this.readFileCache.set(path, data);
this.existsCache.set(path, true); this.existsCache.set(path, true);
} }

View File

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

View File

@ -18,7 +18,9 @@ export class NodeJSFileSystem implements FileSystem {
private _caseSensitive: boolean|undefined = undefined; private _caseSensitive: boolean|undefined = undefined;
exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); } exists(path: AbsoluteFsPath): boolean { return fs.existsSync(path); }
readFile(path: AbsoluteFsPath): string { return fs.readFileSync(path, 'utf8'); } 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); } removeFile(path: AbsoluteFsPath): void { fs.unlinkSync(path); }
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { fs.symlinkSync(target, path); } symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void { fs.symlinkSync(target, path); }
readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; } readdir(path: AbsoluteFsPath): PathSegment[] { return fs.readdirSync(path) as PathSegment[]; }

View File

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

View File

@ -94,7 +94,10 @@ describe('CachedFileSystem', () => {
it('should call delegate', () => { it('should call delegate', () => {
const spy = spyOn(delegate, 'writeFile'); const spy = spyOn(delegate, 'writeFile');
fs.writeFile(abcPath, 'Some contents'); 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', () => { it('should update the exists and "readFile" caches', () => {

View File

@ -47,7 +47,10 @@ describe('NodeJSFileSystem', () => {
it('should delegate to fs.writeFileSync()', () => { it('should delegate to fs.writeFileSync()', () => {
const spy = spyOn(realFs, 'writeFileSync'); const spy = spyOn(realFs, 'writeFileSync');
fs.writeFile(abcPath, 'Some contents'); 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 [folderPath, basename] = this.splitIntoFolderAndFile(path);
const {entity} = this.findFromPath(folderPath); const {entity} = this.findFromPath(folderPath);
if (entity === null || !isFolder(entity)) { if (entity === null || !isFolder(entity)) {
throw new MockFileSystemError( throw new MockFileSystemError(
'ENOENT', path, `Unable to write file "${path}". The containing folder does not exist.`); '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; entity[basename] = data;
} }