Skip to content

Instantly share code, notes, and snippets.

@3ru
Created September 1, 2025 05:53
Show Gist options
  • Select an option

  • Save 3ru/ae8f9766c51778ad84d460c62e02494a to your computer and use it in GitHub Desktop.

Select an option

Save 3ru/ae8f9766c51778ad84d460c62e02494a to your computer and use it in GitHub Desktop.

GSOC 2025 Report: ESM Output Improvements for Webpack

Introduction

As part of the Google Summer of Code (GSOC) program, I worked on improving ESM output support in Webpack. The main objective was to implement the "ESM Module Output" feature outlined in issue #17121.

With the JavaScript ecosystem transitioning to ES modules, enabling Webpack to output ESM allows for better tree-shaking and more natural dynamic imports. This report summarizes the key contributions I made during the program.


1. Tree-shaking for External ESM Modules

Background

When using Webpack's externals option to exclude external modules from bundling, the previous implementation imported all named exports from ESM external modules, regardless of actual usage.

Implementation

Implemented selective import of only used exports during module concatenation:

// Before: Import everything
import * as __WEBPACK_EXTERNAL_MODULE__ from "external";

// After: Import only what's used
import { HomeLayout, a } from "external";

Key implementation details:

  • Leveraged exportsInfo.getUsedExports in getSourceForModuleExternal function
  • Properly collected and processed InitFragments in ModuleConcatenationPlugin
  • Handled name collision avoidance during external module resolution

This optimization significantly reduces final bundle size by excluding unused exports.


2. ESM Worker and Chunk Loading for Node.js Targets

Background

When using ESM modules in Node.js, chunk loading for Web Workers and Worker Threads was limited to CommonJS format.

Implementation

Added ESM worker support for Node.js targets:

// ESM workers now supported
const worker = new Worker(
  new URL("./worker.js", import.meta.url),
  { type: "module" }
);

Technical details:

  • Confirmed support for Node.js 12.17.0 LTS and later
  • Automatic configuration of dynamicImportInWorker
  • Set output file extension to .mjs

This enables the same worker code to run in both browser and Node.js environments without platform-specific branching.


3. Hot Module Replacement (HMR) for ESM Format

Background

HMR was not implemented when using ESM output with experiments.outputModule: true.

Implementation

Extended the following modules to enable HMR functionality in ESM chunk format:

  • ModuleChunkFormatPlugin: Export __webpack_modules__, __webpack_runtime__, and __webpack_ids__ when rendering HotUpdateChunk
  • ModuleChunkLoadingPlugin: Register runtime requirements for RuntimeGlobals.hmrDownloadUpdateHandlers and RuntimeGlobals.hmrDownloadManifest
  • ModuleChunkLoadingRuntimeModule: Implement dynamic update chunk loading using import()

This enables fast development iteration while using ESM output with various web frameworks.


4. Dynamic Import Improvements for ESM Library Output

Background

Addressing the issue reported in #15947. Webpack bundles output as ESM libraries couldn't load chunks via dynamic imports when used in other Webpack projects.

The previous implementation used string concatenation for URL generation, causing relative path resolution failures and file:// protocol errors when used as ESM modules.

Implementation

Working towards achieving "chunk loading using new URL()" and "statically analyzable import()" statements.

Completed implementations:

  • Basic improvements using new URL() for ESM library output
  • Created LibraryHelpers.js to consolidate library type determination logic
  • Added test cases confirming dynamic imports work in ESM libraries

Currently in development:

  • Making chunk filename generation fully statically analyzable

5. Prefetch/Preload/FetchPriority Support for URL-based Assets

Background

Implementing resource hints functionality requested in issues #19377, #19373, and #18524 for new URL() syntax and Worker syntax.

Implementation

Implemented resource hint configuration using magic comments:

// Prefetch assets for future use
const imageUrl = new URL(
  /* webpackPrefetch: true */
  './assets/hero.jpg',
  import.meta.url
);

Key features implemented:

  • Added prefetchAsset and preloadAsset to RuntimeGlobals for dynamic <link> element generation at runtime
  • Support for numeric ordering in Worker syntax (similar to import() behavior)
  • Automatic as attribute determination based on file extension (image, font, style, script, etc.)

7. Strict Mode Compatibility Checks

Background

When modules are output as ESM, they automatically run in strict mode. This feature detects code patterns that may cause issues during this conversion and warns developers.

Implementation

Implemented compatibility checks based on ECMAScript specification Annex C:

  • Syntax errors: with statements, duplicate parameters, etc.
  • Runtime behavior changes: arguments.callee, top-level this, etc.
  • Assignment errors: assignments to undeclared variables, etc.

Configuration

module.exports = {
  experiments: {
    strictModeChecks: true // or "warn" | "error"
  }
};

This helps detect potential issues early and ensures smooth ESM migration.


8. Additional Contributions

  • Jest UpgradePR #19604: Upgraded test runner to Jest v30
  • Circular Dependency TestsPR #19623: Added test cases for circular dependencies involving ESM external modules
  • CI FixPR #19813: Fixed GitHub Actions to use Node.js 24.5.0
  • Discussion Template ImprovementsPR #19693: Improved discussion templates to reduce maintenance burden and streamline community communication

Conclusion

Through this GSOC program, I have addressed 10 out of 15 tasks outlined in issue #17121, with several PRs already merged and others currently in active development. These contributions have advanced Webpack's ESM output support.

I plan to continue working on the remaining tasks to bring Webpack's ESM output capabilities to their full potential through long-term maintenance and contributions.

Acknowledgments

I would like to thank @alexander-akait, @xiaoxiaojx, and @hai-x for their technical support, and @evenstensberg, @snitin315, and @ovflowd for their overall guidance and support throughout the program.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment