fix(parser): inject params into one-line non-async arrow scenarios - #5680
Open
mirao wants to merge 1 commit into
Open
fix(parser): inject params into one-line non-async arrow scenarios#5680mirao wants to merge 1 commit into
mirao wants to merge 1 commit into
Conversation
parse-function@5.6.10 decides whether its input is an ES6 object method with
`/^\*?.+\([\S\W]*\)\s*{/`. The greedy `.+` combined with `[\S\W]*` matches any
source containing a `) {` sequence, so a non-async arrow whose body holds an
`if`, `for`, `while` or `switch` gets wrapped in braces as a fake object method
and acorn throws. getParams() then returns undefined and nothing is injected —
`I`, page objects and `current` are all undefined when the test runs.
Async arrows escape through the library's own isAsyncArrow check, and in plain
JavaScript a multi-line arrow escapes as well, because `.` does not cross a
newline so the greedy `.+` cannot reach past the first line. That second escape
does not exist under TypeScript: tsx/esbuild emit every function on one line, so
fn.toString() returns the one-line form however the source was written, and
every non-async scenario with destructured params and a conditional fails.
normalizeArrowFn() asks acorn whether the source really is an
ArrowFunctionExpression and, if so, hands parse-function `async <source>` so it
takes the isAsyncArrow branch. Anything acorn does not confirm as an arrow —
class methods, generators, function expressions, strings, unparseable input — is
returned untouched, so only input that fails today is affected. The prefix cannot
change a parameter list, and default values stay correct because their offsets
are sliced from the same prefixed string.
ecmaVersion becomes a shared const so the parse and the arrow check cannot drift
apart; its value is unchanged, so no syntax gains or loses parseability.
Fixes codeceptjs#5679
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 #5679
Problem
Scenario('...', ({ I }) => { if (true) { I.say('hello') } })injects nothing —I, page objects andcurrentare allundefined, and the test dies withTypeError: Cannot read properties of undefined.parse-function@5.6.10decides whether its input is an ES6 object method with:The greedy
.+combined with[\S\W]*matches any source containing a) {sequence, so a non-async arrow whose body holds anif,for,whileorswitchis wrapped in braces as a fake object method and acorn throws.getParams()logs theSyntaxErrorand returnsundefined.Async arrows escape via the library's own
isAsyncArrowcheck. In plain JavaScript a multi-line arrow escapes too, because.does not cross a newline. That second escape does not exist under TypeScript —tsx/esbuildemit every function on one line, sofn.toString()returns the one-line form however the source was written. In a TypeScript suite every non-async scenario with destructured params and a conditional fails, which is why this surfaced as a 4.0 regression.Fix
normalizeArrowFn()asks acorn whether the source really is anArrowFunctionExpressionand, if so, handsparse-functionasync <source>so it takes theisAsyncArrowbranch instead of the method-wrapping one.Anything acorn does not confirm as an arrow — class methods, generators, function expressions, strings, unparseable input — is returned untouched, so only input that already fails is affected. The prefix cannot change a parameter list, and default values stay correct because their offsets are sliced from the same prefixed string.
ecmaVersionbecomes a shared const so the parse and the arrow check cannot drift apart. Its value is unchanged, so no syntax gains or loses parseability.Verification
lib/parser.jsrevertedgetParamsandgetParamsToStringnpm run test:unitnpm run test:runnerData().Scenario(({ current }) => ...)The differential corpus covered class methods, async/static methods, getters, generators and async generators, function expressions, expression-body arrows, rest params, default values (numeric, string, call expressions), nested arrows, strings containing
=>, sinon proxies, string inputs and garbage input.Not included
A one-line arrow whose body uses ES2021+ syntax (
??=,||=, numeric separators, class fields) still gets no params, becauseecmaVersion: 11pins the parser to ES2020. That is a separate, pre-existing defect with the same symptom — unrelated to theisMethodmisdetection and unchanged by this PR. Raising it to'latest'fixes it and was green on both suites, but it is deliberately left out to keep this PR to the reported bug. Happy to add it here or in a follow-up if maintainers prefer.🤖 Generated with Claude Code