All files / PARSE/1-Discover ReadOnceFileSystem.ts

94.73% Statements 18/19
100% Branches 2/2
92.3% Functions 12/13
94.44% Lines 17/18

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82                                        496x   496x     588x 588x 523x 523x 523x       29428x       469x       742x       78x               1x       1x       1x       1x       1x       5x                
import type IFileSystem from "../../types/IFileSystem";
import invariant from "../../utils/invariant";
 
/**
 * One run's read-only view of the host port, in which each file's text is read
 * once (#1444, owner ruling 3).
 *
 * 1.1 Discover reads through it, so every part of discovery that asks for a
 * file sees the same text. `platformio.ini` was read once per directory that
 * resolved includes, for `lib_extra_dirs`, and then again in Stage 3 for
 * ADR-049's target. A save between those reads gave discovery one version of
 * the file and the target another. Discovery now parses the project from
 * this view too, so the target is read from the same text.
 *
 * Read-only, because 1.1 changes nothing on disk: 3.1 Write owns every change
 * (#1653). A view that forwarded the mutating methods would be one more module
 * able to write, so each refuses instead, and so does the preprocessor's
 * scratch file, which 1.1 never needs.
 */
class ReadOnceFileSystem implements IFileSystem {
  private readonly texts = new Map<string, string>();
 
  constructor(private readonly host: IFileSystem) {}
 
  readFile(path: string): string {
    const known = this.texts.get(path);
    if (known !== undefined) return known;
    const text = this.host.readFile(path);
    this.texts.set(path, text);
    return text;
  }
 
  exists(path: string): boolean {
    return this.host.exists(path);
  }
 
  isDirectory(path: string): boolean {
    return this.host.isDirectory(path);
  }
 
  isFile(path: string): boolean {
    return this.host.isFile(path);
  }
 
  readdir(path: string): string[] {
    return this.host.readdir(path);
  }
 
  stat(path: string): { mtimeMs: number } {
    return this.host.stat(path);
  }
 
  writeFile(path: string): void {
    ReadOnceFileSystem.refuse("write", path);
  }
 
  mkdir(path: string): void {
    ReadOnceFileSystem.refuse("create the directory", path);
  }
 
  unlink(path: string): void {
    ReadOnceFileSystem.refuse("delete", path);
  }
 
  rename(from: string): void {
    ReadOnceFileSystem.refuse("rename", from);
  }
 
  withTempFile<T>(name: string): Promise<T> {
    return ReadOnceFileSystem.refuse("create the scratch file", name);
  }
 
  private static refuse(what: string, path: string): never {
    invariant(
      false,
      `1.1 Discover changes nothing on disk (asked to ${what} ${path})`,
    );
  }
}
 
export default ReadOnceFileSystem;