Skip to content
Open
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
67 changes: 67 additions & 0 deletions cli/args/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ pub struct Flags {
pub eszip: bool,
pub node_conditions: Vec<String>,
pub preload: Vec<String>,
pub require: Vec<String>,
pub tunnel: bool,
}

Expand Down Expand Up @@ -4159,6 +4160,7 @@ fn compile_args_without_check_args(app: Command) -> Command {
.arg(ca_file_arg())
.arg(unsafely_ignore_certificate_errors_arg())
.arg(preload_arg())
.arg(require_arg())
.arg(min_dep_age_arg())
}

Expand Down Expand Up @@ -4749,6 +4751,17 @@ fn preload_arg() -> Arg {
.value_hint(ValueHint::FilePath)
}

fn require_arg() -> Arg {
Arg::new("require")
.long("require")
.value_name("FILE")
.action(ArgAction::Append)
.help(
"A list of CommonJS modules that will be executed before the main module",
)
.value_hint(ValueHint::FilePath)
}

fn min_dep_age_arg() -> Arg {
Arg::new("minimum-dependency-age")
.long("minimum-dependency-age")
Expand Down Expand Up @@ -6511,6 +6524,7 @@ fn compile_args_without_check_parse(
ca_file_arg_parse(flags, matches);
unsafely_ignore_certificate_errors_parse(flags, matches);
preload_arg_parse(flags, matches);
require_arg_parse(flags, matches);
min_dep_age_arg_parse(flags, matches);
Ok(())
}
Expand Down Expand Up @@ -6789,6 +6803,12 @@ fn preload_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) {
}
}

fn require_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) {
if let Some(require) = matches.remove_many::<String>("require") {
flags.require = require.collect();
}
}

fn min_dep_age_arg_parse(flags: &mut Flags, matches: &mut ArgMatches) {
flags.minimum_dependency_age = matches.remove_one("minimum-dependency-age");
}
Expand Down Expand Up @@ -13464,6 +13484,53 @@ Usage: deno repl [OPTIONS] [-- [ARGS]...]\n"
);
}

#[test]
fn require_flag_test() {
let flags = flags_from_vec(svec![
"deno",
"run",
"--require",
"require.js",
"main.ts"
])
.unwrap();
assert_eq!(
flags,
Flags {
subcommand: DenoSubcommand::Run(RunFlags {
script: "main.ts".into(),
..Default::default()
}),
require: svec!["require.js"],
code_cache_enabled: true,
..Default::default()
}
);

let flags = flags_from_vec(svec![
"deno",
"run",
"--require",
"r1.js",
"--require",
"./r2.js",
"main.ts"
])
.unwrap();
assert_eq!(
flags,
Flags {
subcommand: DenoSubcommand::Run(RunFlags {
script: "main.ts".into(),
..Default::default()
}),
require: svec!["r1.js", "./r2.js"],
code_cache_enabled: true,
..Default::default()
}
);
}

#[test]
fn check_with_v8_flags() {
let flags =
Expand Down
19 changes: 16 additions & 3 deletions cli/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -642,12 +642,25 @@ impl CliOptions {
return Ok(vec![]);
}

let mut preload = Vec::with_capacity(self.flags.preload.len());
let mut modules = Vec::with_capacity(self.flags.preload.len());
for preload_specifier in self.flags.preload.iter() {
preload.push(resolve_url_or_path(preload_specifier, self.initial_cwd())?);
modules.push(resolve_url_or_path(preload_specifier, self.initial_cwd())?);
}

Ok(preload)
Ok(modules)
}

pub fn require_modules(&self) -> Result<Vec<ModuleSpecifier>, AnyError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, really confusing :D I need an explanation for the difference between all_preload_modules(), preload_modules() and require_modules() - feels like we only need to latter two?

if self.flags.require.is_empty() {
return Ok(vec![]);
}

let mut require = Vec::with_capacity(self.flags.require.len());
for require_specifier in self.flags.require.iter() {
require.push(resolve_url_or_path(require_specifier, self.initial_cwd())?);
}

Ok(require)
}

