Skip to content

Commit

Permalink
feat: add "mode" for typescript reporter (#429)
Browse files Browse the repository at this point in the history
To improve initial compilation time on babel-loader, we can now specify
"write-tsbuildinfo" and "write-references" mode.
  • Loading branch information
piotr-oles committed May 28, 2020
1 parent 2ba396d commit 9b3b9dd
Show file tree
Hide file tree
Showing 14 changed files with 502 additions and 291 deletions.
19 changes: 10 additions & 9 deletions README.md
Expand Up @@ -153,15 +153,16 @@ Options passed to the plugin constructor will overwrite options from the cosmico

Options for the TypeScript checker (`typescript` option object).

| Name | Type | Default value | Description |
| -------------------- | --------- | ------------------------------------------------------------------------- | ----------- |
| `enabled` | `boolean` | `true` | If `true`, it enables TypeScript checker. |
| `memoryLimit` | `number` | `2048` | Memory limit for the checker process in MB. If the process exits with the allocation failed error, try to increase this number. |
| `tsconfig` | `string` | `'tsconfig.json'` | Path to the `tsconfig.json` file (path relative to the `compiler.options.context` or absolute path) |
| `build` | `boolean` | `false` | The equivalent of the `--build` flag for the `tsc` command. To enable `incremental` mode, set it in the `tsconfig.json` file. _Note that this plugin doesn't emit any files even in the `build` mode._ |
| `compilerOptions` | `object` | `{ skipLibCheck: true, skipDefaultLibCheck: true }` | These options will overwrite compiler options from the `tsconfig.json` file. |
| `diagnosticsOptions` | `object` | `{ syntactic: false, semantic: true, declaration: false, global: false }` | Settings to select which diagnostics do we want to perform. |
| `extensions` | `object` | `{}` | See [TypeScript extensions options](#typescript-extensions-options). |
| Name | Type | Default value | Description |
| -------------------- | --------------------- | ------------------------------------------------------------------------- | ----------- |
| `enabled` | `boolean` | `true` | If `true`, it enables TypeScript checker. |
| `memoryLimit` | `number` | `2048` | Memory limit for the checker process in MB. If the process exits with the allocation failed error, try to increase this number. |
| `tsconfig` | `string` | `'tsconfig.json'` | Path to the `tsconfig.json` file (path relative to the `compiler.options.context` or absolute path) |
| `build` | `boolean` | `false` | If truthy, it's the equivalent of the `--build` flag for the `tsc` command. |
| `mode` | `'readonly'` or `'write-tsbuildinfo'` or `'write-references'` | `'readonly'` | If you use the `babel-loader`, it's recommended to use `write-references` mode to improve initial compilation time. If you use `ts-loader`, it's recommended to use `readonly` mode to not overwrite filed emitted by `ts-loader`. |
| `compilerOptions` | `object` | `{ skipLibCheck: true, skipDefaultLibCheck: true }` | These options will overwrite compiler options from the `tsconfig.json` file. |
| `diagnosticsOptions` | `object` | `{ syntactic: false, semantic: true, declaration: false, global: false }` | Settings to select which diagnostics do we want to perform. |
| `extensions` | `object` | `{}` | See [TypeScript extensions options](#typescript-extensions-options). |

#### TypeScript extensions options

Expand Down
5 changes: 5 additions & 0 deletions src/ForkTsCheckerWebpackPluginOptions.json
Expand Up @@ -121,6 +121,11 @@
"type": "boolean",
"description": "The equivalent of the `--build` flag from the `tsc`."
},
"mode": {
"type": "string",
"enum": ["readonly", "write-tsbuildinfo", "write-references"],
"description": "`readonly` keeps all emitted files in memory, `write-tsbuildinfo` which writes only .tsbuildinfo files and `write-references` which writes both .tsbuildinfo and referenced projects output"
},
"incremental": {
"type": "boolean",
"description": "The equivalent of the `--incremental` flag from the `tsc`."
Expand Down
2 changes: 2 additions & 0 deletions src/typescript-reporter/TypeScriptReporterConfiguration.ts
Expand Up @@ -14,6 +14,7 @@ interface TypeScriptReporterConfiguration {
memoryLimit: number;
tsconfig: string;
build: boolean;
mode: 'readonly' | 'write-tsbuildinfo' | 'write-references';
compilerOptions: Partial<ts.CompilerOptions>;
diagnosticOptions: TypeScriptDiagnosticsOptions;
extensions: {
Expand Down Expand Up @@ -53,6 +54,7 @@ function createTypeScriptReporterConfiguration(
enabled: options !== false,
memoryLimit: 2048,
build: false,
mode: 'readonly',
...(typeof options === 'object' ? options : {}),
tsconfig: tsconfig,
compilerOptions: compilerOptions,
Expand Down
1 change: 1 addition & 0 deletions src/typescript-reporter/TypeScriptReporterOptions.ts
Expand Up @@ -8,6 +8,7 @@ type TypeScriptReporterOptions =
memoryLimit?: number;
tsconfig?: string;
build?: boolean;
mode?: 'readonly' | 'write-tsbuildinfo' | 'write-references';
compilerOptions?: object;
diagnosticOptions?: Partial<TypeScriptDiagnosticsOptions>;
extensions?: {
Expand Down
26 changes: 26 additions & 0 deletions src/typescript-reporter/file-system/FileSystem.ts
@@ -0,0 +1,26 @@
// eslint-disable-next-line node/no-unsupported-features/node-builtins
import { Dirent, Stats } from 'fs';

/**
* Interface to abstract file system implementation details.
*/
interface FileSystem {
// read
exists(path: string): boolean;
readFile(path: string, encoding?: string): string | undefined;
readDir(path: string): Dirent[];
readStats(path: string): Stats | undefined;
realPath(path: string): string;
normalizePath(path: string): string;

// write
writeFile(path: string, data: string): void;
deleteFile(path: string): void;
createDir(path: string): void;
updateTimes(path: string, atime: Date, mtime: Date): void;

// cache
clearCache(): void;
}

export { FileSystem };
145 changes: 145 additions & 0 deletions src/typescript-reporter/file-system/PassiveFileSystem.ts
@@ -0,0 +1,145 @@
import { dirname, normalize } from 'path';
import { fs as mem } from 'memfs';
import { FileSystem } from './FileSystem';
// eslint-disable-next-line node/no-unsupported-features/node-builtins
import { Dirent, Stats } from 'fs';

/**
* It's an implementation of FileSystem interface which reads from the real file system, but write to the in-memory file system.
*
* @param caseSensitive
* @param realFileSystem
*/
function createPassiveFileSystem(caseSensitive = false, realFileSystem: FileSystem): FileSystem {
function normalizePath(path: string): string {
return caseSensitive ? normalize(path) : normalize(path).toLowerCase();
}

function memExists(path: string): boolean {
return mem.existsSync(normalizePath(path));
}

function memReadStats(path: string): Stats | undefined {
return memExists(path) ? mem.statSync(normalizePath(path)) : undefined;
}

function memReadFile(path: string, encoding?: string): string | undefined {
if (memExists(path)) {
return mem
.readFileSync(normalizePath(path), { encoding: encoding as BufferEncoding })
.toString();
}
}

function memReadDir(path: string): Dirent[] {
if (memExists(path)) {
return mem.readdirSync(normalizePath(path), { withFileTypes: true }) as Dirent[];
}

return [];
}

function exists(path: string) {
return realFileSystem.exists(path) || memExists(path);
}

function readFile(path: string, encoding?: string) {
const fsStats = realFileSystem.readStats(path);
const memStats = memReadStats(path);

if (fsStats && memStats) {
return fsStats.mtimeMs > memStats.mtimeMs
? realFileSystem.readFile(path, encoding)
: memReadFile(path, encoding);
} else if (fsStats) {
return realFileSystem.readFile(path, encoding);
} else if (memStats) {
return memReadFile(path, encoding);
}
}

function readDir(path: string) {
const fsDirents = realFileSystem.readDir(path);
const memDirents = memReadDir(path);

// merge list of dirents from fs and mem
return fsDirents
.filter((fsDirent) => !memDirents.some((memDirent) => memDirent.name === fsDirent.name))
.concat(memDirents);
}

function readStats(path: string) {
const fsStats = realFileSystem.readStats(path);
const memStats = memReadStats(path);

if (fsStats && memStats) {
return fsStats.mtimeMs > memStats.mtimeMs ? fsStats : memStats;
} else if (fsStats) {
return fsStats;
} else if (memStats) {
return memStats;
}
}

function createDir(path: string) {
mem.mkdirSync(normalizePath(path), { recursive: true });
}

function writeFile(path: string, data: string) {
if (!memExists(dirname(path))) {
createDir(dirname(path));
}

mem.writeFileSync(normalizePath(path), data);
}

function deleteFile(path: string) {
if (memExists(path)) {
mem.unlinkSync(normalizePath(path));
}
}

function updateTimes(path: string, atime: Date, mtime: Date) {
if (memExists(path)) {
mem.utimesSync(normalizePath(path), atime, mtime);
}
}

return {
exists(path: string) {
return exists(realFileSystem.realPath(path));
},
readFile(path: string, encoding?: string) {
return readFile(realFileSystem.realPath(path), encoding);
},
readDir(path: string) {
return readDir(realFileSystem.realPath(path));
},
readStats(path: string) {
return readStats(realFileSystem.realPath(path));
},
realPath(path: string) {
return realFileSystem.realPath(path);
},
normalizePath(path: string) {
return normalizePath(path);
},
writeFile(path: string, data: string) {
writeFile(realFileSystem.realPath(path), data);
},
deleteFile(path: string) {
deleteFile(realFileSystem.realPath(path));
},
createDir(path: string) {
createDir(realFileSystem.realPath(path));
},
updateTimes(path: string, atime: Date, mtime: Date) {
updateTimes(realFileSystem.realPath(path), atime, mtime);
},
clearCache() {
realFileSystem.clearCache();
},
};
}

export { createPassiveFileSystem };

0 comments on commit 9b3b9dd

Please sign in to comment.