Skip to content

Add ESM support for @azure/functions-core virtual module#791

Open
TsuyoshiUshio wants to merge 1 commit intov3.xfrom
tsushi/esmsupport
Open

Add ESM support for @azure/functions-core virtual module#791
TsuyoshiUshio wants to merge 1 commit intov3.xfrom
tsushi/esmsupport

Conversation

@TsuyoshiUshio
Copy link
Copy Markdown

Add ESM support for @azure/functions-core virtual module

Resolves #104

Summary

The @azure/functions-core virtual module is currently only accessible via CJS require() through a Module.prototype.require Proxy. This PR adds ESM import support using Node.js loader hooks (module.register()), enabling ESM-based code to use import { ... } from '@azure/functions-core' at runtime.

This is Step 1 of the full-stack ESM support plan for the Azure Functions Node.js ecosystem.

Changes

src/setupCoreModule.ts (modified)

  • Expose coreApi on globalThis via Symbol.for('azure-functions-core') so the ESM loader can access it
  • Register ESM loader hooks via module.register() (feature-gated for Node.js >= 20.6)
  • Existing CJS require() Proxy is unchanged -- zero risk to CJS consumers

src/esm-core-loader.mjs (new)

  • ESM loader hooks file registered by module.register()
  • resolve(): Maps @azure/functions-core to a virtual URL
  • load(): Returns synthetic ESM source that re-exports properties from the globalThis coreApi object

src/esmCoreLoaderHooks.ts (new)

  • TypeScript version of the loader hooks for unit test coverage
  • Same logic as esm-core-loader.mjs but importable by Mocha/ts-node

webpack.config.js (modified)

  • Added .mjs handling rule so the ESM loader file is not broken by webpack

test/setupCoreModule.test.ts (new)

  • 8 unit tests covering:
    • CJS Proxy interception (2 tests)
    • globalThis Symbol exposure (1 test)
    • ESM resolve hook -- virtual URL and passthrough (2 tests)
    • ESM load hook -- synthetic source, passthrough, and error case (3 tests)

docs/design-esm-core-module.md (new)

  • Design document covering architecture, alternatives considered, and open questions

How it works

setupCoreModule()
  |-- 1. CJS Proxy (existing, unchanged)
  |     require('@azure/functions-core') --> coreApi
  |
  |-- 2. ESM Loader (new)
        globalThis[Symbol.for('azure-functions-core')] = coreApi
        module.register('./esm-core-loader.mjs')
          |-- resolve(): '@azure/functions-core' --> virtual URL
          |-- load(): virtual URL --> synthetic ESM with named exports

Node.js version compatibility

Node.js module.register() Behavior
18.x Not available Graceful fallback -- CJS only
20.6+ Available Full ESM + CJS support
22.x Available Full ESM + CJS support

Testing

  • New tests: 8/8 passing
  • Full suite: 140 passing, 8 pending, 0 failing
  • No regressions in existing tests

Related issues

Register Node.js ESM loader hooks via module.register so that ESM code
can use import from @azure/functions-core at runtime. The existing
CJS require Proxy is unchanged.

- Expose coreApi on globalThis via Symbol.for azure-functions-core
- Add esm-core-loader.mjs with resolve/load hooks for the virtual module
- Add esmCoreLoaderHooks.ts for testable TypeScript version of the hooks
- Feature-gate module.register call for Node.js 20.6 and above only
- Add 8 unit tests covering CJS interception, globalThis exposure, and
  ESM loader hook resolve/load behavior
- Add design document in docs/design-esm-core-module.md

Resolves #104

Co-authored-by: Dobby <dobby@microsoft.com>
@TsuyoshiUshio TsuyoshiUshio requested a review from a team as a code owner March 27, 2026 21:35
@TsuyoshiUshio
Copy link
Copy Markdown
Author

Created PR first, however, we need to discuss the order of the merging. :)

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.

Add ES6 Style Exports

1 participant