refactor(compiler-cli): split the FileSystem interface up ()

This interface now extends `ReadonlyFileSystem` which in turn
extends `PathManipulation`. This means consumers of these
interfaces can be more specific about what is needed, and so
providers do not need to implement unnecessary methods.

PR Close 
This commit is contained in:
Pete Bacon Darwin 2020-12-30 16:59:30 +00:00 committed by Andrew Scott
parent da6c739bb6
commit 80b1ba9f95
3 changed files with 51 additions and 25 deletions
packages/compiler-cli/src/ngtsc/file_system

@ -3,7 +3,15 @@
To improve cross platform support, all file access (and path manipulation)
is now done through a well known interface (`FileSystem`).
For testing a number of `MockFileSystem` implementations are supplied.
Note that `FileSystem` extends `ReadonlyFileSystem`, which itself extends
`PathManipulation`.
If you are using a file-system object you should only ask for the type that supports
all the methods that you require.
For example, if you have a function (`foo()`) that only needs to resolve paths then
it should only require `PathManipulation`: `foo(fs: PathManipulation)`.
This allows the caller to avoid implementing unneeded functionality.
For testing, a number of `MockFileSystem` implementations are supplied.
These provide an in-memory file-system which emulates operating systems
like OS/X, Unix and Windows.
@ -16,6 +24,11 @@ To prevent this happening accidentally the current file system always starts out
as an instance of `InvalidFileSystem`, which will throw an error if any of its
methods are called.
Generally it is safer to explicitly pass file-system objects to constructors or
free-standing functions if possible. This avoids confusing bugs where the
global file-system has not been set-up correctly before calling functions that
expect there to be a file-system configured globally.
You can set the current file-system by calling `setFileSystem()`.
During testing you can call the helper function `initMockFileSystem(os)`
which takes a string name of the OS to emulate, and will also monkey-patch

@ -9,5 +9,5 @@ export {NgtscCompilerHost} from './src/compiler_host';
export {absoluteFrom, absoluteFromSourceFile, basename, dirname, getFileSystem, isLocalRelativePath, isRoot, isRooted, join, relative, relativeFrom, resolve, setFileSystem, toRelativeImport} from './src/helpers';
export {LogicalFileSystem, LogicalProjectPath} from './src/logical';
export {NodeJSFileSystem} from './src/node_js_file_system';
export {AbsoluteFsPath, FileStats, FileSystem, PathSegment, PathString} from './src/types';
export {AbsoluteFsPath, FileStats, FileSystem, PathManipulation, PathSegment, PathString, ReadonlyFileSystem} from './src/types';
export {getSourceFileOrError} from './src/util';

@ -29,33 +29,14 @@ export type AbsoluteFsPath = BrandedPath<'AbsoluteFsPath'>;
export type PathSegment = BrandedPath<'PathSegment'>;
/**
* A basic interface to abstract the underlying file-system.
*
* This makes it easier to provide mock file-systems in unit tests,
* but also to create clever file-systems that have features such as caching.
* An abstraction over the path manipulation aspects of a file-system.
*/
export interface FileSystem {
exists(path: AbsoluteFsPath): boolean;
readFile(path: AbsoluteFsPath): string;
readFileBuffer(path: AbsoluteFsPath): Uint8Array;
writeFile(path: AbsoluteFsPath, data: string|Uint8Array, exclusive?: boolean): void;
removeFile(path: AbsoluteFsPath): void;
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void;
readdir(path: AbsoluteFsPath): PathSegment[];
lstat(path: AbsoluteFsPath): FileStats;
stat(path: AbsoluteFsPath): FileStats;
pwd(): AbsoluteFsPath;
chdir(path: AbsoluteFsPath): void;
export interface PathManipulation {
extname(path: AbsoluteFsPath|PathSegment): string;
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
ensureDir(path: AbsoluteFsPath): void;
removeDeep(path: AbsoluteFsPath): void;
isCaseSensitive(): boolean;
isRoot(path: AbsoluteFsPath): boolean;
isRooted(path: string): boolean;
resolve(...paths: string[]): AbsoluteFsPath;
dirname<T extends PathString>(file: T): T;
extname(path: AbsoluteFsPath|PathSegment): string;
join<T extends PathString>(basePath: T, ...paths: string[]): T;
/**
* Compute the relative path between `from` and `to`.
@ -66,9 +47,41 @@ export interface FileSystem {
*/
relative<T extends PathString>(from: T, to: T): PathSegment|AbsoluteFsPath;
basename(filePath: string, extension?: string): PathSegment;
normalize<T extends PathString>(path: T): T;
resolve(...paths: string[]): AbsoluteFsPath;
pwd(): AbsoluteFsPath;
chdir(path: AbsoluteFsPath): void;
}
/**
* An abstraction over the read-only aspects of a file-system.
*/
export interface ReadonlyFileSystem extends PathManipulation {
isCaseSensitive(): boolean;
exists(path: AbsoluteFsPath): boolean;
readFile(path: AbsoluteFsPath): string;
readFileBuffer(path: AbsoluteFsPath): Uint8Array;
readdir(path: AbsoluteFsPath): PathSegment[];
lstat(path: AbsoluteFsPath): FileStats;
stat(path: AbsoluteFsPath): FileStats;
realpath(filePath: AbsoluteFsPath): AbsoluteFsPath;
getDefaultLibLocation(): AbsoluteFsPath;
normalize<T extends PathString>(path: T): T;
}
/**
* A basic interface to abstract the underlying file-system.
*
* This makes it easier to provide mock file-systems in unit tests,
* but also to create clever file-systems that have features such as caching.
*/
export interface FileSystem extends ReadonlyFileSystem {
writeFile(path: AbsoluteFsPath, data: string|Uint8Array, exclusive?: boolean): void;
removeFile(path: AbsoluteFsPath): void;
symlink(target: AbsoluteFsPath, path: AbsoluteFsPath): void;
copyFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
moveFile(from: AbsoluteFsPath, to: AbsoluteFsPath): void;
ensureDir(path: AbsoluteFsPath): void;
removeDeep(path: AbsoluteFsPath): void;
}
export type PathString = string|AbsoluteFsPath|PathSegment;