Skip to content

Commit 9b3b9dd

Browse files
authored
feat: add "mode" for typescript reporter (#429)
To improve initial compilation time on babel-loader, we can now specify "write-tsbuildinfo" and "write-references" mode.
1 parent 2ba396d commit 9b3b9dd

14 files changed

+502
-291
lines changed

README.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,16 @@ Options passed to the plugin constructor will overwrite options from the cosmico
153153

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

156-
| Name | Type | Default value | Description |
157-
| -------------------- | --------- | ------------------------------------------------------------------------- | ----------- |
158-
| `enabled` | `boolean` | `true` | If `true`, it enables TypeScript checker. |
159-
| `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. |
160-
| `tsconfig` | `string` | `'tsconfig.json'` | Path to the `tsconfig.json` file (path relative to the `compiler.options.context` or absolute path) |
161-
| `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._ |
162-
| `compilerOptions` | `object` | `{ skipLibCheck: true, skipDefaultLibCheck: true }` | These options will overwrite compiler options from the `tsconfig.json` file. |
163-
| `diagnosticsOptions` | `object` | `{ syntactic: false, semantic: true, declaration: false, global: false }` | Settings to select which diagnostics do we want to perform. |
164-
| `extensions` | `object` | `{}` | See [TypeScript extensions options](#typescript-extensions-options). |
156+
| Name | Type | Default value | Description |
157+
| -------------------- | --------------------- | ------------------------------------------------------------------------- | ----------- |
158+
| `enabled` | `boolean` | `true` | If `true`, it enables TypeScript checker. |
159+
| `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. |
160+
| `tsconfig` | `string` | `'tsconfig.json'` | Path to the `tsconfig.json` file (path relative to the `compiler.options.context` or absolute path) |
161+
| `build` | `boolean` | `false` | If truthy, it's the equivalent of the `--build` flag for the `tsc` command. |
162+
| `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`. |
163+
| `compilerOptions` | `object` | `{ skipLibCheck: true, skipDefaultLibCheck: true }` | These options will overwrite compiler options from the `tsconfig.json` file. |
164+
| `diagnosticsOptions` | `object` | `{ syntactic: false, semantic: true, declaration: false, global: false }` | Settings to select which diagnostics do we want to perform. |
165+
| `extensions` | `object` | `{}` | See [TypeScript extensions options](#typescript-extensions-options). |
165166

166167
#### TypeScript extensions options
167168

src/ForkTsCheckerWebpackPluginOptions.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@
121121
"type": "boolean",
122122
"description": "The equivalent of the `--build` flag from the `tsc`."
123123
},
124+
"mode": {
125+
"type": "string",
126+
"enum": ["readonly", "write-tsbuildinfo", "write-references"],
127+
"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"
128+
},
124129
"incremental": {
125130
"type": "boolean",
126131
"description": "The equivalent of the `--incremental` flag from the `tsc`."

src/typescript-reporter/TypeScriptReporterConfiguration.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface TypeScriptReporterConfiguration {
1414
memoryLimit: number;
1515
tsconfig: string;
1616
build: boolean;
17+
mode: 'readonly' | 'write-tsbuildinfo' | 'write-references';
1718
compilerOptions: Partial<ts.CompilerOptions>;
1819
diagnosticOptions: TypeScriptDiagnosticsOptions;
1920
extensions: {
@@ -53,6 +54,7 @@ function createTypeScriptReporterConfiguration(
5354
enabled: options !== false,
5455
memoryLimit: 2048,
5556
build: false,
57+
mode: 'readonly',
5658
...(typeof options === 'object' ? options : {}),
5759
tsconfig: tsconfig,
5860
compilerOptions: compilerOptions,

src/typescript-reporter/TypeScriptReporterOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ type TypeScriptReporterOptions =
88
memoryLimit?: number;
99
tsconfig?: string;
1010
build?: boolean;
11+
mode?: 'readonly' | 'write-tsbuildinfo' | 'write-references';
1112
compilerOptions?: object;
1213
diagnosticOptions?: Partial<TypeScriptDiagnosticsOptions>;
1314
extensions?: {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// eslint-disable-next-line node/no-unsupported-features/node-builtins
2+
import { Dirent, Stats } from 'fs';
3+
4+
/**
5+
* Interface to abstract file system implementation details.
6+
*/
7+
interface FileSystem {
8+
// read
9+
exists(path: string): boolean;
10+
readFile(path: string, encoding?: string): string | undefined;
11+
readDir(path: string): Dirent[];
12+
readStats(path: string): Stats | undefined;
13+
realPath(path: string): string;
14+
normalizePath(path: string): string;
15+
16+
// write
17+
writeFile(path: string, data: string): void;
18+
deleteFile(path: string): void;
19+
createDir(path: string): void;
20+
updateTimes(path: string, atime: Date, mtime: Date): void;
21+
22+
// cache
23+
clearCache(): void;
24+
}
25+
26+
export { FileSystem };
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { dirname, normalize } from 'path';
2+
import { fs as mem } from 'memfs';
3+
import { FileSystem } from './FileSystem';
4+
// eslint-disable-next-line node/no-unsupported-features/node-builtins
5+
import { Dirent, Stats } from 'fs';
6+
7+
/**
8+
* It's an implementation of FileSystem interface which reads from the real file system, but write to the in-memory file system.
9+
*
10+
* @param caseSensitive
11+
* @param realFileSystem
12+
*/
13+
function createPassiveFileSystem(caseSensitive = false, realFileSystem: FileSystem): FileSystem {
14+
function normalizePath(path: string): string {
15+
return caseSensitive ? normalize(path) : normalize(path).toLowerCase();
16+
}
17+
18+
function memExists(path: string): boolean {
19+
return mem.existsSync(normalizePath(path));
20+
}
21+
22+
function memReadStats(path: string): Stats | undefined {
23+
return memExists(path) ? mem.statSync(normalizePath(path)) : undefined;
24+
}
25+
26+
function memReadFile(path: string, encoding?: string): string | undefined {
27+
if (memExists(path)) {
28+
return mem
29+
.readFileSync(normalizePath(path), { encoding: encoding as BufferEncoding })
30+
.toString();
31+
}
32+
}
33+
34+
function memReadDir(path: string): Dirent[] {
35+
if (memExists(path)) {
36+
return mem.readdirSync(normalizePath(path), { withFileTypes: true }) as Dirent[];
37+
}
38+
39+
return [];
40+
}
41+
42+
function exists(path: string) {
43+
return realFileSystem.exists(path) || memExists(path);
44+
}
45+
46+
function readFile(path: string, encoding?: string) {
47+
const fsStats = realFileSystem.readStats(path);
48+
const memStats = memReadStats(path);
49+
50+
if (fsStats && memStats) {
51+
return fsStats.mtimeMs > memStats.mtimeMs
52+
? realFileSystem.readFile(path, encoding)
53+
: memReadFile(path, encoding);
54+
} else if (fsStats) {
55+
return realFileSystem.readFile(path, encoding);
56+
} else if (memStats) {
57+
return memReadFile(path, encoding);
58+
}
59+
}
60+
61+
function readDir(path: string) {
62+
const fsDirents = realFileSystem.readDir(path);
63+
const memDirents = memReadDir(path);
64+
65+
// merge list of dirents from fs and mem
66+
return fsDirents
67+
.filter((fsDirent) => !memDirents.some((memDirent) => memDirent.name === fsDirent.name))
68+
.concat(memDirents);
69+
}
70+
71+
function readStats(path: string) {
72+
const fsStats = realFileSystem.readStats(path);
73+
const memStats = memReadStats(path);
74+
75+
if (fsStats && memStats) {
76+
return fsStats.mtimeMs > memStats.mtimeMs ? fsStats : memStats;
77+
} else if (fsStats) {
78+
return fsStats;
79+
} else if (memStats) {
80+
return memStats;
81+
}
82+
}
83+
84+
function createDir(path: string) {
85+
mem.mkdirSync(normalizePath(path), { recursive: true });
86+
}
87+
88+
function writeFile(path: string, data: string) {
89+
if (!memExists(dirname(path))) {
90+
createDir(dirname(path));
91+
}
92+
93+
mem.writeFileSync(normalizePath(path), data);
94+
}
95+
96+
function deleteFile(path: string) {
97+
if (memExists(path)) {
98+
mem.unlinkSync(normalizePath(path));
99+
}
100+
}
101+
102+
function updateTimes(path: string, atime: Date, mtime: Date) {
103+
if (memExists(path)) {
104+
mem.utimesSync(normalizePath(path), atime, mtime);
105+
}
106+
}
107+
108+
return {
109+
exists(path: string) {
110+
return exists(realFileSystem.realPath(path));
111+
},
112+
readFile(path: string, encoding?: string) {
113+
return readFile(realFileSystem.realPath(path), encoding);
114+
},
115+
readDir(path: string) {
116+
return readDir(realFileSystem.realPath(path));
117+
},
118+
readStats(path: string) {
119+
return readStats(realFileSystem.realPath(path));
120+
},
121+
realPath(path: string) {
122+
return realFileSystem.realPath(path);
123+
},
124+
normalizePath(path: string) {
125+
return normalizePath(path);
126+
},
127+
writeFile(path: string, data: string) {
128+
writeFile(realFileSystem.realPath(path), data);
129+
},
130+
deleteFile(path: string) {
131+
deleteFile(realFileSystem.realPath(path));
132+
},
133+
createDir(path: string) {
134+
createDir(realFileSystem.realPath(path));
135+
},
136+
updateTimes(path: string, atime: Date, mtime: Date) {
137+
updateTimes(realFileSystem.realPath(path), atime, mtime);
138+
},
139+
clearCache() {
140+
realFileSystem.clearCache();
141+
},
142+
};
143+
}
144+
145+
export { createPassiveFileSystem };

0 commit comments

Comments
 (0)