Skip to content

Conversation

@fraidev
Copy link
Contributor

@fraidev fraidev commented Nov 6, 2025

Solution for #31154

This PR implements Node.js-compatible --require flag that allows preloading modules before the main script executes.

Also, ensuring that any module loaded via the --require flag is always treated as CommonJS.

Example:
deno run --require ./setup.js --require ./logger.cjs main.ts

Implementation

CjsTracker already handles CommonJS detection based on:

  • File extension (.cjs, .cts)
  • package.json "type" field
  • IsCjsResolutionMode (Disabled/ExplicitTypeCommonJs/ImplicitTypeCommonJs)

This change adds a fourth dimension: explicit --require modules.

The require_modules list flows through the system:
CLI flags → CliOptions.require_modules() → ResolverFactoryOptions
→ ResolverFactory → CjsTracker.new(require_modules)

When CjsTracker.is_maybe_cjs() is called, it now checks:
1. Is this specifier in require_modules? → Return true (always CJS)
2. Otherwise, use existing logic (extension, package.json, etc.)

This ensures early exit for --require modules before checking extension or package.json, guaranteeing CommonJS treatment.

@fraidev fraidev force-pushed the require branch 6 times, most recently from 40c6581 to 085e3df Compare November 8, 2025 02:05
@fraidev fraidev marked this pull request as ready for review November 8, 2025 02:05
Copilot AI review requested due to automatic review settings November 8, 2025 02:05
@fraidev fraidev changed the title feat: add --require run argument feat: add --require run arg to support commonJS Nov 8, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request implements support for the --require flag in Deno, which allows loading CommonJS modules before the main module execution. This feature is Node.js-compatible and ensures that modules loaded via --require are always treated as CommonJS, regardless of their file extension.

  • Added --require flag for preloading CommonJS modules
  • Integrated require modules into the CJS tracking system to force CommonJS resolution
  • Updated all relevant commands (run, test, bench, compile) to handle require modules

