From 720aa43bd3c8f2ce018b2e9ca8f9a0b34ebcd331 Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Tue, 10 Aug 2021 11:23:30 +0200 Subject: [PATCH] handle hashbangs in javascript files fixes https://github.com/vercel/next.js/issues/27806 --- cspell.json | 1 + lib/CompatibilityPlugin.js | 25 +++++++++++++++++++++---- lib/NormalModule.js | 4 +++- lib/Parser.js | 1 + lib/javascript/JavascriptParser.js | 2 ++ test/cases/parsing/hashbang/file.js | 2 ++ test/cases/parsing/hashbang/file.mjs | 2 ++ test/cases/parsing/hashbang/index.js | 9 +++++++++ types.d.ts | 1 + 9 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 test/cases/parsing/hashbang/file.js create mode 100644 test/cases/parsing/hashbang/file.mjs create mode 100644 test/cases/parsing/hashbang/index.js diff --git a/cspell.json b/cspell.json index ddccec492..7aa2415b9 100644 --- a/cspell.json +++ b/cspell.json @@ -187,6 +187,7 @@ "kaios", "parallelism", "gitattributes", + "hashbang", "webassemblyjs", "fsevents", diff --git a/lib/CompatibilityPlugin.js b/lib/CompatibilityPlugin.js index b86a64ea5..54b04bfca 100644 --- a/lib/CompatibilityPlugin.js +++ b/lib/CompatibilityPlugin.js @@ -69,7 +69,8 @@ class CompatibilityPlugin { * @param {JavascriptParser} parser the parser * @returns {void} */ - const nestedWebpackRequireHandler = parser => { + const handler = parser => { + // Handle nested requires parser.hooks.preStatement.tap("CompatibilityPlugin", statement => { if ( statement.type === "FunctionDeclaration" && @@ -117,17 +118,33 @@ class CompatibilityPlugin { parser.state.module.addPresentationalDependency(dep); return true; }); + + // Handle hashbang + parser.hooks.program.tap( + "CompatibilityPlugin", + (program, comments) => { + if (comments.length === 0) return; + const c = comments[0]; + if (c.type === "Line" && c.range[0] === 0) { + if (parser.state.source.slice(0, 2).toString() !== "#!") return; + // this is a hashbang comment + const dep = new ConstDependency("//", 0); + dep.loc = c.loc; + parser.state.module.addPresentationalDependency(dep); + } + } + ); }; normalModuleFactory.hooks.parser .for("javascript/auto") - .tap("CompatibilityPlugin", nestedWebpackRequireHandler); + .tap("CompatibilityPlugin", handler); normalModuleFactory.hooks.parser .for("javascript/dynamic") - .tap("CompatibilityPlugin", nestedWebpackRequireHandler); + .tap("CompatibilityPlugin", handler); normalModuleFactory.hooks.parser .for("javascript/esm") - .tap("CompatibilityPlugin", nestedWebpackRequireHandler); + .tap("CompatibilityPlugin", handler); } ); } diff --git a/lib/NormalModule.js b/lib/NormalModule.js index 598f2107c..769554a1e 100644 --- a/lib/NormalModule.js +++ b/lib/NormalModule.js @@ -1025,7 +1025,9 @@ class NormalModule extends Module { let result; try { - result = this.parser.parse(this._ast || this._source.source(), { + const source = this._source.source(); + result = this.parser.parse(this._ast || source, { + source, current: this, module: this, compilation: compilation, diff --git a/lib/Parser.js b/lib/Parser.js index f5921c229..efd673d2b 100644 --- a/lib/Parser.js +++ b/lib/Parser.js @@ -12,6 +12,7 @@ /** * @typedef {Object} ParserStateBase + * @property {string | Buffer} source * @property {NormalModule} current * @property {NormalModule} module * @property {Compilation} compilation diff --git a/lib/javascript/JavascriptParser.js b/lib/javascript/JavascriptParser.js index 7d94e499f..f2d600c99 100644 --- a/lib/javascript/JavascriptParser.js +++ b/lib/javascript/JavascriptParser.js @@ -135,6 +135,8 @@ const defaultParserOptions = { locations: true, ecmaVersion: "latest", sourceType: "module", + // https://github.com/tc39/proposal-hashbang + allowHashBang: true, onComment: null }; diff --git a/test/cases/parsing/hashbang/file.js b/test/cases/parsing/hashbang/file.js new file mode 100644 index 000000000..2866673b1 --- /dev/null +++ b/test/cases/parsing/hashbang/file.js @@ -0,0 +1,2 @@ +#!/usr/bin/env node +module.exports = "ok"; diff --git a/test/cases/parsing/hashbang/file.mjs b/test/cases/parsing/hashbang/file.mjs new file mode 100644 index 000000000..97e357ad7 --- /dev/null +++ b/test/cases/parsing/hashbang/file.mjs @@ -0,0 +1,2 @@ +#!/usr/bin/env node +export default "ok"; diff --git a/test/cases/parsing/hashbang/index.js b/test/cases/parsing/hashbang/index.js new file mode 100644 index 000000000..f4bb67453 --- /dev/null +++ b/test/cases/parsing/hashbang/index.js @@ -0,0 +1,9 @@ +it("should load a file with hashbang", function () { + var result = require("./file.js"); + expect(result).toEqual("ok"); +}); + +import result from "./file.mjs"; +it("should load a module with hashbang", function () { + expect(result).toEqual("ok"); +}); diff --git a/types.d.ts b/types.d.ts index 754159d15..386899a2c 100644 --- a/types.d.ts +++ b/types.d.ts @@ -8470,6 +8470,7 @@ declare interface ParserOptionsByModuleTypeUnknown { } type ParserState = Record & ParserStateBase; declare interface ParserStateBase { + source: string | Buffer; current: NormalModule; module: NormalModule; compilation: Compilation;