Skip to content

Commit

Permalink
Breaking: Implement 'ecmaVersion: latest' (fixes #495) (#499)
Browse files Browse the repository at this point in the history
* Breaking: Implement 'ecmaVersion: latest' (fixes #495)

* Update lib/options.js

Co-authored-by: Brandon Mills <btmills@users.noreply.github.com>

* Add extra test

Co-authored-by: Brandon Mills <btmills@users.noreply.github.com>
  • Loading branch information
nzakas and btmills committed Jun 9, 2021
1 parent 8294427 commit b068cea
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 45 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -140,8 +140,9 @@ const options = {
// create a top-level tokens array containing all tokens
tokens: false,

// Set to 3, 5 (default), 6, 7, 8, 9, 10, 11, or 12 to specify the version of ECMAScript syntax you want to use.
// Set to 3, 5, 6, 7, 8, 9, 10, 11, or 12 to specify the version of ECMAScript syntax you want to use.
// You can also set to 2015 (same as 6), 2016 (same as 7), 2017 (same as 8), 2018 (same as 9), 2019 (same as 10), 2020 (same as 11), or 2021 (same as 12) to use the year-based naming.
// You can also set "latest" (the default) to use the most recently supported version.
ecmaVersion: 5,

// specify which type of script you're parsing ("script" or "module")
Expand Down
46 changes: 23 additions & 23 deletions lib/options.js
Expand Up @@ -7,7 +7,6 @@
// Helpers
//------------------------------------------------------------------------------

const DEFAULT_ECMA_VERSION = 5;
const SUPPORTED_VERSIONS = [
3,
5,
Expand All @@ -20,18 +19,35 @@ const SUPPORTED_VERSIONS = [
12
];

/**
* Get the latest ECMAScript version supported by Espree.
* @returns {number} The latest ECMAScript version.
*/
export function getLatestEcmaVersion() {
return SUPPORTED_VERSIONS[SUPPORTED_VERSIONS.length - 1];
}

/**
* Get the list of ECMAScript versions supported by Espree.
* @returns {number[]} An array containing the supported ECMAScript versions.
*/
export function getSupportedEcmaVersions() {
return [...SUPPORTED_VERSIONS];
}

/**
* Normalize ECMAScript version from the initial config
* @param {number} ecmaVersion ECMAScript version from the initial config
* @param {(number|"latest")} 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 = DEFAULT_ECMA_VERSION) {
if (typeof ecmaVersion !== "number") {
throw new Error(`ecmaVersion must be a number. Received value of type ${typeof ecmaVersion} instead.`);
}
function normalizeEcmaVersion(ecmaVersion = getLatestEcmaVersion()) {

let version = ecmaVersion;
let version = ecmaVersion === "latest" ? getLatestEcmaVersion() : ecmaVersion;

if (typeof version !== "number") {
throw new Error(`ecmaVersion must be a number or "latest". Received value of type ${typeof ecmaVersion} instead.`);
}

// Calculate ECMAScript edition number from official year version starting with
// ES2015, which corresponds with ES6 (or a difference of 2009).
Expand Down Expand Up @@ -76,19 +92,3 @@ export function normalizeOptions(options) {
}
return Object.assign({}, options, { ecmaVersion, sourceType, ranges, locations });
}

/**
* Get the latest ECMAScript version supported by Espree.
* @returns {number} The latest ECMAScript version.
*/
export function getLatestEcmaVersion() {
return SUPPORTED_VERSIONS[SUPPORTED_VERSIONS.length - 1];
}

/**
* Get the list of ECMAScript versions supported by Espree.
* @returns {number[]} An array containing the supported ECMAScript versions.
*/
export function getSupportedEcmaVersions() {
return [...SUPPORTED_VERSIONS];
}
37 changes: 36 additions & 1 deletion tests/lib/ecma-version.js
Expand Up @@ -173,7 +173,7 @@ describe("ecmaVersion", () => {
loc: true
}
);
}, /ecmaVersion must be a number. Received value of type string instead/u);
}, /ecmaVersion must be a number or "latest". Received value of type string instead/u);
});