Reviewed Changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
cli/args/flags.rs Added --require flag definition and parsing logic
cli/args/mod.rs Added methods to retrieve require modules and combined preload modules
cli/factory.rs Passed require_modules to resolver factory options
cli/tools/run/mod.rs Updated to use all_preload_modules() for combined module loading
cli/tools/test/mod.rs Updated to use all_preload_modules() for combined module loading
cli/tools/bench/mod.rs Updated to use all_preload_modules() for combined module loading
cli/tools/compile.rs Updated to use all_preload_modules() for combined module loading
cli/standalone/binary.rs Added require_modules serialization for standalone binaries
cli/rt/run.rs Integrated require_modules into CjsTracker and preload execution
cli/lsp/config.rs Initialized empty require_modules for LSP configuration
cli/lib/standalone/binary.rs Added require_modules field to Metadata struct
libs/resolver/factory.rs Added require_modules field to ResolverFactoryOptions
libs/resolver/cjs/mod.rs Added require_modules tracking to force CommonJS resolution
tests/specs/run/require_modules_node/* Test for require with node built-in modules
tests/specs/run/require_modules_multiple/* Test for multiple require modules

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@fraidev fraidev force-pushed the require branch 2 times, most recently from aa69f3b to 9a66cfb Compare November 8, 2025 02:22
@fraidev fraidev requested a review from bartlomieju November 8, 2025 03:14
.long("require")
.value_name("FILE")
.action(ArgAction::Append)
.help("A list of CommonJS modules that will be executed before the main module regardless of their file extension")
Copy link
Member

Choose a reason for hiding this comment

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

I'm not 100% sure we should mention the "regardless of their file extension". @dsherret what do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Make sense, I removed this part

cli/args/mod.rs Outdated
Comment on lines 647 to 653
let mut modules = IndexSet::new();
for preload_specifier in self.flags.preload.iter() {
modules
.insert(resolve_url_or_path(preload_specifier, self.initial_cwd())?);
}

for require_specifier in self.flags.require.iter() {
Copy link
Member

Choose a reason for hiding this comment

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

Can you please explain this one? Why are you storing both --preload and --require modules in the same set? preload are ESM while require are CJS

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was my solution to not add a new Vec argument in all the system where is using modules.

As we only need to know if a module is a CJS or ESM module at CjsTracker phase, and there we have the modules separated (preload_modules and require_modules) to check if the URL is CJS or ESM module.
https://github.com/denoland/deno/pull/31201/files/9a66cfbca46ba77fba3c37867a47b9043d6b3563#diff-30d2cefb89866d76c4d39015b82c80a6f8ae31b4e02894bc9f20d87caf7e8c3bR141

I can back with only preload_modules and require_modules and merge them when is need (like all_preload_modules) or just add a new argument for require_modules in every place. WDYT?

Copy link
Contributor Author

@fraidev fraidev Nov 10, 2025

Choose a reason for hiding this comment

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

Or maybe rename to something like preload_and_require_modules?

Copy link
Member

Choose a reason for hiding this comment

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

Okay, now I understand where my confusion comes from. I'd say it's better to keep them separate - I completely missed the is_from_require argument in get_known_mode_with_is_script.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I added a commit to pass preload and require modules separately

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?

Comment on lines +852 to +856
let require_modules = metadata
.require_modules
.iter()
.map(|key| root_dir_url.join(key).unwrap())
.collect::<Vec<_>>();
Copy link
Member

Choose a reason for hiding this comment

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

👍

@@ -0,0 +1,2 @@
console.log("require_first.js loading");
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I might be missing something here - but this will not execute as a CommonJS, but ESM - ie. if you try to access __filename or require you'll get hit with ReferenceError. Is this the behavior Node has?

Copy link
Contributor Author

@fraidev fraidev Nov 10, 2025

Choose a reason for hiding this comment

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

__filename or require should work, this is working fine for me:

index.js:

const os = require("node:os");

console.log(`cjs preload: ${os.platform()}`);

console.log(__filename)

globalThis.wasPreloaded = true;

main.ts:

console.log(`module was preloaded: ${globalThis.wasPreloaded}`);

Run and Output:

denod run --require index.js main.ts                                                                                                                                          $!? require 
cjs preload: darwin
/Users/frai/workspace/deno/deno/cjs.js
module was preloaded: true

(denod = alias for ./target/debug/deno)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I add more test cases using it?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, having tests that call require() and/or __filename and __dirname would be useful, thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I merged both tested in one to use multiples require modules and using require(), __filename and __dirname

@fraidev fraidev force-pushed the require branch 5 times, most recently from 60998dd to 102d2c0 Compare November 14, 2025 14:14
Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

Almost there, just a few more changes

@coderabbitai
Copy link

coderabbitai bot commented Nov 17, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new --require CLI option and threads its values (require_modules) through argument parsing, resolver/CJS tracking, worker factories, runtime execution, tooling, standalone metadata, and tests to preload required modules before the main script.

Changes

Cohort / File(s) Summary
CLI flags & args
cli/args/flags.rs, cli/args/mod.rs
Added pub require: Vec<String> to Flags, implemented require_arg() parsing, and added require_modules(&self) to resolve specifiers.
Resolver factory & options
cli/factory.rs, cli/lsp/config.rs, libs/resolver/factory.rs
Plumbed require_modules into ResolverFactoryOptions and passed it into resolver/CJS tracker initialization.
CommonJS tracking & resolver
libs/resolver/cjs/mod.rs
Added require_modules: Vec<Url> to CjsTracker, extended constructor, introduced is_from_require checks, updated resolution APIs, and treat non-code media from require as ResolutionMode::Require.
Worker factory & worker types
cli/worker.rs, cli/lib/worker.rs, cli/lib/standalone/binary.rs
Extended worker factory signatures to accept require_modules, added require_modules field to LibMainWorker/Metadata, and execute require_modules alongside preload_modules.
Runtime & CJS propagation
cli/rt/run.rs, cli/standalone/binary.rs
Compute require_modules from metadata/options, convert to URLs, pass into CjsTracker and into worker creation paths (standard and standalone).
Tooling integrations (pass-through)
cli/tools/run/mod.rs, cli/tools/test/mod.rs, cli/tools/bench/mod.rs
Thread require_modules: Vec<ModuleSpecifier> from CLI options through run/test/bench flows and into worker creation; updated public signatures where applicable.
Tooling integrations (no-op placeholders)
cli/tools/deploy.rs, cli/tools/jupyter/mod.rs, cli/tools/lint/plugins.rs, cli/tools/repl/mod.rs, cli/tools/serve.rs, cli/lsp/testing/execution.rs, cli/tools/compile.rs
Added extra empty Vec arguments for require modules where feature is not supported; compile now includes require modules in module_roots.
Standalone metadata
cli/lib/standalone/binary.rs, cli/standalone/binary.rs
Added pub require_modules: Vec<String> to standalone Metadata and include require entries when constructing metadata.
Tests / fixtures
tests/specs/run/require_modules_multiple/*
Added test spec and files (__test__.jsonc, main_multiple.ts, require_first.js, require_second.js) to verify multiple --require modules are loaded and executed.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as CLI Parser
    participant Resolver as ResolverFactory
    participant CJS as CjsTracker
    participant WorkerFactory as Worker Factory
    participant Worker as Main Worker
    participant Loader as Module Loader

    User->>CLI: deno run --require r1.js --require r2.js main.js
    CLI->>CLI: parse --require flags -> Flags.require
    CLI->>Resolver: require_modules() -> resolve to ModuleSpecifiers
    Resolver->>CJS: initialize CjsTracker(require_modules)
    CJS->>CJS: mark require_modules for resolution

    Resolver->>WorkerFactory: create_main_worker(preload, require_modules, ...)
    WorkerFactory->>Worker: construct worker(with require_modules)
    Worker->>Loader: execute_preload_modules() and execute_require_modules()
    rect rgba(220,240,220,0.6)
      note right of Loader: For each module: load -> evaluate
      Loader->>CJS: resolution checks consider is_from_require
    end

    Loader->>Worker: setup complete
    Worker->>Worker: run main.js
    Worker->>User: stdout / exit
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to consistent propagation of the new require_modules parameter across factory and worker creation call sites.
  • Review CjsTracker changes: correctness of is_from_require detection and its effect on non-code media resolution.
  • Verify standalone metadata additions and test fixtures for correct URL/serialization handling and deterministic ordering.

Poem

🐇 I nibbled flags and found a thread,

require files waiting, snug in bed.
Workers wake and hop to run,
Globals set — the tests say "done!"
A tiny twitch, a joyous pun.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature being added: a --require CLI argument for CommonJS module preloading, which is the primary change across all modified files.
Description check ✅ Passed The description clearly explains the implementation of a Node.js-compatible --require flag, references the associated GitHub issue, and details how the feature flows through the codebase and integrates with CjsTracker.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
cli/rt/run.rs (1)

852-868: Require-module wiring is correct; consider de‑duplicating computation

The new require_modules handling correctly:

  • Resolves metadata.require_modules against root_dir_url.
  • Passes the list into CjsTracker::new so those modules are always treated as CJS.
  • Passes a (recomputed) list into create_main_worker so the worker factory can preload them.

Functionally this is fine, but you’re computing require_modules twice with identical logic and shadowing the name. Consider computing it once and either cloning for the CjsTracker or passing by Arc/shared slice to both call sites to avoid drift if this mapping changes later.

Also applies to: 1092-1104

cli/args/flags.rs (3)

4163-4165: Scope --require to runtime subcommands (avoid exposing it on resolution-only commands)

Wiring via compile_args_without_check_args/compile_args_without_check_parse makes --require appear on check, doc, cache, fmt, coverage, info, etc., which don't execute code. This can confuse users and doesn’t add value. Prefer scoping to runtime-only paths (run, serve, test, bench, eval, compile) by moving the arg and its parser into the runtime args.

Suggested changes:

  • Remove from compile args…
--- a/cli/args/flags.rs
@@
-    .arg(require_arg())
--- a/cli/args/flags.rs
@@
-  require_arg_parse(flags, matches);
  • …and add to runtime args:
--- a/cli/args/flags.rs
@@ fn runtime_misc_args(app: Command) -> Command {
   app
     .arg(cached_only_arg())
     .arg(location_arg())
     .arg(v8_flags_arg())
     .arg(seed_arg())
     .arg(enable_testing_features_arg())
     .arg(trace_ops_arg())
-    .arg(eszip_arg())
+    .arg(eszip_arg())
+    .arg(require_arg())
 }
--- a/cli/args/flags.rs
@@ fn runtime_args_parse(
   env_file_arg_parse(flags, matches);
   trace_ops_parse(flags, matches);
   eszip_arg_parse(flags, matches);
+  require_arg_parse(flags, matches);
   Ok(())
 }

This keeps --require where it actually has effect, reduces surface area, and aligns with user expectations.

Also applies to: 6527-6529


4754-4764: Clarify help to state execution order

Tiny UX nit: mention order to match Node users’ expectations.

-    .help(
-      "A list of CommonJS modules that will be executed before the main module",
-    )
+    .help("CommonJS modules to run before the main module (in the order specified)")

13487-13533: Add a guardrail test for the -r short flag confusion

Given -r is already used by --reload, a quick test that deno run -r setup.js main.ts fails with a helpful message (or at least does not set Flags.require) would reduce user confusion when migrating from Node. If you adopt the scoping change above, also consider a negative test that deno check --require x is rejected.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5421a29 and 0eac61e.

⛔ Files ignored due to path filters (1)
  • tests/specs/run/require_modules_multiple/multiple.out is excluded by !**/*.out
