From 5a269369f39e8c0f4c2f8119df2ef7be73b4bf51 Mon Sep 17 00:00:00 2001 From: Antoine du Hamel Date: Sun, 21 Mar 2021 15:41:24 +0100 Subject: [PATCH] module: clarify CJS global-like variables not defined error message Fixes: https://github.com/nodejs/node/issues/33741 --- lib/internal/modules/esm/module_job.js | 41 ++++++++++++++++++- ...esm-undefined-cjs-global-like-variables.js | 28 +++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 test/es-module/test-esm-undefined-cjs-global-like-variables.js diff --git a/lib/internal/modules/esm/module_job.js b/lib/internal/modules/esm/module_job.js index ebae1940502b4b..f5b1f2495b32a5 100644 --- a/lib/internal/modules/esm/module_job.js +++ b/lib/internal/modules/esm/module_job.js @@ -4,6 +4,7 @@ const { ArrayPrototypeJoin, ArrayPrototypeMap, ArrayPrototypePush, + ArrayPrototypeSome, FunctionPrototype, ObjectSetPrototypeOf, PromiseAll, @@ -12,10 +13,12 @@ const { ReflectApply, SafeArrayIterator, SafeSet, + StringPrototypeEndsWith, StringPrototypeIncludes, StringPrototypeMatch, StringPrototypeReplace, StringPrototypeSplit, + StringPrototypeStartsWith, } = primordials; const { ModuleWrap } = internalBinding('module_wrap'); @@ -28,6 +31,19 @@ const noop = FunctionPrototype; let hasPausedEntry = false; +const CJSGlobalLike = [ + 'require', + 'module', + 'exports', + '__filename', + '__dirname', +]; +const isCommonJSGlobalLikeNotDefinedError = (errorMessage) => + ArrayPrototypeSome( + CJSGlobalLike, + (globalLike) => StringPrototypeStartsWith(errorMessage, globalLike) + ); + /* A ModuleJob tracks the loading of a single Module, and the ModuleJobs of * its dependencies, over time. */ class ModuleJob { @@ -151,7 +167,30 @@ class ModuleJob { await this.instantiate(); const timeout = -1; const breakOnSigint = false; - await this.module.evaluate(timeout, breakOnSigint); + try { + await this.module.evaluate(timeout, breakOnSigint); + } catch (e) { + if (e.name === 'ReferenceError' && + StringPrototypeEndsWith(e.message, ' is not defined') && + isCommonJSGlobalLikeNotDefinedError(e.message)) { + e.message += ' in ES module scope'; + + if (StringPrototypeStartsWith(e.message, 'require')) { + e.message += '. You can use `await import` instead'; + } + + if (StringPrototypeEndsWith(this.module.url, '.js') && + StringPrototypeStartsWith(this.module.url, 'file://')) { + e.message += + '. It seems you are trying to load a file using `.js` extension ' + + 'inside a folder containing a `package.json` that includes ' + + '`"type": "module"`; you need to use the `.cjs` extension to ' + + 'load the file as a CommonJS module, or add a `package.json` ' + + 'file with ``"type": "commonjs"`.'; + } + } + throw e; + } return { module: this.module }; } } diff --git a/test/es-module/test-esm-undefined-cjs-global-like-variables.js b/test/es-module/test-esm-undefined-cjs-global-like-variables.js new file mode 100644 index 00000000000000..8a022f8b0df0ba --- /dev/null +++ b/test/es-module/test-esm-undefined-cjs-global-like-variables.js @@ -0,0 +1,28 @@ +'use strict'; +const common = require('../common'); +const fixtures = require('../common/fixtures'); +const assert = require('assert'); + +assert.rejects( + import('data:text/javascript,require;'), + // eslint-disable-next-line node-core/no-unescaped-regexp-dot + /use .await import. instead/ +).then(common.mustCall()); + +assert.rejects( + import('data:text/javascript,exports={};'), + // eslint-disable-next-line node-core/no-unescaped-regexp-dot + /^(?!use .await import. instead).*$/ +).then(common.mustCall()); + +assert.rejects( + import('data:text/javascript,require;//.js'), + // eslint-disable-next-line node-core/no-unescaped-regexp-dot + /^(?!It seems you are trying to load a file using `.js` extension).*$/ +).then(common.mustCall()); + +assert.rejects( + import(fixtures.path('/es-modules/package-type-module/cjs.js')), + // eslint-disable-next-line node-core/no-unescaped-regexp-dot + /use the .\.cjs. extension to load the file as a CommonJS module/ +).then(common.mustCall());