Skip to content

Commit dfd8933

Browse files
arcanisjohnnyreilly
authored andcommitted
Adds support for custom resolvers to CompilerHost (TypeStrong#250) - fixes TypeStrong#181
* Adds support for custom compilers to CompilerHost * Attaches the options to the plugin * Adds support for IncrementalChecker * Adds a test * Updates the README * Fixes undefined env values on Node 8 * Bumps version * Avoids any
1 parent bbbbfce commit dfd8933

17 files changed

+366
-58
lines changed

CHANGELOG.md

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## v1.1.0
2+
3+
* [Add new custom resolution options](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/250)
4+
15
## v1.0.4
26

37
* [gracefully handle error thrown from the service](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/249)
@@ -8,7 +12,7 @@
812

913
## v1.0.2
1014

11-
* [Fix ignoreLintWarning mark warnings as errors](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/243)
15+
* [Fix ignoreLintWarning mark warnings as errors](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/243)
1216

1317
## v1.0.1
1418

@@ -20,7 +24,7 @@
2024

2125
This is the first major version of `fork-ts-checker-webpack-plugin`. A long time coming :-)
2226

23-
There are actually no breaking changes that we're aware of; users of 0.x `fork-ts-checker-webpack-plugin` should be be able to upgrade without any drama. Users of TypeScript 3+ may notice a performance improvement as by default the plugin now uses the [incremental watch API](https://github.com/Microsoft/TypeScript/pull/20234) in TypeScript. Should this prove problematic you can opt out of using it by supplying `useTypescriptIncrementalApi: false`.
27+
There are actually no breaking changes that we're aware of; users of 0.x `fork-ts-checker-webpack-plugin` should be be able to upgrade without any drama. Users of TypeScript 3+ may notice a performance improvement as by default the plugin now uses the [incremental watch API](https://github.com/Microsoft/TypeScript/pull/20234) in TypeScript. Should this prove problematic you can opt out of using it by supplying `useTypescriptIncrementalApi: false`.
2428