📒 Files selected for processing (25)
  • cli/args/flags.rs (6 hunks)
  • cli/args/mod.rs (1 hunks)
  • cli/factory.rs (1 hunks)
  • cli/lib/standalone/binary.rs (1 hunks)
  • cli/lib/worker.rs (5 hunks)
  • cli/lsp/config.rs (1 hunks)
  • cli/lsp/testing/execution.rs (1 hunks)
  • cli/rt/run.rs (3 hunks)
  • cli/standalone/binary.rs (2 hunks)
  • cli/tools/bench/mod.rs (10 hunks)
  • cli/tools/compile.rs (1 hunks)
  • cli/tools/deploy.rs (1 hunks)
  • cli/tools/jupyter/mod.rs (1 hunks)
  • cli/tools/lint/plugins.rs (1 hunks)
  • cli/tools/repl/mod.rs (1 hunks)
  • cli/tools/run/mod.rs (10 hunks)
  • cli/tools/serve.rs (2 hunks)
  • cli/tools/test/mod.rs (10 hunks)
  • cli/worker.rs (4 hunks)
  • libs/resolver/cjs/mod.rs (6 hunks)
  • libs/resolver/factory.rs (2 hunks)
  • tests/specs/run/require_modules_multiple/__test__.jsonc (1 hunks)
  • tests/specs/run/require_modules_multiple/main_multiple.ts (1 hunks)
  • tests/specs/run/require_modules_multiple/require_first.js (1 hunks)
  • tests/specs/run/require_modules_multiple/require_second.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
