summaryrefslogtreecommitdiff
path: root/ext/node
diff options
context:
space:
mode:
authorsnek <snek@deno.com>2024-09-10 13:12:36 -0700
committerGitHub <noreply@github.com>2024-09-10 20:12:36 +0000
commitf9007d3386bbe9f709ce413ac0cf099b86d4c4bf (patch)
tree3254119b37ef434062c431b2b4964d9317fb5f1a /ext/node
parentee3829a7787b337b2a7e98251b40d5937b2c255b (diff)
feat: require(esm) (#25501)
implement require(esm) using `op_import_sync` from deno_core. possible future changes: - cts and mts - replace Deno.core.evalContext to optimize esm syntax detection Fixes: https://github.com/denoland/deno/issues/25487
Diffstat (limited to 'ext/node')
-rw-r--r--ext/node/lib.rs1
-rw-r--r--ext/node/ops/require.rs27
-rw-r--r--ext/node/polyfills/01_require.js65
3 files changed, 52 insertions, 41 deletions
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 39b06e9fd..f569f5b2a 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -339,6 +339,7 @@ deno_core::extension!(deno_node,
ops::os::op_homedir<P>,
op_node_build_os,
op_npm_process_state,
+ ops::require::op_require_can_parse_as_esm,
ops::require::op_require_init_paths,
ops::require::op_require_node_module_paths<P>,
ops::require::op_require_proxy_path,
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index 4f88c1913..3578719d0 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -6,6 +6,7 @@ use deno_core::error::AnyError;
use deno_core::normalize_path;
use deno_core::op2;
use deno_core::url::Url;
+use deno_core::v8;
use deno_core::JsRuntimeInspector;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
@@ -614,3 +615,29 @@ fn url_to_file_path(url: &Url) -> Result<PathBuf, AnyError> {
}
}
}
+
+#[op2(fast)]
+pub fn op_require_can_parse_as_esm(
+ scope: &mut v8::HandleScope,
+ #[string] source: &str,
+) -> bool {
+ let scope = &mut v8::TryCatch::new(scope);
+ let Some(source) = v8::String::new(scope, source) else {
+ return false;
+ };
+ let origin = v8::ScriptOrigin::new(
+ scope,
+ source.into(),
+ 0,
+ 0,
+ false,
+ 0,
+ None,
+ true,
+ false,
+ true,
+ None,
+ );
+ let mut source = v8::script_compiler::Source::new(source, Some(&origin));
+ v8::script_compiler::compile_module(scope, &mut source).is_some()
+}
diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js
index e4a781cc4..b84765d31 100644
--- a/ext/node/polyfills/01_require.js
+++ b/ext/node/polyfills/01_require.js
@@ -4,9 +4,11 @@
import { core, internals, primordials } from "ext:core/mod.js";
import {
+ op_import_sync,
op_napi_open,
op_require_as_file_path,
op_require_break_on_next_statement,
+ op_require_can_parse_as_esm,
op_require_init_paths,
op_require_is_deno_dir_package,
op_require_is_request_relative,
@@ -900,16 +902,6 @@ Module.prototype.load = function (filename) {
pathDirname(this.filename),
);
const extension = findLongestRegisteredExtension(filename);
- // allow .mjs to be overridden
- if (
- StringPrototypeEndsWith(filename, ".mjs") && !Module._extensions[".mjs"]
- ) {
- throw createRequireEsmError(
- filename,
- moduleParentCache.get(this)?.filename,
- );
- }
-
Module._extensions[extension](this, this.filename);
this.loaded = true;
@@ -987,27 +979,24 @@ function wrapSafe(
if (process.mainModule === cjsModuleInstance) {
enrichCJSError(err.thrown);
}
- if (isEsmSyntaxError(err.thrown)) {
- throw createRequireEsmError(
- filename,
- moduleParentCache.get(cjsModuleInstance)?.filename,
- );
- } else {
- throw err.thrown;
- }
+ throw err.thrown;
}
return f;
}
Module.prototype._compile = function (content, filename, format) {
- const compiledWrapper = wrapSafe(filename, content, this, format);
-
if (format === "module") {
- // TODO(https://github.com/denoland/deno/issues/24822): implement require esm
- throw createRequireEsmError(
- filename,
- moduleParentCache.get(module)?.filename,
- );
+ return loadESMFromCJS(this, filename, content);
+ }
+
+ let compiledWrapper;
+ try {
+ compiledWrapper = wrapSafe(filename, content, this, format);
+ } catch (err) {
+ if (err instanceof SyntaxError && op_require_can_parse_as_esm(content)) {
+ return loadESMFromCJS(this, filename, content);
+ }
+ throw err;
}
const dirname = pathDirname(filename);
@@ -1065,12 +1054,7 @@ Module._extensions[".js"] = function (module, filename) {
if (StringPrototypeEndsWith(filename, ".js")) {
const pkg = op_require_read_closest_package_json(filename);
if (pkg?.typ === "module") {
- // TODO(https://github.com/denoland/deno/issues/24822): implement require esm
format = "module";
- throw createRequireEsmError(
- filename,
- moduleParentCache.get(module)?.filename,
- );
} else if (pkg?.type === "commonjs") {
format = "commonjs";
}
@@ -1081,20 +1065,19 @@ Module._extensions[".js"] = function (module, filename) {
module._compile(content, filename, format);
};
-function createRequireEsmError(filename, parent) {
- let message = `require() of ES Module ${filename}`;
-
- if (parent) {
- message += ` from ${parent}`;
- }
+function loadESMFromCJS(module, filename, code) {
+ const namespace = op_import_sync(
+ url.pathToFileURL(filename).toString(),
+ code,
+ );
- message +=
- ` not supported. Instead change the require to a dynamic import() which is available in all CommonJS modules.`;
- const err = new Error(message);
- err.code = "ERR_REQUIRE_ESM";
- return err;
+ module.exports = namespace;
}
+Module._extensions[".mjs"] = function (module, filename) {
+ loadESMFromCJS(module, filename);
+};
+
function stripBOM(content) {
if (StringPrototypeCharCodeAt(content, 0) === 0xfeff) {
content = StringPrototypeSlice(content, 1);