module: fix detect-module not retrying as ESM for code that errors only in CommonJS#52024
Merged
nodejs-github-bot merged 8 commits intonodejs:mainfrom Mar 12, 2024
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #50917, using the solution described in #50917 (comment).
The general algorithm for
--experimental-detect-moduleis to try to parse as CommonJS, and if a syntax error is thrown that corresponds to ESM syntax (importorexport, orimport.meta), try again as ESM. The edge case pointed out by #50917 is when there’s syntax that throws in CommonJS but parses in ESM, and this syntax is above the first ESM syntax (so top-levelawait, or a declaration of a variable with the same name as one of the CommonJS module wrapper variables such asconst require =, on the first line or any line above the firstimportorexport).The tricky thing is that the errors thrown by top-level
awaitorconst requirein CommonJS are the same errors as thrown by typingawaitin any ordinary sync function, or by declaringrequiretwice in user code (e.g.const require = 1; const require = 2). So we only want to retry parsing as ESM when these errors are because theawaitor problematic variable declaration is at the top level, where it throws in CommonJS but parses in ESM.To determine this, this PR creates a new error path where if the CommonJS parse returns a syntax error corresponding to either of these cases, we do a special second CommonJS parse of the code wrapped in an async function. This wrapper function creates a new scope, so
const requireis no longer a problematic redeclaration; andawaitno longer throws because it’s within an async function. This wrapper only affects the top level, soawaitin a sync function farther down or variable redeclarations farther down (or twoconstdeclarations at the top level) will still throw; but the code permitted in ESM parses successfully. If this second parse doesn’t throw any errors, we resume the detection algorithm and try parsing as ESM.@nodejs/loaders @chjj @meyfa @joyeecheung