cli/tools/compile.rs (2)
cli/factory.rs (1)
  • cli_options (375-386)
cli/standalone/binary.rs (2)
  • cli_options (719-724)
  • cli_options (726-731)
cli/standalone/binary.rs (2)
cli/args/mod.rs (1)
  • require_modules (653-664)
libs/config/workspace/mod.rs (1)
  • root_dir_url (477-479)
cli/lib/worker.rs (1)
cli/args/mod.rs (2)
  • require_modules (653-664)
  • preload_modules (640-651)
libs/resolver/cjs/mod.rs (1)
cli/args/mod.rs (1)
  • require_modules (653-664)
cli/args/flags.rs (2)
ext/node/polyfills/01_require.js (2)
  • require (1031-1031)
  • require (1144-1146)
tests/specs/bundle/main/uses_node_builtin.cjs (1)
  • require (1-1)
cli/rt/run.rs (3)
cli/args/mod.rs (2)
  • require_modules (653-664)
  • preload_modules (640-651)
libs/config/workspace/mod.rs (1)
  • root_dir_url (477-479)
cli/lib/worker.rs (1)
  • main_module (827-829)
libs/resolver/factory.rs (1)
ext/node/polyfills/url.ts (1)
  • Url (160-912)
cli/tools/test/mod.rs (2)
cli/args/mod.rs (2)
  • preload_modules (640-651)
  • require_modules (653-664)