2529
We are aware of an [issue with Vue and the incremental API](https://github.com/Realytics/fork-ts-checker-webpack-plugin/issues/219). We hope it will be fixed soon - a generous member of the community is taking a look. In the meantime, we will *not* default to using the incremental watch API when in Vue mode.
2630

@@ -79,7 +83,7 @@ Version `1.x` additionally supports webpack 5 alongside webpack 4, whose hooks a
7983
```diff
8084
- compiler.hooks.forkTsCheckerDone.tap(...args)
8185
+ const forkTsCheckerHooks = ForkTsCheckerWebpackPlugin.getCompilerHooks(compiler)
82-
+ forkTsCheckerHooks.done.tap(...args)
86+
+ forkTsCheckerHooks.done.tap(...args)
8387
```
8488

8589
v1.0.0-alpha.0 drops support for node 6.

README.md

+47-28
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![Build Status](https://travis-ci.org/Realytics/fork-ts-checker-webpack-plugin.svg?branch=master)](https://travis-ci.org/Realytics/fork-ts-checker-webpack-plugin)
44

55
Webpack plugin that runs typescript type checker on a separate process.
6-
6+
77
## Installation
88
This plugin requires minimum **webpack 2.3**, **typescript 2.1** and optionally **tslint 4.0**
99
```sh
@@ -23,7 +23,7 @@ var webpackConfig = {
2323
loader: 'ts-loader',
2424
options: {
2525
// disable type checker - we will use it in fork plugin
26-
transpileOnly: true
26+
transpileOnly: true
2727
}
2828
}
2929
]
@@ -39,21 +39,21 @@ There is already similar solution - [awesome-typescript-loader](https://github.c
3939
add `CheckerPlugin` and delegate checker to the separate process. The problem with `awesome-typescript-loader` was that, in our case,
4040
it was a lot slower than [ts-loader](https://github.com/TypeStrong/ts-loader) on an incremental build (~20s vs ~3s).
4141
Secondly, we use [tslint](https://palantir.github.io/tslint) and we wanted to run this, along with type checker, in a separate process.
42-
This is why we've created this plugin. To provide better performance, plugin reuses Abstract Syntax Trees between compilations and shares
42+
This is why we've created this plugin. To provide better performance, plugin reuses Abstract Syntax Trees between compilations and shares
4343
these trees with tslint. It can be scaled with a multi-process mode to utilize maximum CPU power.
4444

4545
## Modules resolution
46-
It's very important to be aware that **this plugin uses [typescript](https://github.com/Microsoft/TypeScript)'s, not
47-
[webpack](https://github.com/webpack/webpack)'s modules resolution**. It means that you have to setup `tsconfig.json` correctly. For example
48-
if you set `files: ['./src/someFile.ts']` in `tsconfig.json`, this plugin will check only `someFile.ts` for semantic errors. It's because
49-
of performance. The goal of this plugin is to be *as fast as possible*. With typescript's module resolution we don't have to wait for webpack
46+
It's very important to be aware that **this plugin uses [typescript](https://github.com/Microsoft/TypeScript)'s, not
47+
[webpack](https://github.com/webpack/webpack)'s modules resolution**. It means that you have to setup `tsconfig.json` correctly. For example
48+
if you set `files: ['./src/someFile.ts']` in `tsconfig.json`, this plugin will check only `someFile.ts` for semantic errors. It's because
49+
of performance. The goal of this plugin is to be *as fast as possible*. With typescript's module resolution we don't have to wait for webpack
5050
to compile files (which traverses dependency graph during compilation) - we have a full list of files from the begin.
5151

5252
To debug typescript's modules resolution, you can use `tsc --traceResolution` command.
5353

5454
## TSLint
55-
If you have installed [tslint](https://palantir.github.io/tslint), you can enable it by setting `tslint: true` or
56-
`tslint: './path/to/tslint.json'`. We recommend changing `defaultSeverity` to a `"warning"` in `tslint.json` file.
55+
If you have installed [tslint](https://palantir.github.io/tslint), you can enable it by setting `tslint: true` or
56+
`tslint: './path/to/tslint.json'`. We recommend changing `defaultSeverity` to a `"warning"` in `tslint.json` file.
5757
It helps to distinguish lints from typescript's diagnostics.
5858

5959
## Options
@@ -63,7 +63,7 @@ Path to *tsconfig.json* file. Default: `path.resolve(compiler.options.context, '
6363
* **compilerOptions** `object`:
6464
Allows overriding TypeScript options. Should be specified in the same format as you would do for the `compilerOptions` property in tsconfig.json. Default: `{}`.
6565

66-
* **tslint** `string | true | undefined`:
66+
* **tslint** `string | true | undefined`:
6767
- If `string`, path to *tslint.json* file to check source files against.
6868
- If `true`, path to `tslint.json` file will be computed with respect to currently checked file, just like TSLint
6969
CLI would do. Suppose you have a project:
@@ -81,13 +81,13 @@ Allows overriding TypeScript options. Should be specified in the same format as
8181
```
8282
In such a case `src/file.ts` and `src/anotherFile.ts` would be checked against root `tslint.json`, and
8383
`src/lib/someHelperFile.ts` would be checked against `src/lib/tslint.json`.
84-
84+
8585
Default: `undefined`.
8686
8787
* **tslintAutoFix** `boolean `:
8888
Passes on `--fix` flag while running `tslint` to auto fix linting errors. Default: false.
8989
90-
* **watch** `string | string[]`:
90+
* **watch** `string | string[]`:
9191
Directories or files to watch by service. Not necessary but improves performance (reduces number of `fs.stat` calls).
9292
9393
* **async** `boolean`:
@@ -97,13 +97,13 @@ We recommend to set this to `false` in projects where type checking is faster th
9797
* **ignoreDiagnostics** `number[]`:
9898
List of typescript diagnostic codes to ignore.
9999
100-
* **ignoreLints** `string[]`:
100+
* **ignoreLints** `string[]`:
101101
List of tslint rule names to ignore.
102102
103103
* **ignoreLintWarnings** `boolean`:
104104
If true, will ignore all lint warnings.
105105
106-
* **reportFiles** `string[]`:
106+
* **reportFiles** `string[]`:
107107
Only report errors on files matching these glob patterns. This can be useful when certain types definitions have errors that are not fatal to your application. Default: `[]`. Please note that this may behave unexpectedly if using the incremental API as the incremental API doesn't look for global and semantic errors [if it has already found syntactic errors](https://github.com/Microsoft/TypeScript/blob/89386ddda7dafc63cb35560e05412487f47cc267/src/compiler/watch.ts#L141).
108108
109109
```js
@@ -127,25 +127,25 @@ Options passed to formatters (currently only `codeframe` - see [available option
127127
* **silent** `boolean`:
128128
If `true`, logger will not be used. Default: `false`.
129129

130-
* **checkSyntacticErrors** `boolean`:
130+
* **checkSyntacticErrors** `boolean`:
131131
This option is useful if you're using ts-loader in `happyPackMode` with [HappyPack](https://github.com/amireh/happypack) or [thread-loader](https://github.com/webpack-contrib/thread-loader) to parallelise your builds. If `true` it will ensure that the plugin checks for *both* syntactic errors (eg `const array = [{} {}];`) and semantic errors (eg `const x: number = '1';`). By default the plugin only checks for semantic errors. This is because when ts-loader is used in `transpileOnly` mode, ts-loader will still report syntactic errors. When used in `happyPackMode` it does not. Default: `false`.
132132

133-
* **memoryLimit** `number`:
133+
* **memoryLimit** `number`:
134134
Memory limit for service process in MB. If service exits with allocation failed error, increase this number. Default: `2048`.
135135

136136
* **workers** `number`:
137-
You can split type checking to a few workers to speed-up increment build. **Be careful** - if you don't want to increase build time, you
137+
You can split type checking to a few workers to speed-up increment build. **Be careful** - if you don't want to increase build time, you
138138
should keep free 1 core for *build* and 1 core for a *system* *(for example system with 4 CPUs should use max 2 workers)*. Second thing -
139139
node doesn't share memory between workers - keep in mind that memory usage will increase. Be aware that in some scenarios increasing workers
140140
number **can increase checking time**. Default: `ForkTsCheckerWebpackPlugin.ONE_CPU`.
141141

142142
* **vue** `boolean`:
143-
If `true`, the linter and compiler will process VueJs single-file-component (.vue) files. See the
143+
If `true`, the linter and compiler will process VueJs single-file-component (.vue) files. See the
144144
[Vue section](https://github.com/Realytics/fork-ts-checker-webpack-plugin#vue) further down for information on how to correctly setup your project.
145145

146146
* **useTypescriptIncrementalApi** `boolean`:
147-
If true, the plugin will use incremental compilation API introduced in typescript 2.7. In this mode you can only have 1
148-
worker, but if the changes in your code are small (like you normally have when you work in 'watch' mode), the compilation
147+
If true, the plugin will use incremental compilation API introduced in typescript 2.7. In this mode you can only have 1
148+
worker, but if the changes in your code are small (like you normally have when you work in 'watch' mode), the compilation
149149
may be much faster, even compared to multi-threaded compilation. Defaults to `true` when working with typescript 3+ and `false` when below 3. The default can be overridden by directly specifying a value.
150150

151151
* **measureCompilationTime** `boolean`:
@@ -155,7 +155,26 @@ especially if there are other loaders/plugins involved in the compilation. **req
155155
* **typescript** `string`:
156156
If supplied this is a custom path where `typescript` can be found. Defaults to `require.resolve('typescript')`.
157157

158-
### Pre-computed consts:
158+
* **resolveModuleNameModule** and **resolveTypeReferenceDirectiveModule** `string`:
159+
Both of those options refer to files on the disk that respectively export a `resolveModuleName` or a `resolveTypeReferenceDirectiveModule` function. These functions will be used to resolve the import statements and the `<reference types="...">` directives instead of the default TypeScript implementation. Check the following code for an example of what those functions should look like:
160+
<details>
161+
<summary>Code sample</summary>
162+
163+
```js
164+
const {resolveModuleName} = require(`ts-pnp`);
165+
166+
exports.resolveModuleName = (typescript, moduleName, containingFile, compilerOptions, resolutionHost) => {
167+
return resolveModuleName(moduleName, containingFile, compilerOptions, resolutionHost, typescript.resolveModuleName);
168+
};
169+
170+
exports.resolveTypeReferenceDirective = (typescript, moduleName, containingFile, compilerOptions, resolutionHost) => {
171+
return resolveModuleName(moduleName, containingFile, compilerOptions, resolutionHost, typescript.resolveTypeReferenceDirective);
172+
};
173+
```
174+
175+
</details>
176+
177+
### Pre-computed consts:
159178
* `ForkTsCheckerWebpackPlugin.ONE_CPU` - always use one CPU
160179
* `ForkTsCheckerWebpackPlugin.ALL_CPUS` - always use all CPUs (will increase build time)
161180
* `ForkTsCheckerWebpackPlugin.ONE_CPU_FREE` - leave only one CPU for build (probably will increase build time)
@@ -188,7 +207,7 @@ This plugin provides some custom webpack hooks (all are sync):
188207
|`fork-ts-checker-service-start`| Service will be started | `tsconfigPath`, `tslintPath`, `watchPaths`, `workersNumber`, `memoryLimit` |
189208
|`fork-ts-checker-service-start-error` | Cannot start service | `error` |
190209
|`fork-ts-checker-service-out-of-memory`| Service is out of memory | - |
191-
|`fork-ts-checker-receive`| Plugin receives diagnostics and lints from service | `diagnostics`, `lints` |
210+
|`fork-ts-checker-receive`| Plugin receives diagnostics and lints from service | `diagnostics`, `lints` |
192211
|`fork-ts-checker-emit`| Service will add errors and warnings to webpack compilation ('build' mode) | `diagnostics`, `lints`, `elapsed` |
193212
|`fork-ts-checker-done`| Service finished type checking and webpack finished compilation ('watch' mode) | `diagnostics`, `lints`, `elapsed` |
194213

@@ -203,7 +222,7 @@ new ForkTsCheckerWebpackPlugin({
203222
```
204223

205224
2. To activate TypeScript in your `.vue` files, you need to ensure your script tag's language attribute is set
206-
to `ts` or `tsx` (also make sure you include the `.vue` extension in all your import statements as shown below):
225+
to `ts` or `tsx` (also make sure you include the `.vue` extension in all your import statements as shown below):
207226

208227
```html
209228
<script lang="ts">
@@ -213,7 +232,7 @@ import Hello from '@/components/hello.vue'
213232
</script>
214233
```
215234

216-
3. Ideally you are also using `ts-loader` (in transpileOnly mode). Your Webpack config rules may look something like this:
235+
3. Ideally you are also using `ts-loader` (in transpileOnly mode). Your Webpack config rules may look something like this:
217236

218237
```js
219238
{
@@ -231,7 +250,7 @@ import Hello from '@/components/hello.vue'
231250
options: vueLoaderConfig
232251
},
233252
```
234-
4. Add rules to your `tslint.json` and they will be applied to Vue files. For example, you could apply the Standard JS rules [tslint-config-standard](https://github.com/blakeembrey/tslint-config-standard) like this:
253+
4. Add rules to your `tslint.json` and they will be applied to Vue files. For example, you could apply the Standard JS rules [tslint-config-standard](https://github.com/blakeembrey/tslint-config-standard) like this:
235254

236255
```json
237256
{
@@ -241,7 +260,7 @@ import Hello from '@/components/hello.vue'
241260
]
242261
}
243262
```
244-
5. Ensure your `tsconfig.json` includes .vue files:
263+
5. Ensure your `tsconfig.json` includes .vue files:
245264

246265
```js
247266
// tsconfig.json
@@ -256,12 +275,12 @@ import Hello from '@/components/hello.vue'
256275
}
257276
```
258277

259-
6. It accepts any wildcard in your TypeScript configuration:
278+
6. It accepts any wildcard in your TypeScript configuration:
260279
```js
261280
// tsconfig.json
262281
{
263282
"compilerOptions": {
264-
283+
265284
// ...
266285

267286
"baseUrl": ".",

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "fork-ts-checker-webpack-plugin",
3-
"version": "1.0.4",
3+
"version": "1.1.0",
44
"description": "Runs typescript type checker and linter on separate process.",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",

src/ApiIncrementalChecker.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from './linterConfigHelpers';
1414
import { NormalizedMessage } from './NormalizedMessage';
1515
import { CompilerHost } from './CompilerHost';
16+
import { ResolveModuleName, ResolveTypeReferenceDirective } from './resolution';
1617
import { FsHelper } from './FsHelper';
1718

1819
export class ApiIncrementalChecker implements IncrementalCheckerInterface {
@@ -41,7 +42,9 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
4142
private context: string,
4243
private linterConfigFile: string | boolean,
4344
private linterAutoFix: boolean,
44-
checkSyntacticErrors: boolean
45+
checkSyntacticErrors: boolean,
46+
resolveModuleName: ResolveModuleName | undefined,
47+
resolveTypeReferenceDirective: ResolveTypeReferenceDirective | undefined
4548
) {
4649
this.hasFixedConfig = typeof this.linterConfigFile === 'string';
4750

@@ -51,7 +54,9 @@ export class ApiIncrementalChecker implements IncrementalCheckerInterface {
5154
typescript,
5255
programConfigFile,
5356
compilerOptions,
54-
checkSyntacticErrors
57+
checkSyntacticErrors,
58+
resolveModuleName,
59+
resolveTypeReferenceDirective
5560
);
5661
}
5762

src/CompilerHost.ts

+49-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
import * as ts from 'typescript'; // Imported for types alone
33
import { LinkedList } from './LinkedList';
44
import { VueProgram } from './VueProgram';
5+
import {
6+
ResolveModuleName,
7+
ResolveTypeReferenceDirective,
8+
makeResolutionFunctions
9+
} from './resolution';
510

611
interface DirectoryWatchDelaySlot {
712
events: { fileName: string }[];
@@ -51,11 +56,16 @@ export class CompilerHost
5156

5257
private compilationStarted = false;
5358

59+
private readonly resolveModuleName: ResolveModuleName;
60+
private readonly resolveTypeReferenceDirective: ResolveTypeReferenceDirective;
61+
5462
constructor(
5563
private typescript: typeof ts,
5664
programConfigFile: string,
5765
compilerOptions: ts.CompilerOptions,
58-
checkSyntacticErrors: boolean
66+
checkSyntacticErrors: boolean,
67+
userResolveModuleName?: ResolveModuleName,
68+
userResolveTypeReferenceDirective?: ResolveTypeReferenceDirective
5969
) {
6070
this.tsHost = typescript.createWatchCompilerHost(
6171
programConfigFile,
@@ -75,6 +85,17 @@ export class CompilerHost
7585

7686
this.configFileName = this.tsHost.configFileName;
7787
this.optionsToExtend = this.tsHost.optionsToExtend || {};
88+
89+
const {
90+
resolveModuleName,
91+
resolveTypeReferenceDirective
92+
} = makeResolutionFunctions(
93+
userResolveModuleName,
94+
userResolveTypeReferenceDirective
95+
);
96+
97+
this.resolveModuleName = resolveModuleName;
98+
this.resolveTypeReferenceDirective = resolveTypeReferenceDirective;
7899
}
79100

80101
public async processChanges(): Promise<{
@@ -346,6 +367,33 @@ export class CompilerHost
346367
this.afterCompile();
347368
}
348369

370+
public resolveModuleNames(moduleNames: string[], containingFile: string) {
371+
return moduleNames.map(moduleName => {
372+
return this.resolveModuleName(
373+
this.typescript,
374+
moduleName,
375+
containingFile,
376+
this.optionsToExtend,
377+
this
378+
).resolvedModule;
379+
});
380+
}
381+
382+
public resolveTypeReferenceDirectives(
383+
typeDirectiveNames: string[],
384+
containingFile: string
385+
) {
386+
return typeDirectiveNames.map(typeDirectiveName => {
387+
return this.resolveTypeReferenceDirective(
388+
this.typescript,
389+
typeDirectiveName,
390+
containingFile,
391+
this.optionsToExtend,
392+
this
393+
).resolvedTypeReferenceDirective;
394+
});
395+
}
396+
349397
// the functions below are use internally by typescript. we cannot use non-emitting version of incremental watching API
350398
// because it is
351399
// - much slower for some reason,

0 commit comments

Comments
 (0)