1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

BREAKING(unstable): remove --compat mode (#15678)

This commit removes "compat" mode. We shipped support for "npm:" specifier
support in v1.25 and that is preferred way to interact with Node code that we
will iterate and improve upon.
This commit is contained in:
Bartek Iwańczuk 2022-09-03 18:19:30 +02:00 committed by GitHub
parent fc4025c878
commit e324151520
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
79 changed files with 12 additions and 2355 deletions

View file

@ -312,9 +312,6 @@ pub struct Flags {
pub lock: Option<PathBuf>, pub lock: Option<PathBuf>,
pub log_level: Option<Level>, pub log_level: Option<Level>,
pub no_remote: 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 no_prompt: bool, pub no_prompt: bool,
pub reload: bool, pub reload: bool,
pub seed: Option<u64>, pub seed: Option<u64>,
@ -1869,7 +1866,6 @@ fn runtime_args(
.arg(v8_flags_arg()) .arg(v8_flags_arg())
.arg(seed_arg()) .arg(seed_arg())
.arg(enable_testing_features_arg()) .arg(enable_testing_features_arg())
.arg(compat_arg())
} }
fn inspect_args(app: Command) -> Command { fn inspect_args(app: Command) -> Command {
@ -2009,21 +2005,6 @@ fn seed_arg<'a>() -> Arg<'a> {
}) })
} }
static COMPAT_HELP: Lazy<String> = Lazy::new(|| {
format!(
"See https://deno.land/manual@v{}/node/compatibility_mode",
SHORT_VERSION.as_str()
)
});
fn compat_arg<'a>() -> Arg<'a> {
Arg::new("compat")
.long("compat")
.requires("unstable")
.help("UNSTABLE: Node compatibility mode.")
.long_help(COMPAT_HELP.as_str())
}
fn watch_arg<'a>(takes_files: bool) -> Arg<'a> { fn watch_arg<'a>(takes_files: bool) -> Arg<'a> {
let arg = Arg::new("watch") let arg = Arg::new("watch")
.long("watch") .long("watch")
@ -2916,7 +2897,6 @@ fn runtime_args_parse(
location_arg_parse(flags, matches); location_arg_parse(flags, matches);
v8_flags_arg_parse(flags, matches); v8_flags_arg_parse(flags, matches);
seed_arg_parse(flags, matches); seed_arg_parse(flags, matches);
compat_arg_parse(flags, matches);
enable_testing_features_arg_parse(flags, matches); enable_testing_features_arg_parse(flags, matches);
} }
@ -3001,12 +2981,6 @@ 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) { fn no_check_arg_parse(flags: &mut Flags, matches: &clap::ArgMatches) {
if let Some(cache_type) = matches.value_of("no-check") { if let Some(cache_type) = matches.value_of("no-check") {
match cache_type { match cache_type {
@ -5565,23 +5539,6 @@ mod tests {
.contains("Expected protocol \"http\" or \"https\"")); .contains("Expected protocol \"http\" or \"https\""));
} }
#[test]
fn compat() {
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()
}
);
}
#[test] #[test]
fn test_config_path_args() { fn test_config_path_args() {
let flags = flags_from_vec(svec!["deno", "run", "foo.js"]).unwrap(); let flags = flags_from_vec(svec!["deno", "run", "foo.js"]).unwrap();

View file

@ -35,7 +35,6 @@ use std::net::SocketAddr;
use std::path::PathBuf; use std::path::PathBuf;
use crate::args::config_file::JsxImportSourceConfig; use crate::args::config_file::JsxImportSourceConfig;
use crate::compat;
use crate::deno_dir::DenoDir; use crate::deno_dir::DenoDir;
use crate::emit::get_ts_config_for_emit; use crate::emit::get_ts_config_for_emit;
use crate::emit::TsConfigType; use crate::emit::TsConfigType;
@ -230,9 +229,6 @@ impl CliOptions {
imports.extend(config_imports); imports.extend(config_imports);
} }
} }
if self.flags.compat {
imports.extend(compat::get_node_imports());
}
if imports.is_empty() { if imports.is_empty() {
Ok(None) Ok(None)
} else { } else {
@ -277,10 +273,6 @@ impl CliOptions {
.unwrap_or(false) .unwrap_or(false)
} }
pub fn compat(&self) -> bool {
self.flags.compat
}
pub fn coverage_dir(&self) -> Option<String> { pub fn coverage_dir(&self) -> Option<String> {
fn allow_coverage(sub_command: &DenoSubcommand) -> bool { fn allow_coverage(sub_command: &DenoSubcommand) -> bool {
match sub_command { match sub_command {

File diff suppressed because it is too large Load diff

View file

@ -1,190 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
mod esm_resolver;
use crate::file_fetcher::FileFetcher;
use crate::node::NODE_COMPAT_URL;
use crate::node::SUPPORTED_MODULES;
use deno_ast::MediaType;
use deno_core::error::AnyError;
use deno_core::located_script_name;
use deno_core::url::Url;
use deno_core::JsRuntime;
use deno_core::ModuleSpecifier;
use once_cell::sync::Lazy;
pub use esm_resolver::check_if_should_use_esm_loader;
pub use esm_resolver::NodeEsmResolver;
pub static GLOBAL_URL: Lazy<Url> =
Lazy::new(|| NODE_COMPAT_URL.join("node/global.ts").unwrap());
pub static MODULE_URL: Lazy<Url> =
Lazy::new(|| NODE_COMPAT_URL.join("node/module.ts").unwrap());
static COMPAT_IMPORT_URL: Lazy<Url> =
Lazy::new(|| Url::parse("flags:compat").unwrap());
/// Provide imports into a module graph when the compat flag is true.
pub fn get_node_imports() -> Vec<(Url, Vec<String>)> {
vec![(
COMPAT_IMPORT_URL.clone(),
vec![GLOBAL_URL.as_str().to_owned()],
)]
}
pub fn load_cjs_module(
js_runtime: &mut JsRuntime,
module: &str,
main: bool,
) -> Result<(), AnyError> {
let source_code = &format!(
r#"(async function loadCjsModule(module) {{
const Module = await import("{module_loader}");
Module.default._load(module, null, {main});
}})('{module}');"#,
module_loader = MODULE_URL.as_str(),
main = main,
module = escape_for_single_quote_string(module),
);
js_runtime.execute_script(&located_script_name!(), source_code)?;
Ok(())
}
pub fn add_global_require(
js_runtime: &mut JsRuntime,
main_module: &str,
) -> Result<(), AnyError> {
let source_code = &format!(
r#"(async function setupGlobalRequire(main) {{
const Module = await import("{}");
const require = Module.createRequire(main);
globalThis.require = require;
}})('{}');"#,
MODULE_URL.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"\\").replace('\'', r"\'")
}
pub fn setup_builtin_modules(
js_runtime: &mut JsRuntime,
) -> Result<(), AnyError> {
let mut script = String::new();
for module in SUPPORTED_MODULES {
// skipping the modules that contains '/' as they are not available in NodeJS repl as well
if !module.name.contains('/') {
script = format!(
"{}const {MODULE_NAME} = require('{MODULE_NAME}');\n",
script,
MODULE_NAME = module.name
);
}
}
js_runtime.execute_script("setup_node_builtins.js", &script)?;
Ok(())
}
/// Translates given CJS module into ESM. This function will perform static
/// analysis on the file to find defined exports and reexports.
///
/// For all discovered reexports the analysis will be performed recursively.
///
/// If successful a source code for equivalent ES module is returned.
pub fn translate_cjs_to_esm(
file_fetcher: &FileFetcher,
specifier: &ModuleSpecifier,
code: String,
media_type: MediaType,
) -> Result<String, AnyError> {
let parsed_source = deno_ast::parse_script(deno_ast::ParseParams {
specifier: specifier.to_string(),
text_info: deno_ast::SourceTextInfo::new(code.into()),
media_type,
capture_tokens: true,
scope_analysis: false,
maybe_syntax: None,
})?;
let analysis = parsed_source.analyze_cjs();
let mut source = vec![
r#"import { createRequire } from "node:module";"#.to_string(),
r#"const require = createRequire(import.meta.url);"#.to_string(),
];
// if there are reexports, handle them first
for (idx, reexport) in analysis.reexports.iter().enumerate() {
// Firstly, resolve relate reexport specifier
let resolved_reexport = node_resolver::resolve(
reexport,
&specifier.to_file_path().unwrap(),
// FIXME(bartlomieju): check if these conditions are okay, probably
// should be `deno-require`, because `deno` is already used in `esm_resolver.rs`
&["deno", "require", "default"],
)?;
let reexport_specifier =
ModuleSpecifier::from_file_path(&resolved_reexport).unwrap();
// Secondly, read the source code from disk
let reexport_file = file_fetcher.get_source(&reexport_specifier).unwrap();
// Now perform analysis again
{
let parsed_source = deno_ast::parse_script(deno_ast::ParseParams {
specifier: reexport_specifier.to_string(),
text_info: deno_ast::SourceTextInfo::new(reexport_file.source),
media_type: reexport_file.media_type,
capture_tokens: true,
scope_analysis: false,
maybe_syntax: None,
})?;
let analysis = parsed_source.analyze_cjs();
source.push(format!(
"const reexport{} = require(\"{}\");",
idx, reexport
));
for export in analysis.exports.iter().filter(|e| e.as_str() != "default")
{
// TODO(bartlomieju): Node actually checks if a given export exists in `exports` object,
// but it might not be necessary here since our analysis is more detailed?
source.push(format!(
"export const {0} = Deno[Deno.internal].require.bindExport(reexport{1}.{2}, reexport{1});",
export, idx, export
));
}
}
}
source.push(format!(
"const mod = require(\"{}\");",
specifier
.to_file_path()
.unwrap()
.to_str()
.unwrap()
.replace('\\', "\\\\")
.replace('\'', "\\\'")
.replace('\"', "\\\"")
));
source.push("export default mod;".to_string());
for export in analysis.exports.iter().filter(|e| e.as_str() != "default") {
// TODO(bartlomieju): Node actually checks if a given export exists in `exports` object,
// but it might not be necessary here since our analysis is more detailed?
source.push(format!(
"export const {} = Deno[Deno.internal].require.bindExport(mod.{}, mod);",
export, export
));
}
let translated_source = source.join("\n");
Ok(translated_source)
}

View file

@ -1 +0,0 @@
import "foo";

View file

View file

@ -1,5 +0,0 @@
{
"name": "foo",
"type": "module",
"exports": "./index.js"
}

View file

@ -1,7 +0,0 @@
{
"name": "bar",
"type": "module",
"dependencies": {
"foo": "1.0.0"
}
}

View file

@ -1 +0,0 @@
import "foo";

View file

@ -1 +0,0 @@
export const BAR = 123;

View file

@ -1,6 +0,0 @@
{
"name": "bar",
"version": "0.1.2",
"type": "module",
"exports": "./bar.js"
}

View file

@ -1 +0,0 @@
import "bar";

View file

@ -1,8 +0,0 @@
{
"name": "foo",
"type": "module",
"exports": "./foo.js",
"dependencies": {
"bar": "0.1.2"
}
}

View file

@ -1,7 +0,0 @@
{
"name": "main_program",
"type": "module",
"dependencies": {
"foo": "1.0.0"
}
}

View file

@ -1 +0,0 @@
import "imports_exports";

View file

@ -1,6 +0,0 @@
import dep from "#dep";
export default {
bar: "bar",
dep,
};

View file

@ -1,3 +0,0 @@
export default {
polyfill: "import",
};

View file

@ -1,17 +0,0 @@
{
"version": "1.0.0",
"name": "imports_exports",
"main": "./require_export.cjs",
"imports": {
"#dep": {
"import": "./import_polyfill.js",
"require": "./require_polyfill.js"
}
},
"exports": {
".": {
"import": "./import_export.js",
"require": "./require_export.cjs"
}
}
}

View file

@ -1,6 +0,0 @@
const dep = require("#dep");
module.exports = {
foo: "foo",
dep,
};

View file

@ -1,3 +0,0 @@
module.exports = {
polyfill: "require",
};

View file

@ -1,7 +0,0 @@
{
"name": "conditions",
"type": "module",
"dependencies": {
"imports_exports": "1.0.0"
}
}

View file

@ -1 +0,0 @@
import "foo";

View file

View file

@ -1,5 +0,0 @@
{
"name": "foo",
"type": "module",
"exports": "./index.js"
}

View file

@ -1 +0,0 @@
require("foo");

View file

@ -1,5 +0,0 @@
{
"name": "foo",
"type": "module",
"exports": "./index.js"
}

View file

@ -1,6 +0,0 @@
{
"name": "bar",
"dependencies": {
"foo": "1.0.0"
}
}

View file

@ -1,2 +0,0 @@
import "foo";
import "foo/server.js";

View file

@ -1,4 +0,0 @@
{
"name": "foo",
"main": "index.js"
}

View file

@ -441,20 +441,6 @@ impl GraphData {
None None
} }
// TODO(bartlomieju): after saving translated source
// it's never removed, potentially leading to excessive
// memory consumption
pub fn add_cjs_esm_translation(
&mut self,
specifier: &ModuleSpecifier,
source: String,
) {
let prev = self
.cjs_esm_translations
.insert(specifier.to_owned(), source);
assert!(prev.is_none());
}
pub fn get_cjs_esm_translation<'a>( pub fn get_cjs_esm_translation<'a>(
&'a self, &'a self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,

View file

@ -5,7 +5,6 @@ mod auth_tokens;
mod cache; mod cache;
mod cdp; mod cdp;
mod checksum; mod checksum;
mod compat;
mod deno_dir; mod deno_dir;
mod deno_std; mod deno_std;
mod diagnostics; mod diagnostics;
@ -806,8 +805,6 @@ async fn run_command(
return run_with_watch(flags, run_flags.script).await; return run_with_watch(flags, run_flags.script).await;
} }
// TODO(bartlomieju): it should not be resolved here if we're in compat mode
// because it might be a bare specifier
// TODO(bartlomieju): actually I think it will also fail if there's an import // TODO(bartlomieju): actually I think it will also fail if there's an import
// map specified and bare specifier is used on the command line - this should // map specified and bare specifier is used on the command line - this should
// probably call `ProcState::resolve` instead // probably call `ProcState::resolve` instead

