UNPKG

23.1 kBMarkdownView Raw
1# Fork TS Checker Webpack Plugin
2
3[![npm version](https://img.shields.io/npm/v/fork-ts-checker-webpack-plugin.svg)](https://www.npmjs.com/package/fork-ts-checker-webpack-plugin)
4
5[![npm beta version](https://img.shields.io/npm/v/fork-ts-checker-webpack-plugin/beta.svg)](https://www.npmjs.com/package/fork-ts-checker-webpack-plugin)
6
7[![build status](https://travis-ci.org/TypeStrong/fork-ts-checker-webpack-plugin.svg?branch=master)](https://travis-ci.org/TypeStrong/fork-ts-checker-webpack-plugin)
8
9[![downloads](http://img.shields.io/npm/dm/fork-ts-checker-webpack-plugin.svg)](https://npmjs.org/package/fork-ts-checker-webpack-plugin)
10
11[![commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
12
13[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
14
15[![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
16
17Webpack plugin that runs TypeScript type checker on a separate process.
18
19## Installation
20
21This plugin requires minimum **webpack 2.3**, **TypeScript 2.1** and optionally **ESLint 6.0.0** or **TSLint 4.0**
22
23```sh
24yarn add fork-ts-checker-webpack-plugin --dev
25```
26
27Basic webpack config (with [ts-loader](https://github.com/TypeStrong/ts-loader))
28
29```js
30const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
31
32const webpackConfig = {
33 context: __dirname, // to automatically find tsconfig.json
34 entry: './src/index.ts',
35 module: {
36 rules: [
37 {
38 test: /\.tsx?$/,
39 loader: 'ts-loader',
40 options: {
41 // disable type checker - we will use it in fork plugin
42 transpileOnly: true
43 }
44 }
45 ]
46 },
47 plugins: [new ForkTsCheckerWebpackPlugin()]
48};
49```
50
51## Motivation
52
53There was already similar solution - [awesome-typescript-loader](https://github.com/s-panferov/awesome-typescript-loader). You can
54add `CheckerPlugin` and delegate checker to the separate process. The problem with `awesome-typescript-loader` was that, in our case,
55it was a lot slower than [ts-loader](https://github.com/TypeStrong/ts-loader) on an incremental build (~20s vs ~3s).
56Secondly, we used [tslint](https://palantir.github.io/tslint) and we wanted to run this, along with type checker, in a separate process.
57This is why this plugin was created. To provide better performance, the plugin reuses Abstract Syntax Trees between compilations and shares
58these trees with TSLint. It can be scaled with a multi-process mode to utilize maximum CPU power.
59
60## Modules resolution
61
62It's very important to be aware that **this plugin uses [TypeScript](https://github.com/Microsoft/TypeScript)'s, not
63[webpack](https://github.com/webpack/webpack)'s modules resolution**. It means that you have to setup `tsconfig.json` correctly. For example
64if you set `files: ['./src/someFile.ts']` in `tsconfig.json`, this plugin will check only `someFile.ts` for semantic errors. It's because
65of 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
66to compile files (which traverses dependency graph during compilation) - we have a full list of files from the begin.
67
68To debug TypeScript's modules resolution, you can use `tsc --traceResolution` command.
69
70## ESLint
71
72[ESLint is the future of linting in the TypeScript world.](https://eslint.org/blog/2019/01/future-typescript-eslint) If you'd like to use eslint with the plugin, supply this option: `eslint: true` and ensure you have the relevant dependencies installed:
73
74`yarn add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --dev`
75
76You should have an ESLint configuration file in your root project directory. Here is a sample `.eslintrc.js` configuration for a TypeScript project:
77
78```js
79const path = require('path');
80module.exports = {
81 parser: '@typescript-eslint/parser', // Specifies the ESLint parser
82 extends: [
83 'plugin:@typescript-eslint/recommended' // Uses the recommended rules from the @typescript-eslint/eslint-plugin
84 ],
85 parserOptions: {
86 project: path.resolve(__dirname, './tsconfig.json'),
87 tsconfigRootDir: __dirname,
88 ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
89 sourceType: 'module', // Allows for the use of imports
90 },
91 rules: {
92 // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
93 // e.g. "@typescript-eslint/explicit-function-return-type": "off",
94 }
95};
96```
97
98There's a good explanation on setting up TypeScript ESLint support by Robert Cooper [here](https://dev.to/robertcoopercode/using-eslint-and-prettier-in-a-typescript-project-53jb).
99
100## TSLint
101
102*[TSLint is being replaced by ESLint](https://medium.com/palantir/tslint-in-2019-1a144c2317a9).
103https://eslint.org/blog/2019/01/future-typescript-eslint. As a consequence, support for TSLint in fork-ts-checker-webpack-plugin will be deprecated and removed in future versions of the plugin.*
104
105If you have installed [tslint](https://palantir.github.io/tslint), you can enable it by setting `tslint: true` or
106`tslint: './path/to/tslint.json'`. We recommend changing `defaultSeverity` to a `"warning"` in `tslint.json` file.
107It helps to distinguish lints from TypeScript's diagnostics.
108
109
110
111## Options
112
113- **tsconfig** `string`:
114 Path to _tsconfig.json_ file. Default: `path.resolve(compiler.options.context, './tsconfig.json')`.
115
116- **compilerOptions** `object`:
117 Allows overriding TypeScript options. Should be specified in the same format as you would do for the `compilerOptions` property in tsconfig.json. Default: `{}`.
118
119- **eslint** `true | undefined`:
120
121 - If `true`, this activates eslint support.
122
123- **eslintOptions** `object`:
124
125 - Options that can be used to initialise ESLint. See https://eslint.org/docs/1.0.0/developer-guide/nodejs-api#cliengine
126
127- **tslint** `string | true | undefined`:
128
129 - If `string`, path to _tslint.json_ file to check source files against.
130 - If `true`, path to `tslint.json` file will be computed with respect to currently checked file, just like TSLint
131 CLI would do. Suppose you have a project:
132
133 ```
134 ./
135 tslint.json
136
137 src/
138 file.ts
139 anotherFile.ts
140
141 lib/
142 tslint.json
143 someHelperFile.ts
144 ```
145
146 In such a case `src/file.ts` and `src/anotherFile.ts` would be checked against root `tslint.json`, and
147 `src/lib/someHelperFile.ts` would be checked against `src/lib/tslint.json`.
148
149 Default: `undefined`.
150
151- **tslintAutoFix** `boolean`:
152 Passes on `--fix` flag while running `tslint` to auto fix linting errors. Default: false.
153
154- **watch** `string | string[]`:
155 Directories or files to watch by service. Not necessary but improves performance (reduces number of `fs.stat` calls). Not applicable when `useTypescriptIncrementalApi` is `true`, in which case watching is handled automatically.
156
157- **async** `boolean`:
158 True by default - `async: false` can block webpack's emit to wait for type checker/linter and to add errors to the webpack's compilation.
159 We recommend to set this to `false` in projects where type checking is faster than webpack's build - it's better for integration with other plugins. Another scenario where you might want to set this to `false` is if you use the `overlay` functionality of `webpack-dev-server`.
160
161- **ignoreDiagnostics** `number[]`:
162 List of TypeScript diagnostic codes to ignore.
163
164- **ignoreLints** `string[]`:
165 List of tslint rule names to ignore.
166
167- **ignoreLintWarnings** `boolean`:
168 If true, will ignore all lint warnings.
169
170- **reportFiles** `string[]`:
171 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).
172
173```js
174// in webpack.config.js
175new ForkTsCheckerWebpackPlugin({
176 reportFiles: ['src/**/*.{ts,tsx}', '!src/skip.ts']
177});
178```
179
180- **colors** `boolean`:
181 If `false`, disables built-in colors in logger messages. Default: `true`.
182
183- **logger** `object`:
184 Logger instance. It should be object that implements method: `error`, `warn`, `info`. Default: `console`.
185
186- **formatter** `'default' | 'codeframe' | ((message: NormalizedMessage, useColors: boolean) => string)`:
187 Formatter for diagnostics and lints. By default uses `default` formatter. You can also pass your own formatter as a function
188 (see `src/NormalizedMessage.js` and `src/formatter/` for api reference).
189
190- **formatterOptions** `object`:
191 Options passed to formatters (currently only `codeframe` - see [available options](https://www.npmjs.com/package/babel-code-frame#options))
192
193- **silent** `boolean`:
194 If `true`, logger will not be used. Default: `false`.
195
196- **checkSyntacticErrors** `boolean`:
197 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`.
198
199- **memoryLimit** `number`:
200 Memory limit for service process in MB. If service exits with allocation failed error, increase this number. Default: `2048`.
201
202- **workers** `number`:
203 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
204 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 -
205 node doesn't share memory between workers - keep in mind that memory usage will increase. Be aware that in some scenarios increasing workers
206 number **can increase checking time**. Default: `ForkTsCheckerWebpackPlugin.ONE_CPU`.
207
208- **vue** `boolean`:
209 If `true`, the linter and compiler will process VueJs single-file-component (.vue) files. See the
210 [Vue section](https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#vue) further down for information on how to correctly setup your project.
211
212- **useTypescriptIncrementalApi** `boolean`:
213 If true, the plugin will use incremental compilation API introduced in TypeScript 2.7. In this mode you can only have 1
214 worker, but if the changes in your code are small (like you normally have when you work in 'watch' mode), the compilation
215 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.
216
217- **measureCompilationTime** `boolean`:
218 If true, the plugin will measure the time spent inside the compilation code. This may be useful to compare modes,
219 especially if there are other loaders/plugins involved in the compilation. **requires node 8+**
220
221- **typescript** `string`:
222 If supplied this is a custom path where `typescript` can be found. Defaults to `require.resolve('typescript')`.
223
224- **resolveModuleNameModule** and **resolveTypeReferenceDirectiveModule** `string`:
225 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:
226
227 <details>
228 <summary>Code sample</summary>
229
230 ```js
231 const { resolveModuleName } = require(`ts-pnp`);
232
233 exports.resolveModuleName = (
234 typescript,
235 moduleName,
236 containingFile,
237 compilerOptions,
238 resolutionHost
239 ) => {
240 return resolveModuleName(
241 moduleName,
242 containingFile,
243 compilerOptions,
244 resolutionHost,
245 typescript.resolveModuleName
246 );
247 };
248
249 exports.resolveTypeReferenceDirective = (
250 typescript,
251 moduleName,
252 containingFile,
253 compilerOptions,
254 resolutionHost
255 ) => {
256 return resolveModuleName(
257 moduleName,
258 containingFile,
259 compilerOptions,
260 resolutionHost,
261 typescript.resolveTypeReferenceDirective
262 );
263 };
264 ```
265
266</details>
267
268### Pre-computed consts:
269
270- `ForkTsCheckerWebpackPlugin.ONE_CPU` - always use one CPU
271- `ForkTsCheckerWebpackPlugin.ALL_CPUS` - always use all CPUs (will increase build time)
272- `ForkTsCheckerWebpackPlugin.ONE_CPU_FREE` - leave only one CPU for build (probably will increase build time)
273- `ForkTsCheckerWebpackPlugin.TWO_CPUS_FREE` - **recommended** - leave two CPUs free (one for build, one for system)
274
275## Different behaviour in watch mode
276
277If you turn on [webpacks watch mode](https://webpack.js.org/configuration/watch/#watch) the `fork-ts-checker-notifier-webpack-plugin` will take care of logging type errors, _not_ webpack itself. That means if you set `silent: true` you won't see type errors in your console in watch mode.
278
279You can either set `silent: false` to show the logging from `fork-ts-checker-notifier-webpack-plugin` _or_ set `async: false`. Now webpack itself will log type errors again, but note that this can slow down your builds depending on the size of your project.
280
281## Notifier
282
283You may already be using the excellent [webpack-notifier](https://github.com/Turbo87/webpack-notifier) plugin to make build failures more obvious in the form of system notifications. There's an equivalent notifier plugin designed to work with the `fork-ts-checker-webpack-plugin`. It is the `fork-ts-checker-notifier-webpack-plugin` and can be found [here](https://github.com/johnnyreilly/fork-ts-checker-notifier-webpack-plugin). This notifier deliberately has a similar API as the `webpack-notifier` plugin to make migration easier.
284
285## Known Issue Watching Non-Emitting Files
286
287At present there is an issue with the plugin regarding the triggering of type-checking when a change is made in a source file that will not emit js. If you have a file which contains only `interface`s and / or `type`s then changes to it will **not** trigger the type checker whilst in watch mode. Sorry about that.
288
289We hope this will be resolved in future; the issue can be tracked [here](https://github.com/TypeStrong/fork-ts-checker-webpack-plugin/issues/36).
290
291## Plugin Hooks
292
293This plugin provides some custom webpack hooks (all are sync):
294
295| Event name | Hook Access Key | Description | Params |
296| --------------------------------------- | -------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------- |
297| `fork-ts-checker-cancel` | `cancel` | Cancellation has been requested | `cancellationToken` |
298| `fork-ts-checker-waiting` | `waiting` | Waiting for results | `hasTsLint` |
299| `fork-ts-checker-service-before-start` | `serviceBeforeStart` | Async plugin that can be used for delaying `fork-ts-checker-service-start` | - |
300| `fork-ts-checker-service-start` | `serviceStart` | Service will be started | `tsconfigPath`, `tslintPath`, `watchPaths`, `workersNumber`, `memoryLimit` |
301| `fork-ts-checker-service-start-error` | `serviceStartError` | Cannot start service | `error` |
302| `fork-ts-checker-service-out-of-memory` | `serviceOutOfMemory` | Service is out of memory | - |
303| `fork-ts-checker-receive` | `receive` | Plugin receives diagnostics and lints from service | `diagnostics`, `lints` |
304| `fork-ts-checker-emit` | `emit` | Service will add errors and warnings to webpack compilation ('build' mode) | `diagnostics`, `lints`, `elapsed` |
305| `fork-ts-checker-done` | `done` | Service finished type checking and webpack finished compilation ('watch' mode) | `diagnostics`, `lints`, `elapsed` |
306
307The **Event name** is there for backward compatibility with webpack 2/3. Regardless
308of the version of webpack (2, 3 or 4) you are using, we will always access plugin hooks with **Hook Access Keys** as
309described below.
310
311### Accessing plugin hooks
312
313All plugin hooks are compatible with both [webpack](https://webpack.js.org) version
3144 and version 2. To access plugin hooks and tap into the event, we need to use
315the `getCompilerHooks` static method. When we call this method with a [webpack compiler instance](https://webpack.js.org/api/node/),
316it returns the series of [tapable](https://github.com/webpack/tapable)
317hooks where you can pass in your callbacks.
318
319```js
320// require the plugin
321const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
322// setup compiler with the plugin
323const compiler = webpack({
324 // .. webpack config
325});
326// Optionally add the plugin to the compiler
327// **Don't do this if already added through configuration**
328new ForkTsCheckerWebpackPlugin({
329 silent: true,
330 async: true
331}).apply(compiler);
332// Now get the plugin hooks from compiler
333const tsCheckerHooks = ForkTsCheckerWebpackPlugin.getCompilerHooks(compiler);
334// These hooks provide access to different events
335// =================================================== //
336// The properties of tsCheckerHooks corresponds to the //
337// Hook Access Key of the table above. //
338// =================================================== //
339// Example, if we want to run some code when plugin has received diagnostics
340// and lint
341tsCheckerHooks.receive.tap('yourListenerName', (diagnostics, lint) => {
342 // do something with diagnostics, perhaps show custom message
343 console.log(diagnostics);
344});
345// Say we want to show some message when plugin is waiting for typecheck results
346tsCheckerHooks.waiting.tap('yourListenerName', () => {
347 console.log('waiting for typecheck results');
348});
349```
350
351Calling `.tap()` on any hooks, requires two arguments.
352
353##### `name` (`string`)
354
355The first argument passed to `.tap` is the name of your listener callback (`yourListenerName`).
356It doesn't need to correspond to anything special. It is intended to be used
357[internally](https://github.com/webpack/tapable#interception) as the `name` of
358the hook.
359
360##### `callback` (`function`)
361
362The second argument is the callback function. Depending on the hook you are
363tapping into, several arguments are passed to the function. Do check the table
364above to find out which arguments are passed to which hooks.
365
366### Accessing hooks on Webpack Multi-Compiler instance
367
368The above method will not work on webpack [multi compiler](https://webpack.js.org/api/node/#multicompiler)
369instance. The reason is `getCompilerHooks` expects (at lease as of now) the same
370compiler instance to be passed where the plugin was attached. So in case of
371multi compiler, we need to access individual compiler instances.
372
373```js
374const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
375// setup multi compiler with the plugin
376const compiler = webpack([
377 {
378 // .. webpack config
379 },
380 {
381 // .. webpack config
382 }
383]);
384
385// safely determine if instance is multi-compiler
386if ('compilers' in compiler) {
387 compiler.compilers.forEach(singleCompiler => {
388 // get plugin hooks from the single compiler instance
389 const tsCheckerHooks = ForkTsCheckerWebpackPlugin.getCompilerHooks(
390 singleCompiler
391 );
392 // now access hooks just like before
393 tsCheckerHooks.waiting.tap('yourListenerName', () => {
394 console.log('waiting for typecheck results');
395 });
396 });
397}
398```
399
400## Vue
401
4021. Turn on the vue option in the plugin in your webpack config:
403
404```js
405new ForkTsCheckerWebpackPlugin({
406 tslint: true,
407 vue: true
408});
409```
410
4112. To activate TypeScript in your `.vue` files, you need to ensure your script tag's language attribute is set
412 to `ts` or `tsx` (also make sure you include the `.vue` extension in all your import statements as shown below):
413
414```html
415<script lang="ts">
416 import Hello from '@/components/hello.vue';
417
418 // ...
419</script>
420```
421
4223. Ideally you are also using `ts-loader` (in transpileOnly mode). Your Webpack config rules may look something like this:
423
424```js
425{
426 test: /\.ts$/,
427 loader: 'ts-loader',
428 include: [resolve('src'), resolve('test')],
429 options: {
430 appendTsSuffixTo: [/\.vue$/],
431 transpileOnly: true
432 }
433},
434{
435 test: /\.vue$/,
436 loader: 'vue-loader',
437 options: vueLoaderConfig
438},
439```
440
4414. 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:
442
443```json
444{
445 "defaultSeverity": "error",
446 "extends": ["tslint-config-standard"]
447}
448```
449
4505. Ensure your `tsconfig.json` includes .vue files:
451
452```js
453// tsconfig.json
454{
455 "include": [
456 "src/**/*.ts",
457 "src/**/*.vue"
458 ],
459 "exclude": [
460 "node_modules"
461 ]
462}
463```
464
4656. It accepts any wildcard in your TypeScript configuration:
466
467```js
468// tsconfig.json
469{
470 "compilerOptions": {
471
472 // ...
473
474 "baseUrl": ".",
475 "paths": {
476 "@/*": [
477 "src/*"
478 ],
479 "~/*": [
480 "src/*"
481 ]
482 }
483 }
484}
485
486// In a .ts or .vue file...
487import Hello from '@/components/hello.vue'
488```
489
4907. If you are working in **VSCode**, you can get extensions [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur) and [TSLint Vue](https://marketplace.visualstudio.com/items?itemName=prograhammer.tslint-vue) to complete the developer workflow.
491
492## Credits
493
494This plugin was created in [Realytics](https://www.realytics.io/) in 2017. Thank you for supporting Open Source.
495
496## License
497
498MIT License