fn resolve_main_module_with_resolver_if_bare(
Expand Down
1 change: 1 addition & 0 deletions cli/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1271,6 +1271,7 @@ impl CliFactory {
} else {
deno_resolver::loader::AllowJsonImports::WithAttribute
},
require_modules: options.require_modules()?,
},
)))
})
Expand Down
1 change: 1 addition & 0 deletions cli/lib/standalone/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ pub struct Metadata {
pub workspace_resolver: SerializedWorkspaceResolver,
pub entrypoint_key: String,
pub preload_modules: Vec<String>,
pub require_modules: Vec<String>,
pub node_modules: Option<NodeModules>,
pub unstable_config: UnstableConfig,
pub otel_config: OtelConfig,
Expand Down
12 changes: 12 additions & 0 deletions cli/lib/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -583,11 +583,13 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
permissions: PermissionsContainer,
main_module: Url,
preload_modules: Vec<Url>,
require_modules: Vec<Url>,
) -> Result<LibMainWorker, CoreError> {
self.create_custom_worker(
mode,
main_module,
preload_modules,
require_modules,
permissions,
vec![],
Default::default(),
Expand All @@ -602,6 +604,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
mode: WorkerExecutionMode,
main_module: Url,
preload_modules: Vec<Url>,
require_modules: Vec<Url>,
permissions: PermissionsContainer,
custom_extensions: Vec<Extension>,
stdio: deno_runtime::deno_io::Stdio,
Expand Down Expand Up @@ -724,6 +727,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
Ok(LibMainWorker {
main_module,
preload_modules,
require_modules,
worker,
})
}
Expand Down Expand Up @@ -811,6 +815,7 @@ impl<TSys: DenoLibSys> LibMainWorkerFactory<TSys> {
pub struct LibMainWorker {
main_module: Url,
preload_modules: Vec<Url>,
require_modules: Vec<Url>,
worker: MainWorker,
}

Expand Down Expand Up @@ -878,6 +883,13 @@ impl LibMainWorker {
self.worker.evaluate_module(id).await?;
self.worker.run_event_loop(false).await?;
}
// Even though we load as ESM here, these files will be forced to be loaded as CJS
// because of checks in get_known_mode_with_is_script
for require_module_url in self.require_modules.iter() {
let id = self.worker.preload_side_module(require_module_url).await?;
self.worker.evaluate_module(id).await?;
self.worker.run_event_loop(false).await?;
}
Ok(())
}

Expand Down
1 change: 1 addition & 0 deletions cli/lsp/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,7 @@ impl ConfigData {
specified_import_map: None,
types_node_version_req: Some(crate::npm::get_types_node_version_req()),
unstable_sloppy_imports: false,
require_modules: vec![],
},
);
let pb = ProgressBar::new(ProgressBarStyle::TextOnly);
Expand Down
2 changes: 2 additions & 0 deletions cli/lsp/testing/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ impl TestRun {
specifier,
// Executing tests in the LSP currently doesn't support preload option
vec![],
// Executing tests in the LSP currently doesn't support require option
vec![],
worker_sender,
fail_fast_tracker,
test::TestSpecifierOptions {
Expand Down
7 changes: 7 additions & 0 deletions cli/rt/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,11 @@ pub async fn run(
node_resolution_sys,
node_resolver::NodeResolverOptions::default(),
));
let require_modules = metadata
.require_modules
.iter()
.map(|key| root_dir_url.join(key).unwrap())
.collect::<Vec<_>>();
Comment on lines +852 to +856
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Comment on lines +852 to +856
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace .unwrap() with proper error handling.

The .unwrap() on line 855 will cause a panic if metadata.require_modules contains invalid URL components. Since metadata is deserialized from the binary and could be corrupted, this should return a helpful error message instead.

Apply this diff:

-  let require_modules = metadata
-    .require_modules
-    .iter()
-    .map(|key| root_dir_url.join(key).unwrap())
-    .collect::<Vec<_>>();
+  let require_modules = metadata
+    .require_modules
+    .iter()
+    .map(|key| {
+      root_dir_url.join(key).with_context(|| {
+        format!("Failed to resolve require module: {}", key)
+      })
+    })
+    .collect::<Result<Vec<_>, _>>()?;

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In cli/rt/run.rs around lines 852 to 856, replace the .unwrap() on
root_dir_url.join(key) with proper error handling: attempt each join and collect
into a Result<Vec<Url>, E> so failures return a descriptive error instead of
panicking; map the join error to a helpful message that includes the offending
key and the underlying join error, propagate that Result (using ? or early
return) so the caller receives a clear error when metadata.require_modules
contains an invalid URL component.

let cjs_tracker = Arc::new(CjsTracker::new(
in_npm_pkg_checker.clone(),
pkg_json_resolver.clone(),
Expand All @@ -859,6 +864,7 @@ pub async fn run(
} else {
IsCjsResolutionMode::ExplicitTypeCommonJs
},
require_modules.clone(),
));
let npm_req_resolver = Arc::new(NpmReqResolver::new(NpmReqResolverOptions {
sys: sys.clone(),
Expand Down Expand Up @@ -1088,6 +1094,7 @@ pub async fn run(
permissions,
main_module,
preload_modules,
require_modules,
)?;

let exit_code = worker.run().await?;
Expand Down
8 changes: 8 additions & 0 deletions cli/standalone/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,6 +723,13 @@ impl<'a> DenoCompileBinaryWriter<'a> {
.map(|s| root_dir_url.specifier_key(&s).into_owned())
.collect::<Vec<_>>();

let require_modules = self
.cli_options
.require_modules()?
.into_iter()
.map(|s| root_dir_url.specifier_key(&s).into_owned())
.collect::<Vec<_>>();

let metadata = Metadata {
argv: compile_flags.args.clone(),
seed: self.cli_options.seed(),
Expand All @@ -744,6 +751,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
env_vars_from_env_file,
entrypoint_key: root_dir_url.specifier_key(entrypoint).into_owned(),
preload_modules,
require_modules,
workspace_resolver: SerializedWorkspaceResolver {
import_map: self.workspace_resolver.maybe_import_map().map(|i| {
SerializedWorkspaceResolverImportMap {
Expand Down
11 changes: 11 additions & 0 deletions cli/tools/bench/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ async fn bench_specifier(
permissions_container: PermissionsContainer,
specifier: ModuleSpecifier,
preload_modules: Vec<ModuleSpecifier>,
require_modules: Vec<ModuleSpecifier>,
sender: UnboundedSender<BenchEvent>,
filter: TestFilter,
) -> Result<(), AnyError> {
Expand All @@ -165,6 +166,7 @@ async fn bench_specifier(
permissions_container,
specifier.clone(),
preload_modules,
require_modules,
&sender,
filter,
)
Expand All @@ -188,6 +190,7 @@ async fn bench_specifier_inner(
permissions_container: PermissionsContainer,
specifier: ModuleSpecifier,
preload_modules: Vec<ModuleSpecifier>,
require_modules: Vec<ModuleSpecifier>,
sender: &UnboundedSender<BenchEvent>,
filter: TestFilter,
) -> Result<(), CreateCustomWorkerError> {
Expand All @@ -196,6 +199,7 @@ async fn bench_specifier_inner(
WorkerExecutionMode::Bench,
specifier.clone(),
preload_modules,
require_modules,
permissions_container,
vec![ops::bench::deno_bench::init(sender.clone())],
Default::default(),
Expand Down Expand Up @@ -301,6 +305,7 @@ async fn bench_specifiers(
permission_desc_parser: &Arc<RuntimePermissionDescriptorParser<CliSys>>,
specifiers: Vec<ModuleSpecifier>,
preload_modules: Vec<ModuleSpecifier>,
require_modules: Vec<ModuleSpecifier>,
options: BenchSpecifierOptions,
) -> Result<(), AnyError> {
let (sender, mut receiver) = unbounded_channel::<BenchEvent>();
Expand All @@ -313,6 +318,7 @@ async fn bench_specifiers(
let sender = sender.clone();
let options = option_for_handles.clone();
let preload_modules = preload_modules.clone();
let require_modules = require_modules.clone();
let cli_options = cli_options.clone();
let permission_desc_parser = permission_desc_parser.clone();
spawn_blocking(move || {
Expand All @@ -333,6 +339,7 @@ async fn bench_specifiers(
permissions_container,
specifier,
preload_modules,
require_modules,
sender,
options.filter,
);
Expand Down Expand Up @@ -499,6 +506,7 @@ pub async fn run_benchmarks(
}

let preload_modules = cli_options.preload_modules()?;
let require_modules = cli_options.require_modules()?;
let log_level = cli_options.log_level();
let worker_factory =
Arc::new(factory.create_cli_main_worker_factory().await?);
Expand All @@ -508,6 +516,7 @@ pub async fn run_benchmarks(
factory.permission_desc_parser()?,
specifiers,
preload_modules,
require_modules,
BenchSpecifierOptions {
filter: TestFilter::from_flag(&workspace_bench_options.filter),
json: workspace_bench_options.json,
Expand Down Expand Up @@ -637,12 +646,14 @@ pub async fn run_benchmarks_with_watch(

let log_level = cli_options.log_level();
let preload_modules = cli_options.preload_modules()?;
let require_modules = cli_options.require_modules()?;
bench_specifiers(
worker_factory,
cli_options,
factory.permission_desc_parser()?,
specifiers,
preload_modules,
require_modules,
BenchSpecifierOptions {
filter: TestFilter::from_flag(&workspace_bench_options.filter),
json: workspace_bench_options.json,
Expand Down
4 changes: 4 additions & 0 deletions cli/tools/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,10 @@ fn get_module_roots_and_include_paths(
module_roots.push(preload_module);
}

for require_module in cli_options.require_modules()? {
module_roots.push(require_module);
}

Ok((module_roots, include_paths))
}

Expand Down
1 change: 1 addition & 0 deletions cli/tools/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub async fn deploy(mut flags: Flags) -> Result<i32, AnyError> {
WorkerExecutionMode::Deploy,
specifier,
vec![],
vec![],
PermissionsContainer::allow_all(
factory.permission_desc_parser()?.clone(),
),
Expand Down
2 changes: 2 additions & 0 deletions cli/tools/jupyter/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ pub async fn kernel(
main_module.clone(),
// `deno jupyter` doesn't support preloading modules
vec![],
// `deno jupyter` doesn't support require modules
vec![],
permissions,
vec![
ops::jupyter::deno_jupyter::init(stdio_tx.clone()),
Expand Down
2 changes: 2 additions & 0 deletions cli/tools/lint/plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ async fn create_plugin_runner_inner(
main_module.clone(),
// `deno lint` doesn't support preloading modules
vec![],
// `deno lint` doesn't support require modules
vec![],
permissions,
vec![crate::ops::lint::deno_lint_ext::init(logger.clone())],
Default::default(),
Expand Down
Loading
Loading