View file

@ -22,27 +22,6 @@ pub fn err_invalid_module_specifier(
type_error(msg) type_error(msg)
} }
pub fn err_invalid_package_config(
path: &str,
maybe_base: Option<String>,
maybe_message: Option<String>,
) -> AnyError {
let mut msg = format!(
"[ERR_INVALID_PACKAGE_CONFIG] Invalid package config {}",
path
);
if let Some(base) = maybe_base {
msg = format!("{} while importing {}", msg, base);
}
if let Some(message) = maybe_message {
msg = format!("{}. {}", msg, message);
}
generic_error(msg)
}
pub fn err_module_not_found(path: &str, base: &str, typ: &str) -> AnyError { pub fn err_module_not_found(path: &str, base: &str, typ: &str) -> AnyError {
generic_error(format!( generic_error(format!(
"[ERR_MODULE_NOT_FOUND] Cannot find {} \"{}\" imported from \"{}\"", "[ERR_MODULE_NOT_FOUND] Cannot find {} \"{}\" imported from \"{}\"",
@ -69,73 +48,3 @@ pub fn err_unsupported_esm_url_scheme(url: &Url) -> AnyError {
msg = format!("{}. Received protocol '{}'", msg, url.scheme()); msg = format!("{}. Received protocol '{}'", msg, url.scheme());
generic_error(msg) generic_error(msg)
} }
pub fn err_invalid_package_target(
pkg_path: String,
key: String,
target: String,
is_import: bool,
maybe_base: Option<String>,
) -> AnyError {
let rel_error = !is_import && !target.is_empty() && !target.starts_with("./");
let mut msg = "[ERR_INVALID_PACKAGE_TARGET]".to_string();
if key == "." {
assert!(!is_import);
msg = format!("{} Invalid \"exports\" main target {} defined in the package config {}package.json", msg, target, pkg_path)
} else {
let ie = if is_import { "imports" } else { "exports" };
msg = format!("{} Invalid \"{}\" target {} defined for '{}' in the package config {}package.json", msg, ie, target, key, pkg_path)
};
if let Some(base) = maybe_base {
msg = format!("{} imported from {}", msg, base);
};
if rel_error {
msg = format!("{}; target must start with \"./\"", msg);
}
generic_error(msg)
}
pub fn err_package_path_not_exported(
pkg_path: String,
subpath: String,
maybe_base: Option<String>,
) -> AnyError {
let mut msg = "[ERR_PACKAGE_PATH_NOT_EXPORTED]".to_string();
if subpath == "." {
msg = format!(
"{} No \"exports\" main defined in {}package.json",
msg, pkg_path
);
} else {
msg = format!("{} Package subpath \'{}\' is not defined by \"exports\" in {}package.json", msg, subpath, pkg_path);
};
if let Some(base) = maybe_base {
msg = format!("{} imported from {}", msg, base);
}
generic_error(msg)
}
pub fn err_package_import_not_defined(
specifier: &str,
package_path: Option<String>,
base: &str,
) -> AnyError {
let mut msg = format!(
"[ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier \"{}\" is not defined in",
specifier
);
if let Some(package_path) = package_path {
msg = format!("{} in package {}package.json", msg, package_path);
}
msg = format!("{} imported from {}", msg, base);
type_error(msg)
}

View file

@ -9,8 +9,6 @@ use crate::cache::EmitCache;
use crate::cache::FastInsecureHasher; use crate::cache::FastInsecureHasher;
use crate::cache::ParsedSourceCache; use crate::cache::ParsedSourceCache;
use crate::cache::TypeCheckCache; use crate::cache::TypeCheckCache;
use crate::compat;
use crate::compat::NodeEsmResolver;
use crate::deno_dir; use crate::deno_dir;
use crate::emit::emit_parsed_source; use crate::emit::emit_parsed_source;
use crate::emit::TsConfigType; use crate::emit::TsConfigType;
@ -186,9 +184,6 @@ impl ProcState {
// FIXME(bartlomieju): `NodeEsmResolver` is not aware of JSX resolver // FIXME(bartlomieju): `NodeEsmResolver` is not aware of JSX resolver
// created below // created below
let node_resolver = NodeEsmResolver::new(
maybe_import_map.clone().map(ImportMapResolver::new),
);
let maybe_import_map_resolver = let maybe_import_map_resolver =
maybe_import_map.clone().map(ImportMapResolver::new); maybe_import_map.clone().map(ImportMapResolver::new);
let maybe_jsx_resolver = cli_options let maybe_jsx_resolver = cli_options
@ -196,9 +191,7 @@ impl ProcState {
.map(|cfg| JsxResolver::new(cfg, maybe_import_map_resolver.clone())); .map(|cfg| JsxResolver::new(cfg, maybe_import_map_resolver.clone()));
let maybe_resolver: Option< let maybe_resolver: Option<
Arc<dyn deno_graph::source::Resolver + Send + Sync>, Arc<dyn deno_graph::source::Resolver + Send + Sync>,
> = if cli_options.compat() { > = if let Some(jsx_resolver) = maybe_jsx_resolver {
Some(Arc::new(node_resolver))
} else if let Some(jsx_resolver) = maybe_jsx_resolver {
// the JSX resolver offloads to the import map if present, otherwise uses // the JSX resolver offloads to the import map if present, otherwise uses
// the default Deno explicit import resolution. // the default Deno explicit import resolution.
Some(Arc::new(jsx_resolver)) Some(Arc::new(jsx_resolver))
@ -286,7 +279,7 @@ impl ProcState {
// One might argue that this is a code smell, and I would agree. However // One might argue that this is a code smell, and I would agree. However
// due to flux in "Node compatibility" it's not clear where it should be // due to flux in "Node compatibility" it's not clear where it should be
// decided what `ModuleKind` is decided for root specifier. // decided what `ModuleKind` is decided for root specifier.
let roots = roots let roots: Vec<(deno_core::url::Url, deno_graph::ModuleKind)> = roots
.into_iter() .into_iter()
.map(|r| { .map(|r| {
if let Some(resolver) = &maybe_resolver { if let Some(resolver) = &maybe_resolver {
@ -306,13 +299,6 @@ impl ProcState {
// TODO(bartlomieju): this is very make-shift, is there an existing API // TODO(bartlomieju): this is very make-shift, is there an existing API
// that we could include it like with "maybe_imports"? // that we could include it like with "maybe_imports"?
let roots = if self.options.compat() {
let mut r = vec![(compat::GLOBAL_URL.clone(), ModuleKind::Esm)];
r.extend(roots);
r
} else {
roots
};
if !reload_on_watch { if !reload_on_watch {
let graph_data = self.graph_data.read(); let graph_data = self.graph_data.read();
if self.options.type_check_mode() == TypeCheckMode::None if self.options.type_check_mode() == TypeCheckMode::None
@ -389,32 +375,6 @@ impl ProcState {
) )
.await; .await;
let needs_cjs_esm_translation = graph
.modules()
.iter()
.any(|m| m.kind == ModuleKind::CommonJs);
if needs_cjs_esm_translation {
for module in graph.modules() {
// TODO(bartlomieju): this is overly simplistic heuristic, once we are
// in compat mode, all files ending with plain `.js` extension are
// considered CommonJs modules. Which leads to situation where valid
// ESM modules with `.js` extension might undergo translation (it won't
// work in this situation).
if module.kind == ModuleKind::CommonJs {
let translated_source = compat::translate_cjs_to_esm(
&self.file_fetcher,
&module.specifier,
module.maybe_source.as_ref().unwrap().to_string(),
module.media_type,
)?;
let mut graph_data = self.graph_data.write();
graph_data
.add_cjs_esm_translation(&module.specifier, translated_source);
}
}
}
// If there was a locker, validate the integrity of all the modules in the // If there was a locker, validate the integrity of all the modules in the
// locker. // locker.
graph_lock_or_exit(&graph); graph_lock_or_exit(&graph);

View file

@ -1,194 +0,0 @@
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
use crate::itest;
use deno_core::url::Url;
use test_util as util;
/// Tests in this file should use `std_file_url` to override `DENO_NODE_COMPAT_URL`
/// env variable. This speeds up tests significantly as they no longer
/// download contents of `deno_std` from `https://deno.land` in each test.
/// Return a file URL pointing to "std" submodule
/// in "test_util" directory. It must have a trailing slash.
fn std_file_url() -> String {
let u = Url::from_directory_path(util::std_path()).unwrap();
u.to_string()
}
itest!(globals {
args: "run --compat --no-check --unstable --allow-read --allow-env compat/globals.ts",
output: "compat/globals.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(fs_promises {
args: "run --compat --no-check --unstable -A compat/fs_promises.mjs",
output: "compat/fs_promises.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
// https://github.com/denoland/deno/issues/12494
itest_flaky!(node_prefix_fs_promises {
args: "run --compat --no-check --unstable -A compat/node_fs_promises.mjs",
output: "compat/fs_promises.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(compat_with_import_map_and_https_imports {
args: "run --quiet --no-check --compat --unstable -A --import-map=compat/import_map.json compat/import_map_https_imports.mjs",
output: "compat/import_map_https_imports.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(compat_dyn_import_rejects_with_node_compatible_error {
args:
"run --quiet --no-check --compat --unstable -A compat/dyn_import_reject.js",
output: "compat/dyn_import_reject.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(import_esm_from_cjs {
args:
"run --compat --unstable -A --quiet compat/import_esm_from_cjs/index.js",
output_str: Some("function\n"),
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(test_runner_cjs {
args: "test --compat --unstable -A --quiet compat/test_runner/cjs.js",
exit_code: 1,
output: "compat/test_runner/cjs.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(test_runner_esm {
args: "test --compat --unstable -A --quiet compat/test_runner/esm.mjs",
exit_code: 1,
output: "compat/test_runner/esm.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
// Top level assertion test mostly just make sure that the test runner finishes correctly on compat mode
// when there is no tests
itest!(top_level_assertion_cjs {
args: "test --compat --unstable -A --quiet compat/test_runner/top_level_assertion_cjs.js",
exit_code: 0,
output: "compat/test_runner/top_level_assertion_cjs.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(top_level_assertion_esm {
args: "test --compat --unstable -A --quiet compat/test_runner/top_level_assertion_esm.mjs",
exit_code: 0,
output: "compat/test_runner/top_level_assertion_esm.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(top_level_fail_cjs {
args: "test --compat --unstable -A --quiet compat/test_runner/top_level_fail_cjs.js",
exit_code: 1,
output: "compat/test_runner/top_level_fail_cjs.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(top_level_fail_esm {
args: "test --compat --unstable -A --quiet compat/test_runner/top_level_fail_esm.mjs",
exit_code: 1,
output: "compat/test_runner/top_level_fail_esm.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(compat_worker {
args: "run --compat --unstable -A --quiet --no-check compat/worker/worker_test.mjs",
output: "compat/worker/worker_test.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(cjs_esm_interop {
args:
"run --compat --unstable -A --quiet --no-check compat/import_cjs_from_esm/main.mjs",
output: "compat/import_cjs_from_esm.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
itest!(cjs_esm_interop_dynamic {
args:
"run --compat --unstable -A --quiet --no-check compat/import_cjs_from_esm/main_dynamic.mjs",
output: "compat/import_cjs_from_esm.out",
envs: vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())],
});
#[test]
fn globals_in_repl() {
let (out, _err) = util::run_and_collect_output_with_args(
true,
vec!["repl", "--compat", "--unstable", "--no-check", "--quiet"],
Some(vec!["global.window == window"]),
Some(vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())]),
false,
);
assert!(out.contains("true"));
}
#[test]
fn require_in_repl() {
let (out, _err) = util::run_and_collect_output_with_args(
true,
vec!["repl", "--compat", "--unstable", "--quiet"],
Some(vec![
"const foo = require('./compat/import_esm_from_cjs/index');",
]),
Some(vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())]),
false,
);
assert!(out.contains("function"));
}
#[test]
fn node_compat_url() {
let (out, err) = util::run_and_collect_output_with_args(
false,
vec!["repl", "--compat", "--unstable", "--no-check", "--quiet"],
None,
Some(vec![(
"DENO_NODE_COMPAT_URL".to_string(),
"file:///non_existent/".to_string(),
)]),
false,
);
assert!(out.is_empty());
assert!(!err.is_empty());
assert!(err.contains("file:///non_existent/node/global.ts"));
}
#[test]
fn native_modules_as_global_vars() {
let (out, _err) = util::run_and_collect_output_with_args(
true,
vec!["repl", "--compat", "--unstable", "--quiet"],
Some(vec!["if(cluster && v8 && sys) { true } else { false }"]),
Some(vec![("DENO_NODE_COMPAT_URL".to_string(), std_file_url())]),
false,
);
assert!(out.contains("true"));
}
#[ignore] // todo(dsherret): re-enable
#[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\" }"));
}

View file

@ -60,8 +60,6 @@ mod bundle;
mod cache; mod cache;
#[path = "check_tests.rs"] #[path = "check_tests.rs"]
mod check; mod check;
#[path = "compat_tests.rs"]
mod compat;
#[path = "compile_tests.rs"] #[path = "compile_tests.rs"]
mod compile; mod compile;
#[path = "coverage_tests.rs"] #[path = "coverage_tests.rs"]

View file

@ -1,4 +0,0 @@
import("./foobar.js").catch((e) => {
console.log(e);
console.log(e.code);
});

View file

@ -1,2 +0,0 @@
TypeError: [ERR_MODULE_NOT_FOUND] Cannot find module "file://[WILDCARD]/testdata/compat/foobar.js" imported from "file://[WILDCARD]/testdata/compat/dyn_import_reject.js"
ERR_MODULE_NOT_FOUND

View file

@ -1,3 +0,0 @@
import fs from "fs/promises";
const data = await fs.readFile("compat/test.txt", "utf-8");
console.log(data);

View file

@ -1,2 +0,0 @@
[WILDCARD]
This is some example text that will be read using compatiblity mode.

View file

@ -1,9 +0,0 @@
[WILDCARD]
process {
[WILDCARD]
}
[Function: Buffer] {
[WILDCARD]
}
[Function: setImmediate]
[Function: clearTimeout]

View file

@ -1,4 +0,0 @@
console.log(process);
console.log(Buffer);
console.log(setImmediate);
console.log(clearImmediate);

View file

@ -1 +0,0 @@
{ a: "A", b: "B", foo: "foo", bar: "bar", fizz: { buzz: "buzz", fizz: "FIZZ" } }

View file

@ -1,9 +0,0 @@
exports = {
a: "A",
b: "B",
};
exports.foo = "foo";
exports.bar = "bar";
exports.fizz = require("./reexports.js");
console.log(exports);

View file

@ -1 +0,0 @@
import "./imported.js";

View file

@ -1,2 +0,0 @@
const url = import.meta.resolve("./imported.js");
await import(url);

View file

@ -1 +0,0 @@
module.exports = require("./reexports2.js");

View file

@ -1,2 +0,0 @@
exports.buzz = "buzz";
exports.fizz = "FIZZ";

View file

@ -1 +0,0 @@
require("pure-cjs");

View file

@ -1,4 +0,0 @@
async function run() {
const _result = await import('pure-esm');
}
run()

View file

@ -1,4 +0,0 @@
{
"name": "pure-cjs",
"main": "./index.js"
}

View file

@ -1,2 +0,0 @@
import fs from 'node:fs';
console.log(typeof fs.chmod);

View file

@ -1,5 +0,0 @@
{
"name": "pure-esm",
"type": "module",
"main": "./index.js"
}

View file

@ -1,5 +0,0 @@
{
"imports": {
"std/": "https://deno.land/std@0.126.0/"
}
}

View file

@ -1,7 +0,0 @@
import { sortBy } from "std/collections/sort_by.ts";
import { findSingle } from "https://deno.land/std@0.126.0/collections/find_single.ts";
import os from "node:os";
console.log(sortBy([2, 3, 1], (it) => it));
console.log(findSingle([2, 3, 1], (it) => it == 2));
console.log("arch", os.arch());

View file

@ -1,3 +0,0 @@
[ 1, 2, 3 ]
2
arch [WILDCARD]

View file

@ -1,3 +0,0 @@
import fs from "node:fs/promises";
const data = await fs.readFile("compat/test.txt", "utf-8");
console.log(data);

View file

@ -1 +0,0 @@
This is some example text that will be read using compatiblity mode.

View file

@ -1,9 +0,0 @@
const { strictEqual } = require("assert");
Deno.test("Correct assertion", () => {
strictEqual(20, 20);
});
Deno.test("Failed assertion", () => {
strictEqual(10, 20);
});

View file

@ -1,27 +0,0 @@
running 2 tests from ./compat/test_runner/cjs.js
Correct assertion ... ok ([WILDCARD])
Failed assertion ... FAILED ([WILDCARD])
ERRORS
Failed assertion => ./compat/test_runner/cjs.js:[WILDCARD]
error: AssertionError: Values are not strictly equal:
[Diff] Actual / Expected
- 10
+ 20
Error.captureStackTrace(this, stackStartFn || stackStartFunction);
^
[WILDCARD]
FAILURES
Failed assertion => ./compat/test_runner/cjs.js:[WILDCARD]
FAILED | 1 passed | 1 failed ([WILDCARD])
error: Test failed

View file

@ -1,9 +0,0 @@
import { strictEqual } from "assert";
Deno.test("Correct assertion", () => {
strictEqual(20, 20);
});
Deno.test("Failed assertion", () => {
strictEqual(10, 20);
});

View file

@ -1,27 +0,0 @@
running 2 tests from ./compat/test_runner/esm.mjs
Correct assertion ... ok ([WILDCARD])
Failed assertion ... FAILED ([WILDCARD])
ERRORS
Failed assertion => ./compat/test_runner/esm.mjs:[WILDCARD]
error: AssertionError: Values are not strictly equal:
[Diff] Actual / Expected
- 10
+ 20
Error.captureStackTrace(this, stackStartFn || stackStartFunction);
^
[WILDCARD]
FAILURES
Failed assertion => ./compat/test_runner/esm.mjs:[WILDCARD]
FAILED | 1 passed | 1 failed ([WILDCARD])
error: Test failed

View file

@ -1,4 +0,0 @@
const { notStrictEqual, strictEqual } = require("assert");
notStrictEqual(require.main, module, "The module was loaded as a main module");
strictEqual(20, 20);

View file

@ -1,4 +0,0 @@
running 0 tests from [WILDCARD]
ok | 0 passed | 0 failed ([WILDCARD])

View file

@ -1,4 +0,0 @@
import assert, { strictEqual } from "assert";
assert(!import.meta.main, "The module was loaded as a main module");
strictEqual(20, 20);

View file

@ -1,4 +0,0 @@
running 0 tests from [WILDCARD]
ok | 0 passed | 0 failed ([WILDCARD])

View file

@ -1,4 +0,0 @@
const { notStrictEqual, strictEqual } = require("assert");
notStrictEqual(require.main, module, "The module was loaded as a main module");
strictEqual(10, 20);

View file

@ -1,27 +0,0 @@
Uncaught error from ./compat/test_runner/top_level_fail_cjs.js FAILED
ERRORS
./compat/test_runner/top_level_fail_cjs.js (uncaught error)
error: (in promise) AssertionError: Values are not strictly equal:
[Diff] Actual / Expected
- 10
+ 20
Error.captureStackTrace(this, stackStartFn || stackStartFunction);
^
at [WILDCARD]
This error was not caught from a test and caused the test runner to fail on the referenced module.
It most likely originated from a dangling promise, event/timeout handler or top-level code.
FAILURES
./compat/test_runner/top_level_fail_cjs.js (uncaught error)
FAILED | 0 passed | 1 failed ([WILDCARD])
error: Test failed

View file

@ -1,4 +0,0 @@
import assert, { strictEqual } from "assert";
assert(!import.meta.main, "The module was loaded as a main module");
strictEqual(10, 20);

View file

@ -1,27 +0,0 @@
Uncaught error from ./compat/test_runner/top_level_fail_esm.mjs FAILED
ERRORS
./compat/test_runner/top_level_fail_esm.mjs (uncaught error)
error: AssertionError: Values are not strictly equal:
[Diff] Actual / Expected
- 10
+ 20
Error.captureStackTrace(this, stackStartFn || stackStartFunction);
^
at [WILDCARD]
This error was not caught from a test and caused the test runner to fail on the referenced module.
It most likely originated from a dangling promise, event/timeout handler or top-level code.
FAILURES
./compat/test_runner/top_level_fail_esm.mjs (uncaught error)
FAILED | 0 passed | 1 failed ([WILDCARD])
error: Test failed

View file

@ -1,9 +0,0 @@
console.log("hello from worker");
self.onmessage = (e) => {
if (e.data != "hello") {
throw new Error("wrong message");
}
self.postMessage({ pid: process.pid });
}

View file

@ -1,18 +0,0 @@
import { deferred } from "../../../../../test_util/std/async/deferred.ts";
const promise = deferred();
const url = import.meta.resolve("./worker.mjs");
const worker = new Worker(url, { type: "module" });
worker.onmessage = (e) => {
const pid = e.data.pid;
if (typeof pid != "number") {
throw new Error("pid is not a number");
}
console.log("process.pid from worker:", pid);
promise.resolve();
};
worker.postMessage("hello");
await promise;
worker.terminate();

View file

@ -1,2 +0,0 @@
hello from worker
process.pid from worker: [WILDCARD]

View file

@ -333,10 +333,6 @@ fn resolve_shim_data(
executable_args.push("--no-prompt".to_string()); executable_args.push("--no-prompt".to_string());
} }
if flags.compat {
executable_args.push("--compat".to_string());
}
if !flags.v8_flags.is_empty() { if !flags.v8_flags.is_empty() {
executable_args.push(format!("--v8-flags={}", flags.v8_flags.join(","))); executable_args.push(format!("--v8-flags={}", flags.v8_flags.join(",")));
} }
@ -592,7 +588,6 @@ mod tests {
allow_read: Some(vec![]), allow_read: Some(vec![]),
type_check_mode: TypeCheckMode::None, type_check_mode: TypeCheckMode::None,
log_level: Some(Level::Error), log_level: Some(Level::Error),
compat: true,
..Flags::default() ..Flags::default()
}, },
&InstallFlags { &InstallFlags {
@ -613,7 +608,6 @@ mod tests {
"--allow-read", "--allow-read",
"--allow-net", "--allow-net",
"--quiet", "--quiet",
"--compat",
"http://localhost:4545/echo_server.ts", "http://localhost:4545/echo_server.ts",
"--foobar", "--foobar",
] ]

View file

@ -275,7 +275,6 @@ pub fn compile_to_runtime_flags(
lock: None, lock: None,
log_level: flags.log_level, log_level: flags.log_level,
type_check_mode: TypeCheckMode::Local, type_check_mode: TypeCheckMode::Local,
compat: flags.compat,
unsafely_ignore_certificate_errors: flags unsafely_ignore_certificate_errors: flags
.unsafely_ignore_certificate_errors .unsafely_ignore_certificate_errors
.clone(), .clone(),

View file

@ -23,7 +23,6 @@ use deno_runtime::BootstrapOptions;
use crate::args::DenoSubcommand; use crate::args::DenoSubcommand;
use crate::checksum; use crate::checksum;
use crate::compat;
use crate::errors; use crate::errors;
use crate::module_loader::CliModuleLoader; use crate::module_loader::CliModuleLoader;
use crate::node; use crate::node;
@ -52,15 +51,6 @@ impl CliMainWorker {
} }
pub async fn setup_repl(&mut self) -> Result<(), AnyError> { pub async fn setup_repl(&mut self) -> Result<(), AnyError> {
if self.ps.options.compat() {
self.worker.execute_side_module(&compat::GLOBAL_URL).await?;
compat::add_global_require(
&mut self.worker.js_runtime,
self.main_module.as_str(),
)?;
self.worker.run_event_loop(false).await?;
compat::setup_builtin_modules(&mut self.worker.js_runtime)?;
}
self.worker.run_event_loop(false).await?; self.worker.run_event_loop(false).await?;
Ok(()) Ok(())
} }
@ -70,39 +60,7 @@ impl CliMainWorker {
self.maybe_setup_coverage_collector().await?; self.maybe_setup_coverage_collector().await?;
log::debug!("main_module {}", self.main_module); log::debug!("main_module {}", self.main_module);
if self.ps.options.compat() { if self.is_main_cjs {
// TODO(bartlomieju): fix me
assert_eq!(self.main_module.scheme(), "file");
// Set up Node globals
self.worker.execute_side_module(&compat::GLOBAL_URL).await?;
// And `module` module that we'll use for checking which
// loader to use and potentially load CJS module with.
// This allows to skip permission check for `--allow-net`
// which would otherwise be requested by dynamically importing
// this file.
self.worker.execute_side_module(&compat::MODULE_URL).await?;
let use_esm_loader =
compat::check_if_should_use_esm_loader(&self.main_module)?;
if use_esm_loader {
// ES module execution in Node compatiblity mode
self.worker.execute_main_module(&self.main_module).await?;
} else {
// CJS module execution in Node compatiblity mode
compat::load_cjs_module(
&mut self.worker.js_runtime,
&self
.main_module
.to_file_path()
.unwrap()
.display()
.to_string(),
true,
)?;
}
} else if self.is_main_cjs {
self.initialize_main_module_for_node().await?; self.initialize_main_module_for_node().await?;
node::load_cjs_module_from_ext_node( node::load_cjs_module_from_ext_node(
&mut self.worker.js_runtime, &mut self.worker.js_runtime,
@ -160,13 +118,6 @@ impl CliMainWorker {
/// Execute the given main module emitting load and unload events before and after execution /// Execute the given main module emitting load and unload events before and after execution
/// respectively. /// respectively.
pub async fn execute(&mut self) -> Result<(), AnyError> { pub async fn execute(&mut self) -> Result<(), AnyError> {
if self.inner.ps.options.compat() {
self
.inner
.worker
.execute_side_module(&compat::GLOBAL_URL)
.await?;
}
self.inner.execute_main_module_possibly_with_npm().await?; self.inner.execute_main_module_possibly_with_npm().await?;
self self
.inner .inner
@ -240,33 +191,9 @@ impl CliMainWorker {
// We only execute the specifier as a module if it is tagged with TestMode::Module or // We only execute the specifier as a module if it is tagged with TestMode::Module or
// TestMode::Both. // TestMode::Both.
if mode != TestMode::Documentation { if mode != TestMode::Documentation {
if self.ps.options.compat() {
self.worker.execute_side_module(&compat::GLOBAL_URL).await?;
self.worker.execute_side_module(&compat::MODULE_URL).await?;
let use_esm_loader =
compat::check_if_should_use_esm_loader(&self.main_module)?;
if use_esm_loader {
self.worker.execute_side_module(&self.main_module).await?;
} else {
compat::load_cjs_module(
&mut self.worker.js_runtime,
&self
.main_module
.to_file_path()
.unwrap()
.display()
.to_string(),
false,
)?;
self.worker.run_event_loop(false).await?;
}
} else {
// We execute the module module as a side module so that import.meta.main is not set. // We execute the module module as a side module so that import.meta.main is not set.
self.execute_side_module_possibly_with_npm().await?; self.execute_side_module_possibly_with_npm().await?;
} }
}
self.worker.dispatch_load_event(&located_script_name!())?; self.worker.dispatch_load_event(&located_script_name!())?;
self self
@ -331,32 +258,8 @@ impl CliMainWorker {
pub async fn run_bench_specifier(&mut self) -> Result<(), AnyError> { pub async fn run_bench_specifier(&mut self) -> Result<(), AnyError> {
self.worker.enable_bench(); self.worker.enable_bench();
if self.ps.options.compat() {
self.worker.execute_side_module(&compat::GLOBAL_URL).await?;
self.worker.execute_side_module(&compat::MODULE_URL).await?;
let use_esm_loader =
compat::check_if_should_use_esm_loader(&self.main_module)?;
if use_esm_loader {
self.worker.execute_side_module(&self.main_module).await?;
} else {
compat::load_cjs_module(
&mut self.worker.js_runtime,
&self
.main_module
.to_file_path()
.unwrap()
.display()
.to_string(),
false,
)?;
self.worker.run_event_loop(false).await?;
}
} else {
// We execute the module module as a side module so that import.meta.main is not set. // We execute the module module as a side module so that import.meta.main is not set.
self.execute_side_module_possibly_with_npm().await?; self.execute_side_module_possibly_with_npm().await?;
}
self.worker.dispatch_load_event(&located_script_name!())?; self.worker.dispatch_load_event(&located_script_name!())?;
self.worker.run_benchmarks().await?; self.worker.run_benchmarks().await?;
@ -543,20 +446,13 @@ pub async fn create_main_worker(
}) })
} }
// TODO(bartlomieju): this callback could have default value
// and not be required
fn create_web_worker_preload_module_callback( fn create_web_worker_preload_module_callback(
ps: ProcState, _ps: ProcState,
) -> Arc<WorkerEventCb> { ) -> Arc<WorkerEventCb> {
let compat = ps.options.compat(); Arc::new(move |worker| {
let fut = async move { Ok(worker) };
Arc::new(move |mut worker| {
let fut = async move {
if compat {
worker.execute_side_module(&compat::GLOBAL_URL).await?;
worker.execute_side_module(&compat::MODULE_URL).await?;
}
Ok(worker)
};
LocalFutureObj::new(Box::new(fut)) LocalFutureObj::new(Box::new(fut))
}) })
} }

View file

@ -11,7 +11,6 @@ use serde::Serialize;
use std::io::ErrorKind; use std::io::ErrorKind;
use std::path::PathBuf; use std::path::PathBuf;
// TODO(bartlomieju): deduplicate with cli/compat/esm_resolver.rs
#[derive(Clone, Debug, Serialize)] #[derive(Clone, Debug, Serialize)]
pub struct PackageJson { pub struct PackageJson {
pub exists: bool, pub exists: bool,