Skip to content

Commit

Permalink
Breaking: validate parser options (fixes #384) (#412)
Browse files Browse the repository at this point in the history
* Breaking: validate parser options (fixes #384)
it will throw an error if any of the conditions is true:

+ ecmaVersion is invalid
+ sourceType is invalid
+ ecmaVersion < 6 & sourceType: "module"

* fix failing tests

* Update lib/espree.js

Co-Authored-By: aladdin-add <[email protected]>

* Update tests/lib/ecma-version.js

Co-Authored-By: aladdin-add <[email protected]>

* chore: update test

* Chore: move some assginment to func normalizeOptions

* Update lib/espree.js

Co-Authored-By: aladdin-add <[email protected]>

* Update lib/espree.js

Co-Authored-By: aladdin-add <[email protected]>

* Update ecma-version.js
  • Loading branch information
aladdin-add authored and not-an-aardvark committed Apr 1, 2019
1 parent a7895ea commit 493d464
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 15 deletions.
61 changes: 47 additions & 14 deletions lib/espree.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ const STATE = Symbol("espree's internal state");
const ESPRIMA_FINISH_NODE = Symbol("espree's esprimaFinishNode");
const tokTypes = Object.assign({}, acorn.tokTypes, jsx.tokTypes);


/**
* Normalize ECMAScript version from the initial config
* @param {number} ecmaVersion ECMAScript version from the initial config
* @throws {Error} throws an error if the ecmaVersion is invalid.
* @returns {number} normalized ECMAScript version
*/
function normalizeEcmaVersion(ecmaVersion) {
function normalizeEcmaVersion(ecmaVersion = DEFAULT_ECMA_VERSION) {
if (typeof ecmaVersion === "number") {
let version = ecmaVersion;

Expand All @@ -35,12 +37,42 @@ function normalizeEcmaVersion(ecmaVersion) {
case 10:
return version;

default:
throw new Error("Invalid ecmaVersion.");
// no default
}
} else {
return DEFAULT_ECMA_VERSION;
}

throw new Error("Invalid ecmaVersion.");
}

/**
* Normalize sourceType from the initial config
* @param {string} sourceType to normalize
* @throws {Error} throw an error if sourceType is invalid
* @returns {string} normalized sourceType
*/
function normalizeSourceType(sourceType = "script") {
if (sourceType === "script" || sourceType === "module") {
return sourceType;
}
throw new Error("Invalid sourceType.");
}

/**
* Normalize parserOptions
* @param {Object} options the parser options to normalize
* @throws {Error} throw an error if found invalid option.
* @returns {Object} normalized options
*/
function normalizeOptions(options) {
const ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);
const sourceType = normalizeSourceType(options.sourceType);
const ranges = options.range === true;
const locations = options.loc === true;

if (sourceType === "module" && ecmaVersion < 6) {
throw new Error("sourceType 'module' is not supported when ecmaVersion < 2015. Consider adding `{ ecmaVersion: 2015 }` to the parser options.");
}
return Object.assign({}, options, { ecmaVersion, sourceType, ranges, locations });
}

/**
Expand Down Expand Up @@ -77,28 +109,29 @@ function convertAcornCommentToEsprimaComment(block, text, start, end, startLoc,
}

module.exports = () => Parser => class Espree extends Parser {
constructor(options, code) {
if (typeof options !== "object" || options === null) {
options = {};
constructor(opts, code) {
if (typeof opts !== "object" || opts === null) {
opts = {};
}
if (typeof code !== "string" && !(code instanceof String)) {
code = String(code);
}

const options = normalizeOptions(opts);
const ecmaFeatures = options.ecmaFeatures || {};
const ecmaVersion = normalizeEcmaVersion(options.ecmaVersion);
const isModule = options.sourceType === "module";
const tokenTranslator =
options.tokens === true
? new TokenTranslator(tokTypes, code)
: null;

// Initialize acorn parser.
super({
ecmaVersion: isModule ? Math.max(6, ecmaVersion) : ecmaVersion,
sourceType: isModule ? "module" : "script",
ranges: options.range === true,
locations: options.loc === true,

// TODO: use {...options} when spread is supported(Node.js >= 8.3.0).
ecmaVersion: options.ecmaVersion,
sourceType: options.sourceType,
ranges: options.ranges,
locations: options.locations,

// Truthy value is true for backward compatibility.
allowReturnOutsideFunction: Boolean(ecmaFeatures.globalReturn),
Expand Down
24 changes: 24 additions & 0 deletions tests/lib/ecma-version.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,30 @@ describe("ecmaVersion", () => {
}, /Invalid ecmaVersion/);
});

it("Should throw error when non-numeric year is provided", () => {
assert.throws(() => {
espree.parse(
"let foo = bar;", {
ecmaVersion: "2015",
comment: true,
tokens: true,
range: true,
loc: true
}
);
}, /Invalid ecmaVersion/);
});

it("Should throw error when using module in pre-ES6", () => {
assert.throws(() => {
espree.parse(
"let foo = bar;", {
ecmaVersion: 5,
sourceType: "module"
}
);
}, /sourceType 'module' is not supported when ecmaVersion < 2015/);
});
});

});
2 changes: 1 addition & 1 deletion tests/lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ describe("parse()", () => {
it("should have correct column number when strict mode error occurs", () => {

try {
espree.parse("function fn(a, a) {\n}", { sourceType: "module" });
espree.parse("function fn(a, a) {\n}", { ecmaVersion: 6, sourceType: "module" });
} catch (err) {
assert.strictEqual(err.column, 16);
}
Expand Down

0 comments on commit 493d464

Please sign in to comment.