From 3a6cb6a5d21c11bebae723751911a5c8c690eb78 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Wed, 15 Jan 2020 20:21:58 +0000 Subject: [PATCH] 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 --- .../src/ngtsc/file_system/src/cached_file_system.ts | 4 ++-- .../src/ngtsc/file_system/src/invalid_file_system.ts | 2 +- .../src/ngtsc/file_system/src/node_js_file_system.ts | 4 +++- packages/compiler-cli/src/ngtsc/file_system/src/types.ts | 2 +- .../src/ngtsc/file_system/test/cached_file_system_spec.ts | 5 ++++- .../src/ngtsc/file_system/test/node_js_file_system_spec.ts | 5 ++++- .../src/ngtsc/file_system/testing/src/mock_file_system.ts | 6 +++++- 7 files changed, 20 insertions(+), 8 deletions(-) diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts index 00b057b3e7..ce9900194f 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/cached_file_system.ts @@ -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); } diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts index 11b2c8ab15..693f813af4 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/invalid_file_system.ts @@ -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(); } diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts index 9670504d6a..042c827dfd 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/node_js_file_system.ts @@ -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[]; } diff --git a/packages/compiler-cli/src/ngtsc/file_system/src/types.ts b/packages/compiler-cli/src/ngtsc/file_system/src/types.ts index eb3410d032..28f5defa84 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/types.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/types.ts @@ -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[]; diff --git a/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts b/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts index 401b9f1bdf..354373940d 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/test/cached_file_system_spec.ts @@ -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', () => { diff --git a/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts b/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts index c4f5161c3d..d17af51223 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/test/node_js_file_system_spec.ts @@ -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'}); }); }); diff --git a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts index d8bae1db87..7dcd81249d 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/testing/src/mock_file_system.ts @@ -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; }