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

Commit

Permalink
Breaking: Implement parseForESLint() function (#412)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHenry authored Dec 13, 2017
1 parent 04e948f commit aec31cb
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 3,259 deletions.
12 changes: 2 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# TypeScript ESLint Parser (Experimental)
# TypeScript ESLint Parser

A parser that converts TypeScript into an [ESTree](https://github.com/estree/estree)-compatible form so it can be used in ESLint.
A parser that converts TypeScript source code into an [ESTree](https://github.com/estree/estree)-compatible form.

**Important:** This parser is not fully compatible with all ESLint rules and plugins. Some rules will improperly mark source code as failing or not find problems where it should.

Expand All @@ -20,18 +20,10 @@ The following ESLint rules will fail on acceptable code:
- no-undef [#77](https://github.com/eslint/typescript-eslint-parser/issues/77)
- no-unused-vars [#77](https://github.com/eslint/typescript-eslint-parser/issues/77)
- no-useless-constructor [#77](https://github.com/eslint/typescript-eslint-parser/issues/77)
- space-infix-ops [#224](https://github.com/eslint/typescript-eslint-parser/issues/224)

The follow ESLint plugins have issues when used with this parser:
- eslint-plugin-react [#213](https://github.com/eslint/typescript-eslint-parser/issues/213)
- eslint-plugin-import
- prefer-default-export - Will fail exports inside of Namespaces or Modules

The following TypeScript syntax will cause rules to fail or ESLint v3 to crash:
- Empty body functions
- Abstract methods
- Function overloading
- Declared functions

## Usage

Expand Down
3 changes: 2 additions & 1 deletion lib/ast-converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ module.exports = (ast, extra) => {
ast,
additionalOptions: {
errorOnUnknownASTType: extra.errorOnUnknownASTType || false,
useJSXTextNode: extra.useJSXTextNode || false
useJSXTextNode: extra.useJSXTextNode || false,
parseForESLint: extra.parseForESLint
}
});

Expand Down
19 changes: 19 additions & 0 deletions lib/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@ module.exports = function convert(config) {
});
}

/**
* If we are parsing for ESLint we need to perform a custom namespacing step
* on functions which have no body so that we do not break any ESLint rules which
* rely on them to have one.
*
* @param {ESTreeNode} functionNode the converted ESTreeNode
* @returns {void}
*/
function namespaceEmptyBodyFunctionForESLint(functionNode) {
if (!config.additionalOptions.parseForESLint || functionNode.body) {
return;
}
functionNode.type = `TSEmptyBody${functionNode.type}`;
}

/**
* Converts a TypeScript node into an ESTree node.
* @param {TSNode} child the child TSNode
Expand Down Expand Up @@ -634,6 +649,8 @@ module.exports = function convert(config) {
result.typeParameters = convertTSTypeParametersToTypeParametersDeclaration(node.typeParameters);
}

namespaceEmptyBodyFunctionForESLint(result);

// check for exports
result = nodeUtils.fixExports(node, result, ast);

Expand Down Expand Up @@ -954,6 +971,8 @@ module.exports = function convert(config) {
method.typeParameters = convertTSTypeParametersToTypeParametersDeclaration(node.typeParameters);
}

namespaceEmptyBodyFunctionForESLint(result.value);

break;

}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"babel-code-frame": "6.26.0",
"babylon": "7.0.0-beta.34",
"dedent": "0.7.0",
"eslint": "4.13.0",
"eslint": "4.13.1",
"eslint-config-eslint": "4.0.0",
"eslint-plugin-node": "5.2.1",
"eslint-release": "0.10.3",
Expand Down
25 changes: 21 additions & 4 deletions parser.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* @fileoverview Parser that converts TypeScript into ESTree format.
* @author Nicholas C. Zakas
* @author James Henry <https://github.com/JamesHenry>
* @copyright jQuery Foundation and other contributors, https://jquery.org/
* MIT License
*/
Expand Down Expand Up @@ -45,11 +46,13 @@ function resetExtra() {

/**
* Parses the given source code to produce a valid AST
* @param {mixed} code TypeScript code
* @param {Object} options configuration object for the parser
* @param {mixed} code TypeScript code
* @param {Object} options configuration object for the parser
* @param {Object} additionalParsingContext additional internal configuration
* @returns {Object} the AST
*/
function parse(code, options) {
function generateAST(code, options, additionalParsingContext) {
additionalParsingContext = additionalParsingContext || {};

const toString = String;

Expand Down Expand Up @@ -104,6 +107,13 @@ function parse(code, options) {
extra.log = Function.prototype;
}

/**
* Provide the context as to whether or not we are parsing for ESLint,
* specifically
*/
if (additionalParsingContext.isParseForESLint) {
extra.parseForESLint = true;
}
}

if (!isRunningSupportedTypeScriptVersion && !warnedAboutTSVersion) {
Expand Down Expand Up @@ -175,7 +185,14 @@ function parse(code, options) {

exports.version = require("./package.json").version;

exports.parse = parse;
exports.parse = function parse(code, options) {
return generateAST(code, options, { isParseForESLint: false });
};

exports.parseForESLint = function parseForESLint(code, options) {
const ast = generateAST(code, options, { isParseForESLint: true });
return { ast };
};

// Deep copy.
/* istanbul ignore next */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"use strict";

declare namespace FF {
class Foo extends Bar.Baz {
far(): any;
}
}

declare module "FF" {
class Foo extends Bar.Baz {
far(): any;
}
}

declare class Foo extends Bar.Baz {
far(): any;
}

declare namespace d3 {
export function select(selector: string): Selection<any>;
}
12 changes: 12 additions & 0 deletions tests/integration/external-fixtures/jsdoc-indent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @a
*/
foo;

/**
* @a
*/
/**
* a
*/
foo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export async function readFile(
filename: string,
options?: { flag?: string }
): Promise<Buffer>
export async function readFile(
filename: string,
options?: { encoding: BufferEncoding; flag?: string }
): Promise<string>
export async function readFile(
filename: string,
options?: { encoding?: string; flag?: string }
): Promise<Buffer | string> {
// ...
}
4 changes: 4 additions & 0 deletions tests/integration/external-fixtures/range-error-indent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class a {
/** @b {} c */
d = e => {}
}
Loading

0 comments on commit aec31cb

Please sign in to comment.