diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..92c7845c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +## v0.1.4 + * Fix send to closed channel case + * Fix removed files case + * Add `fork-ts-checker-service-start-error` hook + +## v0.1.3 + * Fix "Cannot read property 'mtime' of undefined on OSX" + +## v0.1.2 + * Workers mode works correctly (fixed typo) + +## v0.1.1 + * Support memory limit in multi-process mode + * Handle already closed channel case on sending ipc message + +## v0.1.0 + * Initial release - not production ready. \ No newline at end of file diff --git a/README.md b/README.md index 42d17089..e682ddfc 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ [![Npm version](https://img.shields.io/npm/v/fork-ts-checker-webpack-plugin.svg?style=flat-square)](https://www.npmjs.com/package/fork-ts-checker-webpack-plugin) [![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) -Webpack plugin that runs typescript type checker (with optional linter) on separate processes. +Webpack plugin that runs typescript type checker on a separate process. ## Installation ## This plugin requires minimum **webpack 2**, **typescript 2.1** and optionally **tslint 5.0** ```sh -npm install --save fork-ts-checker-webpack-plugin +npm install --save-dev fork-ts-checker-webpack-plugin ``` Basic webpack config (with [ts-loader](https://github.com/TypeStrong/ts-loader)) ```js @@ -43,18 +43,17 @@ var webpackConfig = { ## Motivation ## There is already similar solution - [awesome-typescript-loader](https://github.com/s-panferov/awesome-typescript-loader). You can -add `CheckerPlugin` and delegate checker to the separate process. The problem with `awesome-typescript-loader` is that it's a lot slower -than [ts-loader](https://github.com/TypeStrong/ts-loader) on incremental build in our case (~20s vs ~3s). -Secondly, we use [tslint](https://palantir.github.io/tslint/) and we wanted to run this also on separate process. -This is why we've created this plugin. The performance is great because of reusing Abstract Syntax Trees between compilations and sharing -these trees with tslint. We can also scale checker with multi-process mode - it will split work between processes to utilize maximum cpu -power. +add `CheckerPlugin` and delegate checker to the separate process. The problem with `awesome-typescript-loader` was that, in our case, +it was a lot slower than [ts-loader](https://github.com/TypeStrong/ts-loader) on an incremental build (~20s vs ~3s). +Secondly, we use [tslint](https://palantir.github.io/tslint/) and we wanted to run this, along with type checker, in a separate process. +This is why we've created this plugin. To provide better performance, plugin reuses Abstract Syntax Trees between compilations and shares +these trees with tslint. It can be scaled with a multi-process mode to utilize maximum CPU power. ## Options ## **tsconfig** `string` - Path to tsconfig.json file. If not set, plugin will use `path.resolve(compiler.options.context, './tsconfig.json')`. **tslint** `string | false` - Path to tslint.json file. If not set, plugin will use `path.resolve(compiler.options.context, './tslint.json')`. - If `false`, disables tslint. + If `false`, disables tslint. **watch** `string | string[]` - Directories or files to watch by service. Not necessary but improves performance (reduces number of `fs.stat` calls). @@ -72,18 +71,17 @@ power. **silent** `boolean` - If `true`, logger will not be used. Default: `false`. -**workers** `number` - You can split type checking to few workers to speed-up on increment build. - **Be careful** - if you don't want to increase build time, you should keep 1 core for *build* and 1 core for - *system* free *(for example system with 4 cpu threads should use max 2 workers)*. - Second thing - node doesn't share memory between workers so keep in mind that memory usage will increase - linearly. If you want to use workers, please experiment with workers number. In some scenarios increasing this number - **can increase check time** (and of course memory consumption). +**workers** `number` - 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 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 - node doesn't share memory between workers - keep in mind that memory usage will increase. + Be aware that in some scenarios increasing workers number **can increase checking time**. Default: `ForkTsCheckerWebpackPlugin.ONE_CPU`. Pre-computed consts: - * `ForkTsCheckerWebpackPlugin.ONE_CPU` - always use one cpu (core) - * `ForkTsCheckerWebpackPlugin.ONE_CPU_FREE` - leave only one cpu for build (probably will increase build time) - * `ForkTsCheckerWebpackPlugin.TWO_CPUS_FREE` - leave two cpus free (one for build, one for system) + * `ForkTsCheckerWebpackPlugin.ONE_CPU` - always use one CPU + * `ForkTsCheckerWebpackPlugin.ONE_CPU_FREE` - leave only one CPU for build (probably will increase build time) + * `ForkTsCheckerWebpackPlugin.TWO_CPUS_FREE` - leave two CPUs free (one for build, one for system) **memoryLimit** `number` - Memory limit for service process in MB. If service exits with allocation failed error, increase this number. Default: `2048`. @@ -96,6 +94,7 @@ This plugin provides some custom webpack hooks (all are sync): |`fork-ts-checker-cancel`| Cancellation has been requested | `cancellationToken` | |`fork-ts-checker-waiting`| Waiting for results | `hasTsLint` | |`fork-ts-checker-service-start`| Service will be started | `tsconfigPath`, `tslintPath`, `watchPaths`, `workersNumber`, `memoryLimit` | +|`fork-ts-checker-service-start-error` | Cannot start service | `error` | |`fork-ts-checker-service-out-of-memory`| Service is out of memory | - | |`fork-ts-checker-receive`| Plugin receives diagnostics and lints from service | `diagnostics`, `lints` | |`fork-ts-checker-emit`| Service will add errors and warnings to webpack compilation (`blockEmit: true`) | `diagnostics`, `lints`, `elapsed` | diff --git a/lib/IncrementalChecker.js b/lib/IncrementalChecker.js index 0aae2c17..853bf86c 100644 --- a/lib/IncrementalChecker.js +++ b/lib/IncrementalChecker.js @@ -47,9 +47,14 @@ IncrementalChecker.createProgram = function (programConfig, files, watcher, oldP host.getSourceFile = function (filePath, languageVersion, onError) { // first check if watcher is watching file - if not - check it's mtime if (!watcher.isWatchingFile(filePath)) { - var stats = fs.statSync(filePath); - - files.setMtime(filePath, stats.mtime.valueOf()); + try { + var stats = fs.statSync(filePath); + + files.setMtime(filePath, stats.mtime.valueOf()); + } catch (e) { + // probably file does not exists + files.remove(filePath); + } } // get source file only if there is no source in files register @@ -145,7 +150,14 @@ IncrementalChecker.prototype.getLints = function (cancellationToken) { workSet.forEach(function (fileName) { cancellationToken.throwIfCancellationRequested(); - this.linter.lint(fileName, undefined, this.linterConfig); + try { + this.linter.lint(fileName, undefined, this.linterConfig); + } catch (e) { + if (fs.existsSync(fileName)) { + // it's not because file doesn't exist - throw error + throw e; + } + } }.bind(this)); // set lints in files register diff --git a/lib/cluster.js b/lib/cluster.js index 6848be48..0255a42a 100644 --- a/lib/cluster.js +++ b/lib/cluster.js @@ -29,7 +29,12 @@ var result = new WorkResult(pids); process.on('message', function (message) { // broadcast message to all workers workers.forEach(function (worker) { - worker.send(message); + try { + worker.send(message); + } catch (e) { + // channel closed - something went wrong - close cluster... + process.exit(); + } }); // clear previous result set diff --git a/lib/index.js b/lib/index.js index 952b0a44..6ed470dd 100644 --- a/lib/index.js +++ b/lib/index.js @@ -156,7 +156,16 @@ ForkTsCheckerWebpackPlugin.prototype.pluginCompile = function () { if (!this.service || !this.service.connected) { this.spawnService(); } - this.service.send(this.cancellationToken); + + try { + this.service.send(this.cancellationToken); + } catch (error) { + if (!this.options.silent && this.logger) { + this.logger.error(this.colors.red('Cannot start checker service: ' + (error ? error.toString() : 'Unknown error'))); + } + + this.compiler.applyPlugins('fork-ts-checker-service-start-error', error); + } }.bind(this)); }; @@ -234,7 +243,7 @@ ForkTsCheckerWebpackPlugin.prototype.spawnService = function () { } if (this.tslint && !this.options.tslint) { // auto-detect tslint path - print to the user to be sure that it's proper file - lines.push(this.colors.grey(this.tslint)); + lines.push(this.colors.grey(this.tslintPath)); } this.logger.info(lines.join('\n')); diff --git a/package.json b/package.json index cb63ba00..1d2497d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fork-ts-checker-webpack-plugin", - "version": "0.1.3", + "version": "0.1.4", "description": "Runs typescript type checker and linter on separate process.", "main": "lib/index.js", "scripts": { @@ -34,11 +34,11 @@ "chai": "^3.5.0", "eslint": "^3.19.0", "istanbul": "^0.4.5", - "mocha": "^3.2.0", - "mock-fs": "^4.2.0", + "mocha": "^3.4.1", + "mock-fs": "^4.3.0", "mock-require": "^2.0.2", "rimraf": "^2.5.4", - "sinon": "^2.1.0", + "sinon": "^2.2.0", "typescript": "^2.1.0" }, "peerDependencies": { @@ -47,12 +47,9 @@ }, "dependencies": { "chalk": "^1.1.3", - "chokidar": "^1.6.1", + "chokidar": "^1.7.0", "lodash.endswith": "^4.2.1", "lodash.isstring": "^4.0.1", "lodash.startswith": "^4.2.1" - }, - "optionalDependencies": { - "tslint": "^5.0.0" } } diff --git a/yarn.lock b/yarn.lock index 550fd7aa..4b2718a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -249,9 +249,9 @@ chalk@^1.0.0, chalk@^1.1.0, chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -chokidar@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" +chokidar@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" dependencies: anymatch "^1.3.0" async-each "^1.0.0" @@ -1232,9 +1232,9 @@ mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1: dependencies: minimist "0.0.8" -mocha@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.3.0.tgz#d29b7428d3f52c82e2e65df1ecb7064e1aabbfb5" +mocha@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-3.4.1.tgz#a3802b4aa381934cacb38de70cf771621da8f9af" dependencies: browser-stdout "1.3.0" commander "2.9.0" @@ -1248,9 +1248,9 @@ mocha@^3.2.0: mkdirp "0.5.1" supports-color "3.1.2" -mock-fs@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.2.0.tgz#ef53ae17b77e64f67816dd0467f29208a3b26e19" +mock-fs@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.3.0.tgz#c2fab8d784283287e9b6ae7538f2dc56c1a05ed7" mock-require@^2.0.2: version "2.0.2" @@ -1638,9 +1638,9 @@ signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -sinon@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.1.0.tgz#e057a9d2bf1b32f5d6dd62628ca9ee3961b0cafb" +sinon@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.2.0.tgz#3b1b42ff5defcbf51a52a62aca6d61171b9fd262" dependencies: diff "^3.1.0" formatio "1.2.0"