From f2bf98f8f3acd77ab53d092c5e095eaf6a89dba4 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Tue, 16 Nov 2021 09:21:34 -0800 Subject: [PATCH 1/2] feat: Add sourceType:commonjs support (fixes #519) --- README.md | 4 ++-- lib/espree.js | 8 ++++++-- lib/options.js | 12 +++++++++++- tests/lib/parse.js | 31 ++++++++++++++++++++++++------- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d4c49c75..c5120711 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ const options = { // You can also set "latest" to use the most recently supported version. ecmaVersion: 5, - // specify which type of script you're parsing ("script" or "module") + // specify which type of script you're parsing ("script", "module", or "commonjs") sourceType: "script", // specify additional language features @@ -154,7 +154,7 @@ const options = { // enable JSX parsing jsx: false, - // enable return in global scope + // enable return in global scope (set to true automatically when sourceType is "commonjs") globalReturn: false, // enable implied strict mode (if ecmaVersion >= 5) diff --git a/lib/espree.js b/lib/espree.js index e0be3b5a..dd897f34 100644 --- a/lib/espree.js +++ b/lib/espree.js @@ -56,6 +56,8 @@ export default () => Parser => { code = String(code); } + // save original source type in case of commonjs + const originalSourceType = opts.sourceType; const options = normalizeOptions(opts); const ecmaFeatures = options.ecmaFeatures || {}; const tokenTranslator = @@ -74,7 +76,7 @@ export default () => Parser => { allowReserved: options.allowReserved, // Truthy value is true for backward compatibility. - allowReturnOutsideFunction: Boolean(ecmaFeatures.globalReturn), + allowReturnOutsideFunction: options.allowReturnOutsideFunction, // Collect tokens onToken: token => { @@ -98,6 +100,8 @@ export default () => Parser => { } }, code); + this.originalSourceType = originalSourceType || options.sourceType; + // Initialize internal state. this[STATE] = { tokens: tokenTranslator ? [] : null, @@ -144,7 +148,7 @@ export default () => Parser => { const extra = this[STATE]; const program = super.parse(); - program.sourceType = this.options.sourceType; + program.sourceType = this.originalSourceType; if (extra.comments) { program.comments = extra.comments; diff --git a/lib/options.js b/lib/options.js index acc1e3a2..02c1a750 100644 --- a/lib/options.js +++ b/lib/options.js @@ -73,6 +73,11 @@ function normalizeSourceType(sourceType = "script") { if (sourceType === "script" || sourceType === "module") { return sourceType; } + + if (sourceType === "commonjs") { + return "script"; + } + throw new Error("Invalid sourceType."); } @@ -88,15 +93,20 @@ export function normalizeOptions(options) { const ranges = options.range === true; const locations = options.loc === true; const allowReserved = ecmaVersion === 3 ? "never" : false; + const ecmaFeatures = options.ecmaFeatures || {}; + const allowReturnOutsideFunction = options.sourceType === "commonjs" || + Boolean(ecmaFeatures.globalReturn); 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, - allowReserved + allowReserved, + allowReturnOutsideFunction }); } diff --git a/tests/lib/parse.js b/tests/lib/parse.js index 50c5345e..8fe385f7 100644 --- a/tests/lib/parse.js +++ b/tests/lib/parse.js @@ -41,15 +41,32 @@ describe("parse()", () => { }); - describe("modules", () => { + describe("sourceType", () => { - it("should have correct column number when strict mode error occurs", () => { + describe("module", () => { - try { - espree.parse("function fn(a, a) {\n}", { ecmaVersion: 6, sourceType: "module" }); - } catch (err) { - assert.strictEqual(err.column, 16); - } + it("should have correct column number when strict mode error occurs", () => { + + try { + espree.parse("function fn(a, a) {\n}", { ecmaVersion: 6, sourceType: "module" }); + } catch (err) { + assert.strictEqual(err.column, 16); + } + }); + + }); + + describe("commonjs", () => { + + it("should parse top-level return", () => { + espree.parse("return;", { sourceType: "commonjs" }); + }); + + it("should have sourceType:commonjs on Program node", () => { + const result = espree.parse("return;", { sourceType: "commonjs" }); + + assert.strictEqual(result.sourceType, "commonjs"); + }); }); }); From e585605a1373e30102ac57cc0a0abca10ddbf420 Mon Sep 17 00:00:00 2001 From: "Nicholas C. Zakas" Date: Fri, 19 Nov 2021 12:47:20 -0800 Subject: [PATCH 2/2] Move originalSourceType into Espree state --- lib/espree.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/espree.js b/lib/espree.js index dd897f34..786d89fa 100644 --- a/lib/espree.js +++ b/lib/espree.js @@ -100,10 +100,13 @@ export default () => Parser => { } }, code); - this.originalSourceType = originalSourceType || options.sourceType; - - // Initialize internal state. + /* + * Data that is unique to Espree and is not represented internally in + * Acorn. We put all of this data into a symbol property as a way to + * avoid potential naming conflicts with future versions of Acorn. + */ this[STATE] = { + originalSourceType: originalSourceType || options.sourceType, tokens: tokenTranslator ? [] : null, comments: options.comment === true ? [] : null, impliedStrict: ecmaFeatures.impliedStrict === true && this.options.ecmaVersion >= 5, @@ -148,7 +151,7 @@ export default () => Parser => { const extra = this[STATE]; const program = super.parse(); - program.sourceType = this.originalSourceType; + program.sourceType = extra.originalSourceType; if (extra.comments) { program.comments = extra.comments;