it("Should throw error when using module in pre-ES6", () => {
Expand All @@ -186,6 +186,41 @@ describe("ecmaVersion", () => {
);
}, /sourceType 'module' is not supported when ecmaVersion < 2015/u);
});

it("Should allow 'latest' as value", () => {
const expected = espree.parse(
"let foo = bar;", {
ecmaVersion: espree.latestEcmaVersion,
sourceType: "module"
}
);

const actual = espree.parse(
"let foo = bar;", {
ecmaVersion: "latest",
sourceType: "module"
}
);

assert.deepStrictEqual(actual, expected);
});

it("Should use the latestEcmaVersion as the default for ecmaVersion", () => {
const expected = espree.parse(
"let foo = bar;", {
ecmaVersion: espree.latestEcmaVersion,
sourceType: "module"
}
);

const actual = espree.parse(
"let foo = bar;", {
sourceType: "module"
}
);

assert.deepStrictEqual(actual, expected);
});
});

});
2 changes: 1 addition & 1 deletion tests/lib/libraries.js
Expand Up @@ -37,8 +37,8 @@ describe("Libraries", () => {
it("should produce correct AST when parsed", () => {
const output = shelljs.cat(`${filename}.result.json`);
const input = shelljs.cat(filename);

const result = JSON.stringify(tester.getRaw(espree.parse(input, {
ecmaVersion: 5,
loc: true,
range: true,
tokens: true
Expand Down
14 changes: 11 additions & 3 deletions tests/lib/options.js
Expand Up @@ -8,20 +8,28 @@
//------------------------------------------------------------------------------

import assert from "assert";
import { normalizeOptions } from "../../lib/options.js";
import { normalizeOptions, getLatestEcmaVersion } from "../../lib/options.js";


//------------------------------------------------------------------------------
// Tests
//------------------------------------------------------------------------------

describe("normalizeOptions", () => {
it("should set ecmaVersion to 5 if it wasn't specified", () => {
it("should set ecmaVersion to latestEcmaVersion if it wasn't specified", () => {
const option = {};

const output = normalizeOptions(option);

assert.strictEqual(output.ecmaVersion, 5);
assert.strictEqual(output.ecmaVersion, getLatestEcmaVersion());
});

it("should set ecmaVersion to latestEcmaVersion if ecmaVersion: latest is passed", () => {
const output = normalizeOptions({
ecmaVersion: "latest"
});

assert.strictEqual(output.ecmaVersion, getLatestEcmaVersion());
});

it("should throw error for sourceType module and ecmaVersion < 6", () => {
Expand Down
15 changes: 5 additions & 10 deletions tests/lib/parse.js
Expand Up @@ -23,15 +23,10 @@ describe("parse()", () => {

describe("ecmaVersion", () => {

it("should be 5 if not specified", () => {
it("should be latestEcmaVersion if not specified", () => {

// `ecmaVersion: 3` would throw on getters/setters
espree.parse("var foo = { get bar() {} }");

// needs `ecmaVersion: 6` or higher
assert.throws(() => {
espree.parse("let foo");
});
// `ecmaVersion: 5` would throw on async
espree.parse("let foo = { async bar() {} }");
});

});
Expand All @@ -54,14 +49,14 @@ describe("parse()", () => {
it("should throw an error when using the y regex flag", () => {

assert.throws(() => {
espree.parse("/./y");
espree.parse("/./y", { ecmaVersion: 5 });
});
});

it("should throw an error when using the u regex flag", () => {

assert.throws(() => {
espree.parse("/./u");
espree.parse("/./u", { ecmaVersion: 5 });
});
});

Expand Down
8 changes: 2 additions & 6 deletions tests/lib/tokenize.js
Expand Up @@ -30,14 +30,10 @@ import tildeOperatorTokens from "../fixtures/tokenize/tilde-operator.tokens.js";

describe("tokenize()", () => {

it("should have `ecmaVersion: 5` as default", () => {

// FIXME: is there a way to test that it isn't `ecmaVersion: 3`?
it("should have latestEcmaVersion as default", () => {

// needs `ecmaVersion: 6` or higher
assert.throws(() => {
espree.tokenize("`template`");
});
espree.tokenize("`template`");
});

it("should produce tokens when using let", () => {
Expand Down

0 comments on commit b068cea

Please sign in to comment.