Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compat): inject Node globals #12342

Merged
merged 6 commits into from
Oct 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion cli/compat.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

use deno_core::url::Url;
use std::collections::HashMap;

static STD_NODE: &str = "https://deno.land/std/node/";

static SUPPORTED_MODULES: &[&str] = &[
"assert",
"assert/strict",
Expand Down Expand Up @@ -47,12 +50,20 @@ static SUPPORTED_MODULES: &[&str] = &[
"zlib",
];

pub fn get_node_globals_url() -> Url {
Url::parse(&format!("{}global.ts", STD_NODE)).unwrap()
}

/// Create a map that can be used to update import map.
///
/// Keys are built-in Node modules (and built-ins prefixed with "node:"), while
/// values are URLs pointing to relevant files in deno.land/std/node/ directory.
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);
let module_url = format!("{}{}.ts", STD_NODE, module);
mappings.insert(module.to_string(), module_url.clone());

// Support for `node:<module_name>`
Expand Down
7 changes: 5 additions & 2 deletions cli/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1627,7 +1627,8 @@ 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'.")
.requires("unstable")
.help("Node compatibility mode. Currently only enables built-in node modules like 'fs' and globals like 'process'.")
}

fn watch_arg<'a, 'b>() -> Arg<'a, 'b> {
Expand Down Expand Up @@ -4453,14 +4454,16 @@ mod tests {

#[test]
fn compat() {
let r = flags_from_vec(svec!["deno", "run", "--compat", "foo.js"]);
let r =
flags_from_vec(svec!["deno", "run", "--compat", "--unstable", "foo.js"]);
assert_eq!(
r.unwrap(),
Flags {
subcommand: DenoSubcommand::Run(RunFlags {
script: "foo.js".to_string(),
}),
compat: true,
unstable: true,
..Flags::default()
}
);
Expand Down
39 changes: 30 additions & 9 deletions cli/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ async fn eval_command(
// Force TypeScript compile.
let main_module = resolve_url_or_path("./$deno$eval.ts").unwrap();
let permissions = Permissions::from_options(&flags.clone().into());
let ps = ProcState::build(flags).await?;
let ps = ProcState::build(flags.clone()).await?;
let mut worker =
create_main_worker(&ps, main_module.clone(), permissions, None);
// Create a dummy source file.
Expand Down Expand Up @@ -600,6 +600,11 @@ async fn eval_command(
// to allow module access by TS compiler.
ps.file_fetcher.insert_cached(file);
debug!("main_module {}", &main_module);
if flags.compat {
worker
.execute_side_module(&compat::get_node_globals_url())
.await?;
}
worker.execute_main_module(&main_module).await?;
worker.execute_script(
&located_script_name!(),
Expand Down Expand Up @@ -845,6 +850,11 @@ async fn run_from_stdin(flags: Flags) -> Result<(), AnyError> {
ps.file_fetcher.insert_cached(source_file);

debug!("main_module {}", main_module);
if flags.compat {
worker
.execute_side_module(&compat::get_node_globals_url())
.await?;
}
worker.execute_main_module(&main_module).await?;
worker.execute_script(
&located_script_name!(),
Expand Down Expand Up @@ -912,13 +922,15 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
struct FileWatcherModuleExecutor {
worker: MainWorker,
pending_unload: bool,
compat: bool,
}

impl FileWatcherModuleExecutor {
pub fn new(worker: MainWorker) -> FileWatcherModuleExecutor {
pub fn new(worker: MainWorker, compat: bool) -> FileWatcherModuleExecutor {
FileWatcherModuleExecutor {
worker,
pending_unload: false,
compat,
}
}

Expand All @@ -928,6 +940,12 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {
&mut self,
main_module: &ModuleSpecifier,
) -> Result<(), AnyError> {
if self.compat {
self
.worker
.execute_side_module(&compat::get_node_globals_url())
.await?;
}
self.worker.execute_main_module(main_module).await?;
self.worker.execute_script(
&located_script_name!(),
Expand Down Expand Up @@ -967,16 +985,14 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<(), AnyError> {

let operation = |(ps, main_module): (ProcState, ModuleSpecifier)| {
let flags = flags.clone();
let permissions = Permissions::from_options(&flags.into());
let permissions = Permissions::from_options(&flags.clone().into());
async move {
// We make use an module executor guard to ensure that unload is always fired when an
// operation is called.
let mut executor = FileWatcherModuleExecutor::new(create_main_worker(
&ps,
main_module.clone(),
permissions,
None,
));
let mut executor = FileWatcherModuleExecutor::new(
create_main_worker(&ps, main_module.clone(), permissions, None),
flags.compat,
);

executor.execute(&main_module).await?;

Expand Down Expand Up @@ -1022,6 +1038,11 @@ async fn run_command(
};

debug!("main_module {}", main_module);
if flags.compat {
worker
.execute_side_module(&compat::get_node_globals_url())
.await?;
}
worker.execute_main_module(&main_module).await?;
worker.execute_script(
&located_script_name!(),
Expand Down
6 changes: 5 additions & 1 deletion cli/proc_state.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.

use crate::colors;
use crate::compat;
use crate::config_file::ConfigFile;
use crate::deno_dir;
use crate::file_fetcher::CacheSetting;
Expand Down Expand Up @@ -217,7 +218,7 @@ impl ProcState {
.unwrap()
}
};
let node_builtins = crate::compat::get_mapped_node_builtins();
let node_builtins = compat::get_mapped_node_builtins();
let diagnostics = import_map.update_imports(node_builtins)?;

if !diagnostics.is_empty() {
Expand Down Expand Up @@ -353,6 +354,9 @@ impl ProcState {
)?));
let mut builder =
GraphBuilder::new(handler, maybe_import_map, self.lockfile.clone());
if self.flags.compat {
builder.add(&compat::get_node_globals_url(), false).await?;
}
builder.add(&specifier, is_dynamic).await?;
builder.analyze_config_file(&self.maybe_config_file).await?;
let mut graph = builder.get_graph();
Expand Down
7 changes: 6 additions & 1 deletion cli/tests/integration/compat_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

use crate::itest;

itest!(globals {
args: "run --compat --unstable --allow-read --allow-env compat/globals.ts",
output: "compat/globals.out",
});

itest!(fs_promises {
args: "run --compat --unstable -A compat/fs_promises.js",
output: "compat/fs_promises.out",
Expand All @@ -13,7 +18,7 @@ itest!(node_prefix_fs_promises {
});

itest!(existing_import_map {
args: "run --compat --import-map compat/existing_import_map.json compat/fs_promises.js",
args: "run --compat --unstable --import-map compat/existing_import_map.json compat/fs_promises.js",
output: "compat/existing_import_map.out",
exit_code: 1,
});
3 changes: 2 additions & 1 deletion cli/tests/testdata/compat/existing_import_map.out
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
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]
[WILDCARD]
error: Cannot resolve module [WILDCARD]
7 changes: 7 additions & 0 deletions cli/tests/testdata/compat/globals.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[WILDCARD]
process {
[WILDCARD]
}
[Function: Buffer]
[Function: setImmediate]
[Function: clearTimeout]
8 changes: 8 additions & 0 deletions cli/tests/testdata/compat/globals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
if (global != window) {
throw new Error("global is not equal to window");
}

console.log(process);
console.log(Buffer);
console.log(setImmediate);
console.log(clearImmediate);