cli/standalone/binary.rs (2)
  • cli_options (719-724)
  • cli_options (726-731)
cli/tools/run/mod.rs (3)
cli/args/mod.rs (2)
  • require_modules (653-664)
  • preload_modules (640-651)
cli/factory.rs (1)
  • cli_options (375-386)
cli/standalone/binary.rs (2)
  • cli_options (719-724)
  • cli_options (726-731)
cli/worker.rs (1)
cli/args/mod.rs (2)
  • require_modules (653-664)
  • preload_modules (640-651)
cli/tools/bench/mod.rs (2)
cli/args/mod.rs (2)
  • require_modules (653-664)
  • preload_modules (640-651)
cli/standalone/binary.rs (2)
  • cli_options (719-724)
  • cli_options (726-731)
🔇 Additional comments (29)
cli/tools/lint/plugins.rs (1)

160-161: LGTM! Clear documentation for unsupported feature.

The empty vector for require modules is appropriate here, and the comment clearly explains that deno lint doesn't support this feature, matching the pattern used for preload modules.

cli/tools/serve.rs (1)

103-103: LGTM! Consistent placeholder for future feature.

Both call sites correctly pass empty vectors for require modules, matching the pattern used for preload modules. The existing TODO comments indicate these parameters may be populated in future work.

Also applies to: 159-159

cli/lib/worker.rs (2)

586-586: LGTM! Clean API extension for require modules.

The require_modules parameter is properly threaded through the worker creation API:

  • Added to public method signatures with correct type (Vec<Url>)
  • Passed through to underlying create_custom_worker implementation
  • Stored in LibMainWorker struct for execution during preload phase

The API changes are consistent and well-structured.

Also applies to: 592-592, 607-607, 730-730, 818-818


886-892: LGTM! Execution logic with clear explanation.

The require modules are executed correctly during the preload phase, mirroring the existing preload_modules pattern. The comment at lines 886-887 helpfully clarifies that while these modules are loaded as ESM (via preload_side_module), they will be forced to be treated as CommonJS during resolution due to checks in get_known_mode_with_is_script, addressing previous review feedback.

libs/resolver/cjs/mod.rs (3)

31-31: LGTM! Standard field addition.

The require_modules field is properly added to CjsTracker with correct initialization in the constructor.

Also applies to: 41-41, 50-50


141-141: LGTM! Correct flag computation and propagation.

The is_from_require flag is correctly computed by checking if the specifier is in require_modules and properly propagated through the resolution chain to reach the decision point.

Also applies to: 146-146, 231-231


266-268: LGTM! Critical logic for forcing require modules to CommonJS.

The early return when is_from_require is true correctly ensures that modules loaded via --require are always treated as CommonJS, regardless of file extension or content. The placement before other checks ensures this takes precedence, which is the intended behavior per the PR objectives.

cli/tools/deploy.rs (1)

64-64: LGTM! Correct placeholder for deploy command.

The empty vector is appropriate since the deploy command does not use require modules.

cli/tools/compile.rs (1)

443-445: LGTM! Ensures require modules are included in compilation.

The code correctly iterates over require_modules and adds them to module_roots, mirroring the existing pattern for preload_modules. This ensures that modules loaded via --require are included in the compiled output's dependency graph.

tests/specs/run/require_modules_multiple/require_second.js (1)

1-9: LGTM! Well-structured test module.

This test file correctly validates the multiple require modules feature by:

  • Using CommonJS require() syntax
  • Accessing CommonJS-specific globals (__filename, __dirname)
  • Exposing test globals for verification by the main test
  • Providing console output for execution order verification

The test complements require_first.js to ensure multiple --require modules are loaded sequentially before the main script.

tests/specs/run/require_modules_multiple/__test__.jsonc (1)

