Skip to content
This repository was archived by the owner on Jan 14, 2019. It is now read-only.

Commit 36d2681

Browse files
authored
feat: add errorOnTypeScriptSyntaticAndSemanticIssues option (#57)
1 parent 969c0d3 commit 36d2681

15 files changed

+4369
-951
lines changed

src/ast-converter.ts

+1-14
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,12 @@
55
* @copyright jQuery Foundation and other contributors, https://jquery.org/
66
* MIT License
77
*/
8-
import convert, { getASTMaps, resetASTMaps } from './convert';
8+
import convert, { getASTMaps, resetASTMaps, convertError } from './convert';
99
import { convertComments } from './convert-comments';
1010
import nodeUtils from './node-utils';
1111
import ts from 'typescript';
1212
import { Extra } from './temp-types-based-on-js-source';
1313

14-
/**
15-
* Extends and formats a given error object
16-
* @param {Object} error the error object
17-
* @returns {Object} converted error object
18-
*/
19-
function convertError(error: any) {
20-
return nodeUtils.createError(
21-
error.file,
22-
error.start,
23-
error.message || error.messageText
24-
);
25-
}
26-
2714
export default (
2815
ast: ts.SourceFile,
2916
extra: Extra,

src/convert.ts

+13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ interface ConvertConfig {
3939
additionalOptions: ConvertAdditionalOptions;
4040
}
4141

42+
/**
43+
* Extends and formats a given error object
44+
* @param {Object} error the error object
45+
* @returns {Object} converted error object
46+
*/
47+
export function convertError(error: any) {
48+
return nodeUtils.createError(
49+
error.file,
50+
error.start,
51+
error.message || error.messageText
52+
);
53+
}
54+
4255
/**
4356
* Converts a TypeScript node into an ESTree node
4457
* @param {Object} config configuration options for the conversion

src/parser.ts

+41-5
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,16 @@ import {
1212
import semver from 'semver';
1313
import ts from 'typescript';
1414
import convert from './ast-converter';
15+
import { convertError } from './convert';
16+
import { Program } from './estree/spec';
17+
import util from './node-utils';
1518
import {
16-
Extra,
17-
ParserOptions,
19+
ESTreeComment,
1820
ESTreeToken,
19-
ESTreeComment
21+
Extra,
22+
ParserOptions
2023
} from './temp-types-based-on-js-source';
21-
import { Program } from './estree/spec';
22-
import util from './node-utils';
24+
import { getFirstSemanticOrSyntacticError } from './semantic-errors';
2325

2426
const packageJSON = require('../package.json');
2527

@@ -59,6 +61,7 @@ function resetExtra(): void {
5961
log: console.log,
6062
projects: [],
6163
errorOnUnknownASTType: false,
64+
errorOnTypeScriptSyntaticAndSemanticIssues: false,
6265
code: '',
6366
tsconfigRootDir: process.cwd(),
6467
extraFileExtensions: []
@@ -246,6 +249,18 @@ function generateAST<T extends ParserOptions = ParserOptions>(
246249
extra.errorOnUnknownASTType = true;
247250
}
248251

252+
/**
253+
* Retrieve semantic and syntactic diagnostics from the underlying TypeScript Program
254+
* and turn them into parse errors
255+
*/
256+
if (
257+
shouldGenerateServices &&
258+
typeof options.errorOnTypeScriptSyntaticAndSemanticIssues === 'boolean' &&
259+
options.errorOnTypeScriptSyntaticAndSemanticIssues
260+
) {
261+
extra.errorOnTypeScriptSyntaticAndSemanticIssues = true;
262+
}
263+
249264
if (typeof options.useJSXTextNode === 'boolean' && options.useJSXTextNode) {
250265
extra.useJSXTextNode = true;
251266
}
@@ -304,7 +319,23 @@ function generateAST<T extends ParserOptions = ParserOptions>(
304319
);
305320

306321
extra.code = code;
322+
323+
/**
324+
* Convert the AST
325+
*/
307326
const { estree, astMaps } = convert(ast, extra, shouldProvideParserServices);
327+
328+
/**
329+
* Even if TypeScript parsed the source code ok, and we had no problems converting the AST,
330+
* there may be other syntactic or semantic issues in the code that we can optionally report on.
331+
*/
332+
if (program && extra.errorOnTypeScriptSyntaticAndSemanticIssues) {
333+
const error = getFirstSemanticOrSyntacticError(program, ast);
334+
if (error) {
335+
throw convertError(error);
336+
}
337+
}
338+
308339
return {
309340
estree,
310341
program: shouldProvideParserServices ? program : undefined,
@@ -327,6 +358,11 @@ export function parse<T extends ParserOptions = ParserOptions>(
327358
code: string,
328359
options?: T
329360
) {
361+
if (options && options.errorOnTypeScriptSyntaticAndSemanticIssues) {
362+
throw new Error(
363+
`"errorOnTypeScriptSyntaticAndSemanticIssues" is only supported for parseAndGenerateServices()`
364+
);
365+
}
330366
return generateAST<T>(code, options).estree;
331367
}
332368

src/semantic-errors.ts

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import ts from 'typescript';
2+
3+
interface SemanticOrSyntacticError extends ts.Diagnostic {
4+
message: string;
5+
}
6+
7+
/**
8+
* By default, diagnostics from the TypeScript compiler contain all errors - regardless of whether
9+
* they are related to generic ECMAScript standards, or TypeScript-specific constructs.
10+
*
11+
* Therefore, we filter out all diagnostics, except for the ones we explicitly want to consider when
12+
* the user opts in to throwing errors on semantic issues.
13+
*/
14+
export function getFirstSemanticOrSyntacticError(
15+
program: ts.Program,
16+
ast: ts.SourceFile
17+
): SemanticOrSyntacticError | undefined {
18+
const supportedSyntacticDiagnostics = whitelistSupportedDiagnostics(
19+
program.getSyntacticDiagnostics(ast)
20+
);
21+
if (supportedSyntacticDiagnostics.length) {
22+
return convertDiagnosticToSemanticOrSyntacticError(
23+
supportedSyntacticDiagnostics[0]
24+
);
25+
}
26+
const supportedSemanticDiagnostics = whitelistSupportedDiagnostics(
27+
program.getSemanticDiagnostics(ast)
28+
);
29+
if (supportedSemanticDiagnostics.length) {
30+
return convertDiagnosticToSemanticOrSyntacticError(
31+
supportedSemanticDiagnostics[0]
32+
);
33+
}
34+
return undefined;
35+
}
36+
37+
function whitelistSupportedDiagnostics(
38+
diagnostics: ReadonlyArray<ts.DiagnosticWithLocation | ts.Diagnostic>
39+
): ReadonlyArray<ts.DiagnosticWithLocation | ts.Diagnostic> {
40+
return diagnostics.filter(diagnostic => {
41+
switch (diagnostic.code) {
42+
case 1123: // ts 3.2: "Variable declaration list cannot be empty."
43+
return true;
44+
}
45+
return false;
46+
});
47+
}
48+
49+
function convertDiagnosticToSemanticOrSyntacticError(
50+
diagnostic: ts.Diagnostic
51+
): SemanticOrSyntacticError {
52+
return {
53+
...diagnostic,
54+
message: ts.flattenDiagnosticMessageText(
55+
diagnostic.messageText,
56+
ts.sys.newLine
57+
)
58+
};
59+
}

src/temp-types-based-on-js-source.ts

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export interface ESTreeNodeLoc {
5858

5959
export interface Extra {
6060
errorOnUnknownASTType: boolean;
61+
errorOnTypeScriptSyntaticAndSemanticIssues: boolean;
6162
useJSXTextNode: boolean;
6263
tokens: null | ESTreeToken[];
6364
comment: boolean;
@@ -80,6 +81,7 @@ export interface ParserOptions {
8081
comment?: boolean;
8182
jsx?: boolean;
8283
errorOnUnknownASTType?: boolean;
84+
errorOnTypeScriptSyntaticAndSemanticIssues?: boolean;
8385
useJSXTextNode?: boolean;
8486
loggerFn?: Function | false;
8587
project?: string | string[];

tests/ast-alignment/fixtures-to-test.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ let fixturePatternConfigsToTest = [
190190
* as well, but the TypeScript compiler is so forgiving during parsing that typescript-estree
191191
* does not actually error on them and will produce an AST.
192192
*/
193-
'error-complex-destructured-spread-first' // babel parse errors
193+
'error-complex-destructured-spread-first', // babel parse errors
194+
'not-final-array' // babel parse errors
194195
]
195196
}),
196197

@@ -446,7 +447,8 @@ let fixturePatternConfigsToTest = [
446447
'decorator-on-enum-declaration', // babel parse errors
447448
'decorator-on-interface-declaration', // babel parse errors
448449
'interface-property-modifiers', // babel parse errors
449-
'enum-with-keywords' // babel parse errors
450+
'enum-with-keywords', // babel parse errors
451+
'solo-const' // babel parse errors
450452
]
451453
}),
452454

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
const

0 commit comments

Comments
 (0)