Skip to content

Commit

Permalink
feat: add ext/node for require support (denoland#15362)
Browse files Browse the repository at this point in the history
This commit adds "ext/node" extension that implementes CommonJS module system.

In the future this extension might be extended to actually contain implementation of
Node compatibility layer in favor of "deno_std/node".

Currently this functionality is not publicly exposed, it is available via "Deno[Deno.internal].require"
namespace and is meant to be used by other functionality to be landed soon.

This is a minimal first pass, things that still don't work:

support for dynamic imports in CJS
conditional exports
  • Loading branch information
bartlomieju authored Aug 9, 2022
1 parent af618e3 commit 1f54d87
Show file tree
Hide file tree
Showing 27 changed files with 1,648 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ deno_core = { version = "0.146.0", path = "../core" }
deno_crypto = { version = "0.78.0", path = "../ext/crypto" }
deno_fetch = { version = "0.87.0", path = "../ext/fetch" }
deno_net = { version = "0.56.0", path = "../ext/net" }
deno_node = { version = "0.1.0", path = "../ext/node" }
deno_url = { version = "0.64.0", path = "../ext/url" }
deno_web = { version = "0.95.0", path = "../ext/web" }
deno_webgpu = { version = "0.65.0", path = "../ext/webgpu" }
Expand Down
46 changes: 46 additions & 0 deletions cli/compat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,18 @@ static NODE_COMPAT_URL: Lazy<String> = Lazy::new(|| {
static GLOBAL_URL_STR: Lazy<String> =
Lazy::new(|| format!("{}node/global.ts", NODE_COMPAT_URL.as_str()));

static PROCESS_URL_STR: Lazy<String> =
Lazy::new(|| format!("{}node/process.ts", NODE_COMPAT_URL.as_str()));

pub static GLOBAL_URL: Lazy<Url> =
Lazy::new(|| Url::parse(&GLOBAL_URL_STR).unwrap());

static MODULE_URL_STR: Lazy<String> =
Lazy::new(|| format!("{}node/module.ts", NODE_COMPAT_URL.as_str()));

static MODULE_ALL_URL_STR: Lazy<String> =
Lazy::new(|| format!("{}node/module_all.ts", NODE_COMPAT_URL.as_str()));

pub static MODULE_URL: Lazy<Url> =
Lazy::new(|| Url::parse(&MODULE_URL_STR).unwrap());

Expand All @@ -106,6 +112,46 @@ fn try_resolve_builtin_module(specifier: &str) -> Option<Url> {
}
}

#[allow(unused)]
pub async fn load_builtin_node_modules(
js_runtime: &mut JsRuntime,
) -> Result<(), AnyError> {
let source_code = &format!(
r#"(async function loadBuiltinNodeModules(moduleAllUrl, processUrl) {{
const [moduleAll, processModule] = await Promise.all([
import(moduleAllUrl),
import(processUrl)
]);
Deno[Deno.internal].require.initializeCommonJs(moduleAll.default, processModule.default);
}})('{}', '{}');"#,
MODULE_ALL_URL_STR.as_str(),
PROCESS_URL_STR.as_str(),
);

let value =
js_runtime.execute_script(&located_script_name!(), source_code)?;
js_runtime.resolve_value(value).await?;
Ok(())
}

#[allow(unused)]
pub fn load_cjs_module_from_ext_node(
js_runtime: &mut JsRuntime,
module: &str,
main: bool,
) -> Result<(), AnyError> {
let source_code = &format!(
r#"(function loadCjsModule(module) {{
Deno[Deno.internal].require.Module._load(module, null, {main});
}})('{module}');"#,
main = main,
module = escape_for_single_quote_string(module),
);

js_runtime.execute_script(&located_script_name!(), source_code)?;
Ok(())
}

pub fn load_cjs_module(
js_runtime: &mut JsRuntime,
module: &str,
Expand Down
19 changes: 19 additions & 0 deletions cli/tests/integration/compat_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,22 @@ fn native_modules_as_global_vars() {
);
assert!(out.contains("true"));
}

#[test]
fn ext_node_cjs_execution() {
let (out, _err) = util::run_and_collect_output_with_args(
true,
vec![
"run",
"-A",
"--unstable",
"--quiet",
"commonjs/init.js",
"./example.js",
],
None,
Some(vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())]),
false,
);
assert!(out.contains("{ hello: \"world\" }"));
}
3 changes: 3 additions & 0 deletions cli/tests/testdata/commonjs/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"hello": "world"
}
11 changes: 11 additions & 0 deletions cli/tests/testdata/commonjs/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// deno-lint-ignore no-undef
const processMod = require("process");
const osMod = require("node:os");
console.log("process.pid", processMod.pid);
console.log("os.EOL", osMod.EOL);
const leftPad = require("left-pad");
const json = require("./data");
console.log(json);
console.log(leftPad("foo", 5)); // => " foo"
console.log("main module", processMod.mainModule.filename);
17 changes: 17 additions & 0 deletions cli/tests/testdata/commonjs/init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { fromFileUrl } from "../../../../test_util/std/path/mod.ts";

const DENO_NODE_COMPAT_URL = Deno.env.get("DENO_NODE_COMPAT_URL");
const moduleAllUrl = `${DENO_NODE_COMPAT_URL}node/module_all.ts`;
const processUrl = `${DENO_NODE_COMPAT_URL}node/process.ts`;
let moduleName = import.meta.resolve(Deno.args[0]);
moduleName = fromFileUrl(moduleName);

const [moduleAll, processModule] = await Promise.all([
import(moduleAllUrl),
import(processUrl),
]);
Deno[Deno.internal].require.initializeCommonJs(
moduleAll.default,
processModule.default,
);
Deno[Deno.internal].require.Module._load(moduleName, null, true);
74 changes: 74 additions & 0 deletions cli/tests/testdata/commonjs/node_modules/colorette/index.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions cli/tests/testdata/commonjs/node_modules/colorette/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions cli/tests/testdata/commonjs/node_modules/colorette/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 1f54d87

Please sign in to comment.