1-5: LGTM! Test specification correctly validates multiple require modules.

The test spec properly exercises the new --require flag with multiple modules and expects successful execution.

cli/tools/repl/mod.rs (1)

189-190: LGTM! Correctly extends worker creation for require_modules.

The empty vector with explanatory comment is appropriate for the REPL context, which doesn't support require modules.

tests/specs/run/require_modules_multiple/main_multiple.ts (1)

1-10: LGTM! Test script correctly validates require module execution.

The script properly logs global values that should be set by the required modules, enabling verification of the require_modules feature.

cli/lsp/testing/execution.rs (1)

326-327: LGTM! Correctly threads require_modules through LSP test execution.

The empty vector with explanatory comment is appropriate, as LSP test execution doesn't support the require option.

cli/tools/jupyter/mod.rs (1)

105-106: LGTM! Correctly extends Jupyter worker creation for require_modules.

The empty vector with explanatory comment is appropriate for the Jupyter context, which doesn't support require modules.

cli/lib/standalone/binary.rs (1)

90-90: LGTM! Field addition is correct and well-positioned.

The require_modules field mirrors the preload_modules field structure and is appropriately positioned for serialization in standalone binaries.

cli/standalone/binary.rs (2)

726-731: LGTM! require_modules computation mirrors preload_modules pattern.

The transformation logic correctly resolves require module specifiers and converts them to root-directory-relative keys, matching the established pattern for preload_modules.


754-754: LGTM! Metadata initialization includes require_modules.

The field is correctly included in the Metadata struct initialization, completing the propagation of require_modules into the standalone binary.

cli/lsp/config.rs (1)

1472-1472: LGTM! ResolverFactoryOptions correctly initialized with require_modules.

The empty vector is appropriate for the LSP configuration path, which doesn't utilize require modules.

cli/args/mod.rs (1)

640-663: Require/preload module resolution is consistent and correct

require_modules() cleanly mirrors preload_modules(): both resolve specifiers via resolve_url_or_path relative to initial_cwd, short‑circuit on empty input, and propagate errors. No functional issues spotted.

tests/specs/run/require_modules_multiple/require_first.js (1)

1-9: Good CJS preload coverage for --require

This fixture exercises require("node:os"), __filename, and __dirname, and exposes markers on globalThis, which is exactly what’s needed to verify --require modules run as CommonJS.

libs/resolver/factory.rs (1)

661-687: Plumbing require_modules into CJS tracker looks sound

Adding require_modules: Vec<Url> to ResolverFactoryOptions and threading it into CjsTracker::new provides the required extra signal for CJS detection while defaulting to an empty list for non‑--require contexts. This is backwards‑compatible and keeps responsibilities well‑separated.

Also applies to: 845-854

cli/args/flags.rs (2)

809-811: LGTM: new Flags.require field

Adding pub require: Vec<String> next to preload is consistent and keeps defaults zero-cost. No concerns.


6806-6811: LGTM: parser is simple and consistent with preload

require_arg_parse mirrors preload_arg_parse; no issues.

cli/tools/bench/mod.rs (1)

160-160: LGTM! Consistent threading of require_modules through the bench tooling.

The new require_modules parameter is correctly propagated through the entire bench execution pipeline:

  • bench_specifierbench_specifier_innercreate_custom_worker
  • bench_specifiers appropriately clones the vector for concurrent execution
  • Both run_benchmarks and run_benchmarks_with_watch obtain require_modules from cli_options

The implementation follows the same pattern as preload_modules, ensuring consistency across the codebase.

Also applies to: 193-193, 308-308, 509-509, 649-649

cli/tools/test/mod.rs (2)

651-651: Appropriate lint suppression for expanded function signatures.

The #[allow(clippy::too_many_arguments)] attributes on configure_main_worker and test_specifier are appropriate given that these functions now have additional parameters to support require_modules. This follows the same pattern used elsewhere in the codebase for complex worker configuration functions.

Also applies to: 713-713


656-656: LGTM! Correct propagation of require_modules through test execution paths.

