Skip to content

Conversation

@joyeecheung
Copy link
Member

@joyeecheung joyeecheung commented Jan 27, 2026

This patch extends LoadEnvironment to support loading ES modules, and adds the following new types:

enum class ModuleFormat : uint8_t {
  kCommonJS,
  kModule,
};

// Data for specifying an entry point script for LoadEnvironment().
// This class uses an opaque layout to allow future additions without
// breaking ABI. Use the setter methods to configure the entry point.
class ModuleData {
  void set_source(std::string_view source);
  void set_format(ModuleFormat format);
  void set_resource_name(std::string_view name);

  std::string_view source() const;
  ModuleFormat format() const;
  std::string_view resource_name() const;
};

class StartExecutionCallbackInfoWithModule {
  void set_env(Environment* env);
  void set_process_object(v8::Local<v8::Object> process_object);
  void set_native_require(v8::Local<v8::Function> native_require);
  void set_run_module(v8::Local<v8::Function> run_module);
  void set_data(void* data);

  Environment* env();
  v8::Local<v8::Object> process();
  v8::Local<v8::Function> native_require();
  v8::Local<v8::Function> run_module();
  void* data();
};

And two new LoadEnvironment() overloads:

// Run entry point with ModuleData configuration
MaybeLocal<Value> LoadEnvironment(
    Environment* env,
    const ModuleData* entry_point,
    EmbedderPreloadCallback preload = nullptr);

// Callback-based with new StartExecutionCallbackInfoWithModule
MaybeLocal<Value> LoadEnvironment(
    Environment* env,
    StartExecutionCallbackWithModule cb,
    EmbedderPreloadCallback preload = nullptr,
    void* callback_data = nullptr);

Notes about the run_module function:

  • The run_cjs function is renamed to run_module and now accepts
    three arguments: (source, format, resourceName) where format
    corresponds to ModuleFormat values. This keeps the old
    StartExecutionCallback backward-compatible, and reuses this new
    function for StartExecutionCallbackWithModule.
  • ESM entry points return the module namespace object, CJS entry
    points continue to return the wrapper's return value.

The following are left as TODO for follow-up PRs.

  • Support for import() and import.meta
  • Support for SEA: I have a branch here to support ESM in SEA via the new API.
  • Support for snapshot: this requires integration of ModuleWrap objects
  • Support for code cache: needs more plumbing for v8::Module cache

Refs: #53565

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/startup

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Jan 27, 2026
@joyeecheung joyeecheung changed the title src: add support for ESM in embedder API src: initial support for ESM in embedder API Jan 27, 2026
@joyeecheung
Copy link
Member Author

cc @nodejs/embedders

@codecov
Copy link

codecov bot commented Jan 27, 2026

Codecov Report

❌ Patch coverage is 89.94975% with 20 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.78%. Comparing base (83893bb) to head (e5475d4).
⚠️ Report is 17 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/main/embedding.js 82.50% 14 Missing ⚠️
src/api/environment.cc 94.38% 4 Missing and 1 partial ⚠️
src/node.cc 92.30% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main   #61548    +/-   ##
========================================
  Coverage   89.78%   89.78%            
========================================
  Files         672      672            
  Lines      203809   203916   +107     
  Branches    39183    39182     -1     
========================================
+ Hits       182980   183077    +97     
- Misses      13166    13183    +17     
+ Partials     7663     7656     -7     
Files with missing lines Coverage Δ
src/node.h 95.91% <ø> (ø)
src/node_internals.h 82.35% <ø> (ø)
src/node_main_instance.cc 90.14% <100.00%> (ø)
src/node_modules.cc 79.80% <100.00%> (+0.75%) ⬆️
src/node_worker.cc 81.60% <100.00%> (-0.10%) ⬇️
src/node.cc 76.21% <92.30%> (+0.12%) ⬆️
src/api/environment.cc 79.14% <94.38%> (+2.29%) ⬆️
lib/internal/main/embedding.js 85.08% <82.50%> (-3.91%) ⬇️

... and 27 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@addaleax addaleax added the semver-major PRs that contain breaking changes and should be released in the next major version. label Jan 27, 2026
@joyeecheung joyeecheung removed the semver-major PRs that contain breaking changes and should be released in the next major version. label Jan 27, 2026
@joyeecheung
Copy link
Member Author

joyeecheung commented Jan 27, 2026

I don't think this is semver-major - notice none of the existing tests need updating, as the new overloads are backward compatible.

(I would be worth at some point deprecating the older overloads, maybe before March for 26, because the older types are prone to ABI breakage whenever we try to add more features, but that can be a separate PR).

This patch extends `LoadEnvironment` to support loading ES modules,
and adds the following new types:

```cpp
enum class ModuleFormat : uint8_t {
  kCommonJS,
  kModule,
};

// Data for specifying an entry point script for LoadEnvironment().
// This class uses an opaque layout to allow future additions without
// breaking ABI. Use the setter methods to configure the entry point.
class ModuleData {
  void set_source(std::string_view source);
  void set_format(ModuleFormat format);
  void set_resource_name(std::string_view name);

  std::string_view source() const;
  ModuleFormat format() const;
  std::string_view resource_name() const;
};

class StartExecutionCallbackInfoWithModule {
  void set_env(Environment* env);
  void set_process_object(v8::Local<v8::Object> process_object);
  void set_native_require(v8::Local<v8::Function> native_require);
  void set_run_module(v8::Local<v8::Function> run_module);
  void set_data(void* data);

  Environment* env();
  v8::Local<v8::Object> process();
  v8::Local<v8::Function> native_require();
  v8::Local<v8::Function> run_module();
  void* data();
};
```

And two new `LoadEnvironment()` overloads:

```cpp
// Run entry point with ModuleData configuration
MaybeLocal<Value> LoadEnvironment(
    Environment* env,
    const ModuleData* entry_point,
    EmbedderPreloadCallback preload = nullptr);

// Callback-based with new StartExecutionCallbackInfoWithModule
MaybeLocal<Value> LoadEnvironment(
    Environment* env,
    StartExecutionCallbackWithModule cb,
    EmbedderPreloadCallback preload = nullptr,
    void* callback_data = nullptr);
```
@joyeecheung
Copy link
Member Author

hmm, noticed that I accidentally changed the original overloads - updated to remove the changes. This should be now fully backward compatible.

@joyeecheung joyeecheung added commit-queue Add this label to land a pull request using GitHub Actions. semver-minor PRs that contain new features and should be released in the next minor version. and removed commit-queue Add this label to land a pull request using GitHub Actions. labels Jan 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. semver-minor PRs that contain new features and should be released in the next minor version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants