From 833803b25fbaa3e6cbde1c1426d607b25fa60e7c Mon Sep 17 00:00:00 2001 From: Teddy Katz Date: Fri, 31 Mar 2017 21:59:57 -0400 Subject: [PATCH] Fix: don't mutate user-provided configs (fixes #329) --- espree.js | 26 +++++++++++++++++++++++--- tests/lib/parse.js | 4 ++++ tests/lib/tokenize.js | 4 ++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/espree.js b/espree.js index 87074c8d..4a49b2a9 100644 --- a/espree.js +++ b/espree.js @@ -71,6 +71,26 @@ var lookahead, extra, lastToken; +/** + * Object.assign polyfill for Node < 4 + * @param {Object} target The target object + * @param {...Object} sources Sources for the object + * @returns {Object} `target` after being mutated + */ +var assign = Object.assign || function assign(target) { + for (var argIndex = 1; argIndex < arguments.length; argIndex++) { + if (arguments[argIndex] !== null && typeof arguments[argIndex] === "object") { + var keys = Object.keys(arguments[argIndex]); + + for (var keyIndex = 0; keyIndex < keys.length; keyIndex++) { + target[keys[keyIndex]] = arguments[argIndex][keys[keyIndex]]; + } + } + } + + return target; +}; + /** * Resets the extra object to its default. * @returns {void} @@ -515,7 +535,7 @@ function tokenize(code, options) { lookahead = null; // Options matching. - options = options || {}; + options = assign({}, options); var acornOptions = { ecmaVersion: DEFAULT_ECMA_VERSION, @@ -551,7 +571,7 @@ function tokenize(code, options) { // apply parsing flags if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") { - extra.ecmaFeatures = options.ecmaFeatures; + extra.ecmaFeatures = assign({}, options.ecmaFeatures); impliedStrict = extra.ecmaFeatures.impliedStrict; extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict; } @@ -687,7 +707,7 @@ function parse(code, options) { // apply parsing flags after sourceType to allow overriding if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") { - extra.ecmaFeatures = options.ecmaFeatures; + extra.ecmaFeatures = assign({}, options.ecmaFeatures); impliedStrict = extra.ecmaFeatures.impliedStrict; extra.ecmaFeatures.impliedStrict = typeof impliedStrict === "boolean" && impliedStrict; if (options.ecmaFeatures.globalReturn) { diff --git a/tests/lib/parse.js b/tests/lib/parse.js index 5e64bf50..9eb1970c 100644 --- a/tests/lib/parse.js +++ b/tests/lib/parse.js @@ -77,5 +77,9 @@ describe("parse()", function() { assert.deepEqual([ast.loc.end.line, ast.loc.end.column], [1, 5]); }); + it("should not mutate config", function() { + espree.parse("foo", Object.freeze({ ecmaFeatures: Object.freeze({}) })); + }); + }); }); diff --git a/tests/lib/tokenize.js b/tests/lib/tokenize.js index 50c865b2..7042091e 100644 --- a/tests/lib/tokenize.js +++ b/tests/lib/tokenize.js @@ -182,4 +182,8 @@ describe("tokenize()", function() { ); }); + it("should not mutate config", function() { + espree.tokenize("foo", Object.freeze({ ecmaFeatures: Object.freeze({}) })); + }); + });