The require_modules parameter is properly threaded through the test execution pipeline:

  • test_specifierconfigure_main_workercreate_custom_worker
  • test_specifiers appropriately clones the vector for concurrent test execution
  • Both run_tests and run_tests_with_watch obtain require_modules from cli_options

The implementation mirrors the existing preload_modules pattern, maintaining consistency and ensuring that required modules are available for all test execution scenarios.

Also applies to: 719-719, 1178-1178, 1594-1594, 1808-1808

cli/worker.rs (1)

331-338: LGTM! Worker factory methods correctly extended with require_modules.

All three worker creation methods properly accept and forward the require_modules parameter:

  • create_main_worker (lines 331, 338)
  • create_main_worker_with_unconfigured_runtime (lines 352, 360)
  • create_custom_worker (lines 375, 435)

The parameter is consistently passed through to LibMainWorkerFactory::create_custom_worker, ensuring that required modules are available during worker initialization. The implementation follows the established pattern used for preload_modules.

Also applies to: 352-360, 375-435

cli/tools/run/mod.rs (1)

94-94: LGTM! Complete coverage of require_modules across all run execution paths.

The require_modules parameter has been correctly integrated into all execution modes:

  • run_script (lines 94, 110)
  • run_from_stdin (lines 132, 157)
  • run_with_watch (lines 198, 211-216)
  • eval_command (lines 244, 271)
  • run_eszip (lines 323, 333)

Each path consistently obtains require_modules from cli_options.require_modules() and forwards it to the appropriate worker creation method. This ensures uniform support for the --require flag across all runtime scenarios.

Also applies to: 132-132, 198-198, 244-244, 323-323

@fraidev fraidev force-pushed the require branch 2 times, most recently from 2486454 to efd7a58 Compare November 17, 2025 23:01
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2486454 and efd7a58.

⛔ Files ignored due to path filters (1)
  • tests/specs/run/require_modules_multiple/multiple.out is excluded by !**/*.out
📒 Files selected for processing (6)
  • cli/factory.rs (1 hunks)
  • cli/lib/worker.rs (5 hunks)
  • cli/rt/run.rs (3 hunks)
  • tests/specs/run/require_modules_multiple/main_multiple.ts (1 hunks)
  • tests/specs/run/require_modules_multiple/require_first.js (1 hunks)
  • tests/specs/run/require_modules_multiple/require_second.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/specs/run/require_modules_multiple/main_multiple.ts
  • tests/specs/run/require_modules_multiple/require_first.js
  • tests/specs/run/require_modules_multiple/require_second.js
🧰 Additional context used
🧬 Code graph analysis (2)
cli/rt/run.rs (2)
cli/args/mod.rs (1)
  • require_modules (653-664)
libs/config/workspace/mod.rs (1)
  • root_dir_url (477-479)
cli/lib/worker.rs (1)
cli/args/mod.rs (2)
  • require_modules (653-664)
  • preload_modules (640-651)
🔇 Additional comments (8)
cli/factory.rs (1)

1274-1274: LGTM! Error propagation is correct.

The use of ? properly propagates resolution errors from require_modules() through the get_or_try_init() closure, consistent with other option method calls in the codebase.

cli/rt/run.rs (2)

857-868: LGTM! Correct integration with CjsTracker.

The require_modules parameter is correctly passed to CjsTracker::new(), properly integrating the --require functionality into the standalone binary's CJS tracking.


1092-1098: LGTM! Proper threading of require_modules to worker.

The require_modules is correctly passed to the worker factory, ensuring that required modules are available during standalone execution.

cli/lib/worker.rs (5)

580-598: LGTM! Parameter threading is clean and consistent.

The require_modules parameter is correctly added and forwarded through create_main_worker to create_custom_worker, following the same pattern as preload_modules.


600-612: LGTM! Parameter addition is correct.

The require_modules parameter is properly added to create_custom_worker and appropriately used in the worker construction at line 730.


727-733: LGTM! Worker initialization includes require_modules correctly.

The require_modules field is properly initialized in the LibMainWorker construction, maintaining consistency with the preload_modules pattern.


815-820: LGTM! Struct field is properly defined.

The require_modules field is correctly added to LibMainWorker, following the same pattern as the existing preload_modules field.


880-894: LGTM! The require_modules execution is correctly implemented.

The implementation properly loads require_modules after preload_modules, following the same pattern of preload_side_moduleevaluate_modulerun_event_loop with appropriate error handling. The comment clarifies the ESM-to-CJS treatment mechanism as requested in previous review feedback.

Comment on lines +852 to +856
let require_modules = metadata
.require_modules
.iter()
.map(|key| root_dir_url.join(key).unwrap())
.collect::<Vec<_>>();
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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
cli/rt/run.rs (1)

852-868: Avoid panicking on invalid require_modules URLs deserialized from metadata

metadata.require_modules comes from the embedded metadata; using root_dir_url.join(key).unwrap() will panic if any entry is malformed or no longer a valid URL segment. It’s better to fail with a structured error instead of crashing the process.

You can mirror the existing Result-based patterns in this function, for example:

-  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))
+    .collect::<Result<Vec<_>, _>>()
+    .map_err(AnyError::from)?;

This preserves the current behavior on success while surfacing a clear error instead of a panic when the metadata is bad.

🧹 Nitpick comments (1)
cli/lib/worker.rs (1)

723-732: LibMainWorker storing require_modules is a reasonable extension

Capturing require_modules alongside preload_modules on LibMainWorker is the right place for this state and keeps all startup module lists together. If this grows further, you could consider a small struct to group these lists, but it’s fine as-is.

Also applies to: 815-820

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efd7a58 and 28d1f27.

⛔ Files ignored due to path filters (1)
  • tests/specs/run/require_modules_multiple/multiple.out is excluded by !**/*.out
