Skip to content

Commit

Permalink
New: add a Linter#defineParser function (#9321)
Browse files Browse the repository at this point in the history
  • Loading branch information
CompuIves authored and not-an-aardvark committed Nov 26, 2017
1 parent 5619910 commit 28c9c8e
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 22 deletions.
20 changes: 20 additions & 0 deletions docs/developer-guide/nodejs-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,26 @@ Map {
*/
```

### Linter#defineParser

Each instance of `Linter` holds a map of custom parsers. If you want to define a parser programmatically you can add this function
with the name of the parser as first argument and the [parser object](/docs/developer-guide/working-with-plugins#working-with-custom-parsers) as second argument.

If during linting the parser is not found, it will fallback to `require(parserId)`.

```js
const Linter = require("eslint").Linter;
const linter = new Linter();

linter.defineParser("my-custom-parser", {
parse(code, options) {
// ...
}
});

const results = linter.verify("// some source text", { parser: "my-custom-parser" });
```

### Linter#version

Each instance of `Linter` has a `version` property containing the semantic version number of ESLint that the `Linter` instance is from.
Expand Down
51 changes: 29 additions & 22 deletions lib/linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -507,13 +507,13 @@ function getRuleOptions(ruleConfig) {
* as possible
* @param {string} text The text to parse.
* @param {Object} providedParserOptions Options to pass to the parser
* @param {string} parserName The name of the parser
* @param {Object} parser The parser module
* @param {string} filePath The path to the file being parsed.
* @returns {{success: false, error: Problem}|{success: true,ast: ASTNode, services: Object}}
* An object containing the AST and parser services if parsing was successful, or the error if parsing failed
* @private
*/
function parse(text, providedParserOptions, parserName, filePath) {
function parse(text, providedParserOptions, parser, filePath) {

const parserOptions = Object.assign({}, providedParserOptions, {
loc: true,
Expand All @@ -524,25 +524,6 @@ function parse(text, providedParserOptions, parserName, filePath) {
filePath
});

let parser;

try {
parser = require(parserName);
} catch (ex) {
return {
success: false,
error: {
ruleId: null,
fatal: true,
severity: 2,
source: null,
message: ex.message,
line: 0,
column: 0
}
};
}

/*
* Check for parsing errors first. If there's a parsing error, nothing
* else can happen. However, a parsing error does not throw an error
Expand Down Expand Up @@ -706,6 +687,7 @@ module.exports = class Linter {
this.version = pkg.version;

this.rules = new Rules();
this._parsers = new Map();
this.environments = new Environments();
}

Expand Down Expand Up @@ -785,10 +767,25 @@ module.exports = class Linter {
return [];
}

let parser;

try {
parser = this._parsers.get(config.parser) || require(config.parser);
} catch (ex) {
return [{
ruleId: null,
fatal: true,
severity: 2,
source: null,
message: ex.message,
line: 0,
column: 0
}];
}
const parseResult = parse(
stripUnicodeBOM(text).replace(astUtils.SHEBANG_MATCHER, (match, captured) => `//${captured}`),
config.parserOptions,
config.parser,
parser,
filename
);

Expand Down Expand Up @@ -1036,6 +1033,16 @@ module.exports = class Linter {
return this.rules.getAllLoadedRules();
}

/**
* Define a new parser module
* @param {any} parserId Name of the parser
* @param {any} parserModule The parser object
* @returns {void}
*/
defineParser(parserId, parserModule) {
this._parsers.set(parserId, parserModule);
}

/**
* Performs multiple autofix passes over the text until as many fixes as possible
* have been applied.
Expand Down
27 changes: 27 additions & 0 deletions tests/lib/linter.js
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,33 @@ describe("Linter", () => {

});

describe("when a custom parser is defined using defineParser", () => {

it("should be able to define a custom parser", () => {
const parser = {
parseForESLint: function parse(code, options) {
return {
ast: require("espree").parse(code, options),
services: {
test: {
getMessage() {
return "Hi!";
}
}
}
};
}
};

linter.defineParser("test-parser", parser);
const config = { rules: {}, parser: "test-parser" };
const messages = linter.verify("0", config, filename);

assert.strictEqual(messages.length, 0);
});

});

describe("when config has parser", () => {

// custom parser unsupported in browser, only test in Node
Expand Down

0 comments on commit 28c9c8e

Please sign in to comment.