Skip to content

Commit bbbbfce

Browse files
phryneasjohnnyreilly
authored andcommitted
gracefully handle error thrown from the service (#249)
* gracefully handle error thrown from the service * add integration test * requested changes
1 parent e9a6047 commit bbbbfce

File tree

10 files changed

+89
-10
lines changed

10 files changed

+89
-10
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## v1.0.4
2+
3+
* [gracefully handle error thrown from the service](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/249)
4+
15
## v1.0.3
26

37
* [use worker-rpc library for inter-process communication](https://github.com/Realytics/fork-ts-checker-webpack-plugin/pull/231)

package.json

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

src/NormalizedMessage.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ interface NormalizedMessageJson {
99
file?: string;
1010
line?: number;
1111
character?: number;
12+
stack?: string;
1213
}
1314

1415
export class NormalizedMessage {
@@ -19,13 +20,16 @@ export class NormalizedMessage {
1920
public static readonly SEVERITY_ERROR: Severity = 'error';
2021
public static readonly SEVERITY_WARNING: Severity = 'warning';
2122

23+
public static readonly ERROR_CODE_INTERNAL = 'INTERNAL_ERROR';
24+
2225
public readonly type: ErrorType;
2326
public readonly code: string | number;
2427
public readonly severity: Severity;
2528
public readonly content: string;
2629
public readonly file?: string;
2730
public readonly line?: number;
2831
public readonly character?: number;
32+
public readonly stack?: string;
2933

3034
constructor(data: NormalizedMessageJson) {
3135
this.type = data.type;
@@ -35,6 +39,7 @@ export class NormalizedMessage {
3539
this.file = data.file;
3640
this.line = data.line;
3741
this.character = data.character;
42+
this.stack = data.stack;
3843
}
3944

4045
public static createFromJSON(json: NormalizedMessageJson) {
@@ -73,6 +78,10 @@ export class NormalizedMessage {
7378
messageA.content,
7479
messageB.content
7580
) ||
81+
NormalizedMessage.compareOptionalStrings(
82+
messageA.stack,
83+
messageB.stack
84+
) ||
7685
0 /* EqualTo */
7786
);
7887
}
@@ -149,7 +158,8 @@ export class NormalizedMessage {
149158
content: this.content,
150159
file: this.file,
151160
line: this.line,
152-
character: this.character
161+
character: this.character,
162+
stack: this.stack
153163
} as NormalizedMessageJson;
154164
}
155165

src/NormalizedMessageFactories.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,18 @@ export const makeCreateNormalizedMessageFromRuleFailure = () => {
5858
};
5959
return createNormalizedMessageFromRuleFailure;
6060
};
61+
62+
export const makeCreateNormalizedMessageFromInternalError = () => {
63+
const createNormalizedMessageFromInternalError = (error: any) => {
64+
return new NormalizedMessage({
65+
type: NormalizedMessage.TYPE_DIAGNOSTIC,
66+
severity: NormalizedMessage.SEVERITY_ERROR,
67+
code: NormalizedMessage.ERROR_CODE_INTERNAL,
68+
content:
69+
typeof error.message === 'string' ? error.message : error.toString(),
70+
stack: typeof error.stack === 'string' ? error.stack : undefined,
71+
file: '[internal]'
72+
});
73+
};
74+
return createNormalizedMessageFromInternalError;
75+
};

src/formatter/codeframeFormatter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ export function createCodeframeFormatter(options: any) {
2222
: colors.bold.red;
2323
const positionColor = colors.dim;
2424

25+
if (message.code === NormalizedMessage.ERROR_CODE_INTERNAL) {
26+
return (
27+
messageColor(`INTERNAL ${message.severity.toUpperCase()}: `) +
28+
message.content +
29+
(message.stack
30+
? os.EOL + 'stack trace:' + os.EOL + colors.gray(message.stack)
31+
: '')
32+
);
33+
}
34+
2535
const file = message.file;
2636
const source =
2737
file && FsHelper.existsSync(file) && fs.readFileSync(file, 'utf-8');

src/formatter/defaultFormatter.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ export function createDefaultFormatter() {
1919
const fileAndNumberColor = colors.bold.cyan;
2020
const codeColor = colors.grey;
2121

22+
if (message.code === NormalizedMessage.ERROR_CODE_INTERNAL) {
23+
return (
24+
messageColor(`INTERNAL ${message.severity.toUpperCase()}: `) +
25+
message.content +
26+
(message.stack
27+
? os.EOL + 'stack trace:' + os.EOL + colors.gray(message.stack)
28+
: '')
29+
);
30+
}
31+
2232
return [
2333
messageColor(`${message.severity.toUpperCase()} in `) +
2434
fileAndNumberColor(

src/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,9 @@ class ForkTsCheckerWebpackPlugin {
130130
private measureTime: boolean;
131131
private performance: any;
132132
private startAt: number = 0;
133+
134+
private nodeArgs: string[] = [];
135+
133136
constructor(options?: Partial<Options>) {
134137
options = options || ({} as Options);
135138
this.options = { ...options };
@@ -573,10 +576,10 @@ class ForkTsCheckerWebpackPlugin {
573576
),
574577
[],
575578
{
576-
execArgv:
577-
this.workersNumber > 1
578-
? []
579-
: ['--max-old-space-size=' + this.memoryLimit],
579+
execArgv: (this.workersNumber > 1
580+
? []
581+
: ['--max-old-space-size=' + this.memoryLimit]
582+
).concat(this.nodeArgs),
580583
env: {
581584
...process.env,
582585
TYPESCRIPT_PATH: this.typescriptPath,

src/service.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import { IncrementalCheckerInterface } from './IncrementalCheckerInterface';
88
import { ApiIncrementalChecker } from './ApiIncrementalChecker';
99
import {
1010
makeCreateNormalizedMessageFromDiagnostic,
11-
makeCreateNormalizedMessageFromRuleFailure
11+
makeCreateNormalizedMessageFromRuleFailure,
12+
makeCreateNormalizedMessageFromInternalError
1213
} from './NormalizedMessageFactories';
1314
import { RpcProvider } from 'worker-rpc';
1415
import { RunPayload, RunResult, RUN } from './RpcTypes';
@@ -30,6 +31,7 @@ export const createNormalizedMessageFromDiagnostic = makeCreateNormalizedMessage
3031
typescript
3132
);
3233
export const createNormalizedMessageFromRuleFailure = makeCreateNormalizedMessageFromRuleFailure();
34+
export const createNormalizedMessageFromInternalError = makeCreateNormalizedMessageFromInternalError();
3335

3436
const checker: IncrementalCheckerInterface =
3537
process.env.USE_INCREMENTAL_API === 'true'
@@ -64,9 +66,9 @@ async function run(cancellationToken: CancellationToken) {
6466
let diagnostics: NormalizedMessage[] = [];
6567
let lints: NormalizedMessage[] = [];
6668

67-
checker.nextIteration();
68-
6969
try {
70+
checker.nextIteration();
71+
7072
diagnostics = await checker.getDiagnostics(cancellationToken);
7173
if (checker.hasLinter()) {
7274
lints = checker.getLints(cancellationToken);
@@ -76,7 +78,7 @@ async function run(cancellationToken: CancellationToken) {
7678
return undefined;
7779
}
7880

79-
throw error;
81+
diagnostics.push(createNormalizedMessageFromInternalError(error));
8082
}
8183

8284
if (cancellationToken.isCancellationRequested()) {

test/integration/index.spec.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ function makeCommonTests(useTypescriptIncrementalApi) {
381381

382382
describe('[INTEGRATION] specific tests for useTypescriptIncrementalApi: false', function() {
383383
this.timeout(60000);
384+
var plugin;
384385

385386
function createCompiler(
386387
options,
@@ -390,6 +391,7 @@ describe('[INTEGRATION] specific tests for useTypescriptIncrementalApi: false',
390391
options = options || {};
391392
options.useTypescriptIncrementalApi = false;
392393
var compiler = helpers.createCompiler(options, happyPackMode, entryPoint);
394+
plugin = compiler.plugin;
393395
return compiler.webpack;
394396
}
395397

@@ -458,4 +460,18 @@ describe('[INTEGRATION] specific tests for useTypescriptIncrementalApi: false',
458460
callback();
459461
});
460462
});
463+
464+
it('should handle errors within the IncrementalChecker gracefully as diagnostic', callback => {
465+
var compiler = createCompiler();
466+
plugin.nodeArgs = [
467+
`--require`,
468+
`${path.resolve(__dirname, './mocks/IncrementalCheckerWithError.js')}`
469+
];
470+
471+
compiler.run(function(error, stats) {
472+
expect(stats.compilation.errors.length).to.equal(1);
473+
expect(stats.compilation.errors[0].message).to.include("I'm an error!");
474+
callback();
475+
});
476+
});
461477
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const mock = require('mock-require');
2+
3+
mock('../../../lib/IncrementalChecker', {
4+
IncrementalChecker: class {
5+
nextIteration() {
6+
throw new Error("I'm an error!");
7+
}
8+
}
9+
});

0 commit comments

Comments
 (0)