mirror of
https://github.com/denoland/deno.git
synced 2024-11-28 16:20:57 -05:00
feat: add --compat flag to provide built-in Node modules (#12293)
This commit adds "--compat" flag. When the flag is passed a set of mappings for built-in Node modules is injected into the import map. If user doesn't explicitly provide an import map (using "--import-map" flag) then a map is created on the fly. If there are already existing mappings in import map that would clash with built-in Node modules a set of diagnostics is printed to the terminal with suggestions how to proceed.
This commit is contained in:
parent
64a7187238
commit
f1d3a17043
14 changed files with 157 additions and 4 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1720,9 +1720,9 @@ checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
|
|||
|
||||
[[package]]
|
||||
name = "import_map"
|
||||
version = "0.3.0"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae79a7af71e92e4038963b64884106d28d508da6bf3c7f36294efcb3bb3b003d"
|
||||
checksum = "d315210af92bcde7a84672d5554fc2b4268c4d40dc9c930ae1d1ed765a8f6381"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"log",
|
||||
|
|
|
@ -59,7 +59,7 @@ encoding_rs = "0.8.28"
|
|||
env_logger = "0.8.4"
|
||||
fancy-regex = "0.7.1"
|
||||
http = "0.2.4"
|
||||
import_map = "0.3.0"
|
||||
import_map = "0.3.3"
|
||||
jsonc-parser = { version = "0.17.0", features = ["serde"] }
|
||||
lazy_static = "1.4.0"
|
||||
libc = "0.2.101"
|
||||
|
|
60
cli/compat.rs
Normal file
60
cli/compat.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
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",
|
||||
];
|
||||
|
||||
pub fn get_mapped_node_builtins() -> HashMap<String, String> {
|
||||
let mut mappings = HashMap::new();
|
||||
|
||||
for module in SUPPORTED_MODULES {
|
||||
// TODO(bartlomieju): this is unversioned, and should be fixed to use latest stable?
|
||||
let module_url = format!("https://deno.land/std/node/{}.ts", module);
|
||||
mappings.insert(module.to_string(), module_url);
|
||||
}
|
||||
|
||||
mappings
|
||||
}
|
32
cli/flags.rs
32
cli/flags.rs
|
@ -222,6 +222,9 @@ pub struct Flags {
|
|||
pub log_level: Option<Level>,
|
||||
pub no_check: bool,
|
||||
pub no_remote: bool,
|
||||
/// If true, a list of Node built-in modules will be injected into
|
||||
/// the import map.
|
||||
pub compat: bool,
|
||||
pub prompt: bool,
|
||||
pub reload: bool,
|
||||
pub repl: bool,
|
||||
|
@ -1490,6 +1493,7 @@ fn runtime_args<'a, 'b>(
|
|||
.arg(v8_flags_arg())
|
||||
.arg(seed_arg())
|
||||
.arg(enable_testing_features_arg())
|
||||
.arg(compat_arg())
|
||||
}
|
||||
|
||||
fn inspect_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||
|
@ -1619,6 +1623,12 @@ fn seed_arg<'a, 'b>() -> Arg<'a, 'b> {
|
|||
})
|
||||
}
|
||||
|
||||
fn compat_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name("compat")
|
||||
.long("compat")
|
||||
.help("Node compatibility mode. Currently only enables built-in node modules like 'fs'.")
|
||||
}
|
||||
|
||||
fn watch_arg<'a, 'b>() -> Arg<'a, 'b> {
|
||||
Arg::with_name("watch")
|
||||
.long("watch")
|
||||
|
@ -2228,6 +2238,7 @@ fn runtime_args_parse(
|
|||
location_arg_parse(flags, matches);
|
||||
v8_flags_arg_parse(flags, matches);
|
||||
seed_arg_parse(flags, matches);
|
||||
compat_arg_parse(flags, matches);
|
||||
inspect_arg_parse(flags, matches);
|
||||
enable_testing_features_arg_parse(flags, matches);
|
||||
}
|
||||
|
@ -2313,6 +2324,12 @@ fn seed_arg_parse(flags: &mut Flags, matches: &ArgMatches) {
|
|||
}
|
||||
}
|
||||
|
||||
fn compat_arg_parse(flags: &mut Flags, matches: &ArgMatches) {
|
||||
if matches.is_present("compat") {
|
||||
flags.compat = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn no_check_arg_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
|
||||
if matches.is_present("no-check") {
|
||||
flags.no_check = true;
|
||||
|
@ -4431,4 +4448,19 @@ mod tests {
|
|||
.to_string()
|
||||
.contains("Expected protocol \"http\" or \"https\""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compat() {
|
||||
let r = flags_from_vec(svec!["deno", "run", "--compat", "foo.js"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
subcommand: DenoSubcommand::Run(RunFlags {
|
||||
script: "foo.js".to_string(),
|
||||
}),
|
||||
compat: true,
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
mod ast;
|
||||
mod auth_tokens;
|
||||
mod checksum;
|
||||
mod compat;
|
||||
mod config_file;
|
||||
mod deno_dir;
|
||||
mod diagnostics;
|
||||
|
|
|
@ -36,6 +36,7 @@ use deno_tls::rustls_native_certs::load_native_certs;
|
|||
use deno_tls::webpki_roots::TLS_SERVER_ROOTS;
|
||||
use import_map::ImportMap;
|
||||
use log::debug;
|
||||
use log::info;
|
||||
use log::warn;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
@ -182,7 +183,7 @@ impl ProcState {
|
|||
None
|
||||
};
|
||||
|
||||
let maybe_import_map: Option<ImportMap> =
|
||||
let mut maybe_import_map: Option<ImportMap> =
|
||||
match flags.import_map_path.as_ref() {
|
||||
None => None,
|
||||
Some(import_map_url) => {
|
||||
|
@ -204,6 +205,32 @@ impl ProcState {
|
|||
}
|
||||
};
|
||||
|
||||
if flags.compat {
|
||||
let mut import_map = match maybe_import_map {
|
||||
Some(import_map) => import_map,
|
||||
None => {
|
||||
// INFO: we're creating an empty import map, with its specifier pointing
|
||||
// to `CWD/node_import_map.json` to make sure the map still works as expected.
|
||||
let import_map_specifier =
|
||||
std::env::current_dir()?.join("node_import_map.json");
|
||||
ImportMap::from_json(import_map_specifier.to_str().unwrap(), "{}")
|
||||
.unwrap()
|
||||
}
|
||||
};
|
||||
let node_builtins = crate::compat::get_mapped_node_builtins();
|
||||
let diagnostics = import_map.update_imports(node_builtins)?;
|
||||
|
||||
if !diagnostics.is_empty() {
|
||||
info!("Some Node built-ins were not added to the import map:");
|
||||
for diagnostic in diagnostics {
|
||||
info!(" - {}", diagnostic);
|
||||
}
|
||||
info!("If you want to use Node built-ins provided by Deno remove listed specifiers from \"imports\" mapping in the import map file.");
|
||||
}
|
||||
|
||||
maybe_import_map = Some(import_map);
|
||||
}
|
||||
|
||||
let maybe_inspect_host = flags.inspect.or(flags.inspect_brk);
|
||||
let maybe_inspector_server = maybe_inspect_host.map(|host| {
|
||||
Arc::new(InspectorServer::new(host, version::get_user_agent()))
|
||||
|
|
14
cli/tests/integration/compat_tests.rs
Normal file
14
cli/tests/integration/compat_tests.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::itest;
|
||||
|
||||
itest!(fs_promises {
|
||||
args: "run --compat --unstable -A compat/fs_promises.js",
|
||||
output: "compat/fs_promises.out",
|
||||
});
|
||||
|
||||
itest!(existing_import_map {
|
||||
args: "run --compat --import-map compat/existing_import_map.json compat/fs_promises.js",
|
||||
output: "compat/existing_import_map.out",
|
||||
exit_code: 1,
|
||||
});
|
|
@ -54,6 +54,8 @@ macro_rules! itest_flaky(
|
|||
mod bundle;
|
||||
#[path = "cache_tests.rs"]
|
||||
mod cache;
|
||||
#[path = "compat_tests.rs"]
|
||||
mod compat;
|
||||
#[path = "compile_tests.rs"]
|
||||
mod compile;
|
||||
#[path = "coverage_tests.rs"]
|
||||
|
|
5
cli/tests/testdata/compat/existing_import_map.json
vendored
Normal file
5
cli/tests/testdata/compat/existing_import_map.json
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"imports": {
|
||||
"fs/promises": "./non_existent_file.js"
|
||||
}
|
||||
}
|
5
cli/tests/testdata/compat/existing_import_map.out
vendored
Normal file
5
cli/tests/testdata/compat/existing_import_map.out
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
[WILDCARD]
|
||||
Some Node built-ins were not added to the import map:
|
||||
- "fs/promises" already exists and is mapped to "[WILDCARD]non_existent_file.js"
|
||||
If you want to use Node built-ins provided by Deno remove listed specifiers from "imports" mapping in the import map file.
|
||||
error: Cannot resolve module [WILDCARD]
|
3
cli/tests/testdata/compat/fs_promises.js
vendored
Normal file
3
cli/tests/testdata/compat/fs_promises.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
import fs from "fs/promises";
|
||||
const data = await fs.readFile("compat/test.txt", "utf-8");
|
||||
console.log(data);
|
2
cli/tests/testdata/compat/fs_promises.out
vendored
Normal file
2
cli/tests/testdata/compat/fs_promises.out
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[WILDCARD]
|
||||
This is some example text that will be read using compatiblity mode.
|
1
cli/tests/testdata/compat/test.txt
vendored
Normal file
1
cli/tests/testdata/compat/test.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
This is some example text that will be read using compatiblity mode.
|
|
@ -227,6 +227,7 @@ pub fn compile_to_runtime_flags(
|
|||
lock: None,
|
||||
log_level: flags.log_level,
|
||||
no_check: false,
|
||||
compat: flags.compat,
|
||||
unsafely_ignore_certificate_errors: flags
|
||||
.unsafely_ignore_certificate_errors,
|
||||
no_remote: false,
|
||||
|
|
Loading…
Reference in a new issue