📒 Files selected for processing (6)
  • cli/factory.rs (1 hunks)
  • cli/lib/worker.rs (5 hunks)
  • cli/rt/run.rs (3 hunks)
  • tests/specs/run/require_modules_multiple/main_multiple.ts (1 hunks)
  • tests/specs/run/require_modules_multiple/require_first.js (1 hunks)
  • tests/specs/run/require_modules_multiple/require_second.js (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • tests/specs/run/require_modules_multiple/require_second.js
  • cli/factory.rs
  • tests/specs/run/require_modules_multiple/require_first.js
🧰 Additional context used
🧬 Code graph analysis (2)
cli/lib/worker.rs (1)
cli/args/mod.rs (2)
  • require_modules (653-664)
  • preload_modules (640-651)
cli/rt/run.rs (2)
cli/args/mod.rs (1)
  • require_modules (653-664)
libs/config/workspace/mod.rs (1)
  • root_dir_url (477-479)
🔇 Additional comments (4)
tests/specs/run/require_modules_multiple/main_multiple.ts (1)

1-10: LGTM! Test structure properly validates multiple --require modules.

This test file correctly validates that:

  • Multiple modules can be preloaded via --require
  • Preloaded modules execute before the main script
  • Globals set by preloaded modules are accessible in main
  • CommonJS __filename and __dirname variables work in required modules

The logging approach is appropriate for Deno's spec test pattern, where console output is compared against expected results.

cli/rt/run.rs (1)

1086-1098: Plumbing require_modules into create_main_worker looks consistent

Passing require_modules alongside preload_modules into create_main_worker and ultimately into LibMainWorker aligns with how the non-standalone path handles these lists, and preserves the existing preload-before-main ordering.

cli/lib/worker.rs (2)

579-612: Threading require_modules through LibMainWorkerFactory APIs looks correct

Adding require_modules: Vec<Url> to create_main_worker and create_custom_worker and passing it straight through to worker construction keeps the factory API coherent with the new CLI options and standalone path.


880-892: Require modules execution semantics match preload behavior and CJS comment clarifies intent

Executing each require_module_url via preload_side_module + evaluate_module + run_event_loop(false) mirrors the existing preload_modules behavior and ensures --require modules run before the main module. The comment about CJS handling via get_known_mode_with_is_script is helpful for future readers and addresses the earlier confusion.

@fraidev fraidev requested a review from bartlomieju November 18, 2025 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants