Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat: support module.createRequire when mixedModules: false#558

Merged
styfle merged 11 commits intovercel:mainfrom
logaretm:awad/fix-create-require-mixed-modules
Jan 7, 2026
Merged

feat: support module.createRequire when mixedModules: false#558
styfle merged 11 commits intovercel:mainfrom
logaretm:awad/fix-create-require-mixed-modules

Conversation

@logaretm
Copy link
Contributor

This PR adds several fixes and tests for various edge cases around module.createRequire.

Null Checks

When mixedModules: false and the file is ESM, knownBindings.require is undefined because it's only initialized for CommonJS or when mixedModules: true. The code was accessing knownBindings.require.shadowDepth without checking if require exists first.

Blocking variables named require

This came up by @timfish in this comment, we seem to actively block variables named require.

This is marked as "known unknown" but I thought we should make an exception if the variable was created by module.createRequire.

Named import tracking

Using:

import { createRequire } from 'node:module'

wasn't being detected, so I added a detection block to support it.

closes #543

Copilot AI review requested due to automatic review settings December 23, 2025 16:12
@logaretm logaretm requested review from a team, icyJoseph, ijjk and styfle as code owners December 23, 2025 16:12
@logaretm logaretm changed the title tests: added tests to confirm the issue fix: support module.createRequire when mixedModules: false Dec 23, 2025
Copy link

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 PR adds comprehensive test coverage and fixes for edge cases around module.createRequire in ESM contexts, particularly when mixedModules: false. The changes address three key issues: null pointer exceptions when accessing knownBindings.require, incorrect blocking of variables named require that are created via module.createRequire, and missing support for named imports of createRequire.

  • Added null safety checks for knownBindings.require which may be undefined in ESM with mixedModules: false
  • Modified setKnownBinding to allow variables named require if they're created by module.createRequire
  • Added support for detecting createRequire when imported as a named export

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
test/unit/module-create-require-no-mixed/test-opts.json Configures test to run with mixedModules: false
test/unit/module-create-require-no-mixed/output.js Expected output showing lib.node is traced with mixedModules: false
test/unit/module-create-require-no-mixed/lib.node Dummy binary file for testing dependency tracing
test/unit/module-create-require-no-mixed/input.mjs Test case using module.createRequire with namespace import
test/unit/module-create-require-named-require/test-opts.json Configures test to run with mixedModules: false
test/unit/module-create-require-named-require/output.js Expected output showing variable named 'require' from createRequire is properly traced
test/unit/module-create-require-named-require/lib.node Dummy binary file for testing dependency tracing
test/unit/module-create-require-named-require/input.mjs Test case where the createRequire result is named 'require'
test/unit/module-create-require-named-import/test-opts.json Configures test to run with mixedModules: false
test/unit/module-create-require-named-import/output.js Expected output showing named import of createRequire works correctly
test/unit/module-create-require-named-import/lib.node Dummy binary file for testing dependency tracing
test/unit/module-create-require-named-import/input.mjs Test case using named import syntax for createRequire
test/unit/module-create-require-block-other/test-opts.json Configures test to run with mixedModules: false
test/unit/module-create-require-block-other/output.js Expected output showing non-createRequire 'require' is not traced
test/unit/module-create-require-block-other/lib.node Dummy binary file that should not be traced
test/unit/module-create-require-block-other/input.mjs Test case verifying variables named 'require' that aren't from createRequire are blocked
test/unit.test.js Updated test runner to use input.mjs and respect mixedModules setting from test-opts.json
src/analyze.ts Adds CREATE_REQUIRE symbol, null safety checks, exception for BOUND_REQUIRE in setKnownBinding, and named import tracking for createRequire

πŸ’‘ Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/analyze.ts Outdated
Comment on lines 1118 to 1120
if (parent.type === 'VariableDeclarator') {
const requireName = parent.id.name;
setKnownBinding(requireName, { value: BOUND_REQUIRE });
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

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

The code accesses parent.id.name without first verifying that parent.id.type === 'Identifier'. In JavaScript, variable declarators can use destructuring patterns (e.g., const { x } = createRequire(...)), where parent.id would be an ObjectPattern or ArrayPattern instead of an Identifier. This could cause a runtime error or unexpected behavior.

The existing code at line 1047 shows the correct pattern: it checks parent.id.type === 'Identifier' before accessing parent.id.name. The same check should be added here for consistency and safety.

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

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

This seems like copilot found a bug

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh that's a rare one, I should've given those comments a glance.

I fixed it just now and added a couple of tests for it

@logaretm logaretm requested a review from styfle December 24, 2025 17:29
@@ -0,0 +1,6 @@
import * as module from 'node:module';

// Variable named 'require' - might conflict
Copy link
Member

@styfle styfle Jan 1, 2026

Choose a reason for hiding this comment

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

This test doesn't seem to be actually testing anything different than the test above which uses

const req = createRequire(import.meta.url)

I think instead of import.meta.url all the tests should be using a subdirectory instead like

module.createRequire(new URL('/sub', import.meta.url));

That way we know the require() call is on the subdirectory and that we didn't accidentally resolve using the global require().

Does that make sense?

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 test doesn't seem to be actually testing anything different than the test above....

I wanted ensure that it doesn't ignore variables named require if created by module.createRequire, more of a sanity check. I could delete the other test since this can test both behaviors at the same time. What do you think?

I think instead of import.meta.url all the tests should be using a subdirectory instead like

Makes sense, I just pushed that and updated the tests.


// Destructuring pattern - should not crash, but won't be traced
// since we can't bind a single identifier to the createRequire result
const { resolve } = module.createRequire(import.meta.url);
Copy link
Member

@styfle styfle Jan 7, 2026

Choose a reason for hiding this comment

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

In a futre PR, this test should also call resolve() on a dependency to see if that still works

(preferably under the /sub directory)

Copy link
Member

@styfle styfle left a comment

Choose a reason for hiding this comment

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

Great work, thanks!

@styfle styfle changed the title fix: support module.createRequire when mixedModules: false feat: support module.createRequire when mixedModules: false Jan 7, 2026
@styfle styfle enabled auto-merge (squash) January 7, 2026 18:57
@styfle styfle merged commit 67038d5 into vercel:main Jan 7, 2026
14 checks passed
@github-actions
Copy link

github-actions bot commented Jan 7, 2026

πŸŽ‰ This PR is included in version 1.2.0 πŸŽ‰

The release is available on:

Your semantic-release bot πŸ“¦πŸš€

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resolving module.createRequire throws error when mixedModules: false

2 participants