0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-10-30 09:08:00 -04:00
denoland-deno/cli/compat/mod.rs
Bartek Iwańczuk 617eeabe83
feat(unstable): Node CJS and ESM resolvers for compat mode (#12424)
This commit adds CJS and ESM Node resolvers to the "--compat" mode.

The functionality is spread across "cli/compat" module and Node compatibility
layer in "deno_std/node"; this stems from the fact that ES module resolution
can only be implemented in Rust as it needs to directly integrated with 
"deno_core"; however "deno_std/node" already provided CJS module resolution.

Currently this resolution is only active when running a files using 
"deno run --compat --unstable <filename>", and is not available in other
subcommands, which will be changed in follow up commits.
2021-10-18 19:36:28 +02:00

132 lines
3.4 KiB
Rust

// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
mod errors;
mod esm_resolver;
use deno_core::error::AnyError;
use deno_core::located_script_name;
use deno_core::url::Url;
use deno_core::JsRuntime;
pub use esm_resolver::NodeEsmResolver;
// TODO(bartlomieju): this needs to be bumped manually for
// each release, a better mechanism is preferable, but it's a quick and dirty
// solution to avoid printing `X-Deno-Warning` headers when the compat layer is
// downloaded
static STD_URL_STR: &str = "https://deno.land/std@0.112.0/";
static SUPPORTED_MODULES: &[&str] = &[
"assert",
"assert/strict",
"async_hooks",
"buffer",
"child_process",
"cluster",
"console",
"constants",
"crypto",
"dgram",
"dns",
"domain",
"events",
"fs",
"fs/promises",
"http",
"https",
"module",
"net",
"os",
"path",
"path/posix",
"path/win32",
"perf_hooks",
"process",
"querystring",
"readline",
"stream",
"stream/promises",
"stream/web",
"string_decoder",
"sys",
"timers",
"timers/promises",
"tls",
"tty",
"url",
"util",
"util/types",
"v8",
"vm",
"zlib",
];
lazy_static::lazy_static! {
static ref GLOBAL_URL_STR: String = format!("{}node/global.ts", STD_URL_STR);
pub(crate) static ref GLOBAL_URL: Url = Url::parse(&GLOBAL_URL_STR).unwrap();
static ref MODULE_URL_STR: String = format!("{}node/module.ts", STD_URL_STR);
pub(crate) static ref MODULE_URL: Url = Url::parse(&MODULE_URL_STR).unwrap();
static ref COMPAT_IMPORT_URL: Url = Url::parse("flags:compat").unwrap();
}
/// Provide imports into a module graph when the compat flag is true.
pub(crate) fn get_node_imports() -> Vec<(Url, Vec<String>)> {
vec![(COMPAT_IMPORT_URL.clone(), vec![GLOBAL_URL_STR.clone()])]
}
fn try_resolve_builtin_module(specifier: &str) -> Option<Url> {
if SUPPORTED_MODULES.contains(&specifier) {
let module_url = format!("{}node/{}.ts", STD_URL_STR, specifier);
Some(Url::parse(&module_url).unwrap())
} else {
None
}
}
pub async fn check_if_should_use_esm_loader(
js_runtime: &mut JsRuntime,
main_module: &str,
) -> Result<bool, AnyError> {
// Decide if we're running with Node ESM loader or CJS loader.
let source_code = &format!(
r#"(async function checkIfEsm(main) {{
const {{ resolveMainPath, shouldUseESMLoader }} = await import("{}");
const resolvedMain = resolveMainPath(main);
const useESMLoader = shouldUseESMLoader(resolvedMain);
return useESMLoader;
}})('{}');"#,
MODULE_URL_STR.as_str(),
escape_for_single_quote_string(main_module),
);
let result =
js_runtime.execute_script(&located_script_name!(), source_code)?;
let use_esm_loader_global = js_runtime.resolve_value(result).await?;
let use_esm_loader = {
let scope = &mut js_runtime.handle_scope();
let use_esm_loader_local = use_esm_loader_global.get(scope);
use_esm_loader_local.boolean_value(scope)
};
Ok(use_esm_loader)
}
pub fn load_cjs_module(
js_runtime: &mut JsRuntime,
main_module: &str,
) -> Result<(), AnyError> {
let source_code = &format!(
r#"(async function loadCjsModule(main) {{
const Module = await import("{}");
Module.default._load(main, null, true);
}})('{}');"#,
MODULE_URL_STR.as_str(),
escape_for_single_quote_string(main_module),
);
js_runtime.execute_script(&located_script_name!(), source_code)?;
Ok(())
}
fn escape_for_single_quote_string(text: &str) -> String {
text.replace(r"\", r"\\").replace("'", r"\'")
}