Skip to content

Inline requires: Fix memoization order and partial rewrites - #2092

Open
robhogan wants to merge 3 commits into
mainfrom
robhogan/inline-requires-followups
Open

robhogan wants to merge 3 commits into
mainfrom
robhogan/inline-requires-followups

Conversation

@robhogan

@robhogan robhogan commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #2087 (unreleased), from @javache's review and a bug found along the way.

  • With memoizeCalls, destructured references are now ordered by tree position, not source position. Nodes added by earlier plugins have no position, so the import-export plugin's exports.a = a was taking the memo assignment:
    _foo.a(); // TypeError: Cannot read properties of undefined (reading 'a')
    exports.a = (_foo || (_foo = require("foo"))).a;
  • No more partial rewrites. References that can't be replaced, eg in typeof foo or export {foo}, are now checked up front with Babel's validate, and the require is left alone. Previously the replaceWith error was swallowed mid-rewrite, which with memoizeCalls left a dangling reference.
  • const {} = require('m') keeps its require, for side effects.
  • The memo var is added to the program body directly - addStmtToBlock couldn't fail for a Program.

Changelog: [Fix] Inline requires: Don't partially inline requires referenced in type annotations or export {...} specifiers

Test plan

New tests, which all fail before this change:

  • Import-export plugin plus inline requires on an exported destructured require, executing the output, with and without memoizeCalls
  • References in Flow type annotations, for both forms
  • export {foo} specifiers, for both forms
  • Empty destructuring patterns

Existing snapshots are unchanged.

With `memoizeCalls`, destructured requires sorted their references by source position to decide which one assigns the memoized module. Nodes inserted by earlier plugins have no position, so an `exports.a = a` added by the import-export plugin could take the assignment while an earlier `a()` read the unassigned module. References are now sorted in program order, as `scope.crawl()` collects them.

Both the identifier and destructured paths swallowed `replaceWith` errors, eg for references in type annotations or `export {...}` specifiers, leaving a mix of inlined and original references. References are now checked up front with Babel's `validate`, and a require with any reference that can't be rewritten is left alone. The memo `var` is added to the program body directly, since `addStmtToBlock` can't fail for a `Program`.

Changelog: [Fix] Inline requires: Don't partially inline requires referenced in type annotations or `export {...}` specifiers
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 23, 2026
@robhogan
robhogan marked this pull request as ready for review October 23, 2026 19:24
@robhogan
robhogan requested a review from javache October 23, 2026 19:24

This branch has not been deployed

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

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant