diff --git a/packages/compiler-cli/src/ngtsc/file_system/README.md b/packages/compiler-cli/src/ngtsc/file_system/README.md index 06a29acf08..bce731dd07 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/README.md +++ b/packages/compiler-cli/src/ngtsc/file_system/README.md @@ -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 diff --git a/packages/compiler-cli/src/ngtsc/file_system/index.ts b/packages/compiler-cli/src/ngtsc/file_system/index.ts index 218f99de4b..7b25db8e8e 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/index.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/index.ts @@ -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'; 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 bdc660e301..6720de0b45 100644 --- a/packages/compiler-cli/src/ngtsc/file_system/src/types.ts +++ b/packages/compiler-cli/src/ngtsc/file_system/src/types.ts @@ -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(file: T): T; + extname(path: AbsoluteFsPath|PathSegment): string; join(basePath: T, ...paths: string[]): T; /** * Compute the relative path between `from` and `to`. @@ -66,9 +47,41 @@ export interface FileSystem { */ relative(from: T, to: T): PathSegment|AbsoluteFsPath; basename(filePath: string, extension?: string): PathSegment; + normalize(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(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;