Skip to content

maybe()/optionally() incorrectly handles named capture #549

Open
@cdwmhcc

Description

@cdwmhcc

🐛 The bug

When maybe() contains named capture groups (created via .as()), magic-regexp fails to correctly wrap the entire content of maybe() as a non-capturing group and make it optional. Instead, it incorrectly makes each component within maybe() individually optional.

This leads to incorrect regex generation and inconsistent matching behavior:

const pattern1 = exactly(
  anyOf('beta', 'dev'),
  maybe(
    charIn('-_.').optionally(),
    oneOrMore(digit),
  ),
)

const pattern2 = exactly(
  anyOf('beta', 'dev'),
  maybe(
    charIn('-_.').optionally(),
    oneOrMore(digit).as('number'),
  ),
)

const testText = '-Beta.zip'

const TEST_RE1 = createRegExp(pattern1, ['g', 'i'])
const TEST_RE2 = createRegExp(pattern2, ['g', 'i'])

console.log(TEST_RE1, testText.match(TEST_RE1)) // -> /(?:beta|dev)(?:(?:[\-_.])?\d+)?/gi [ 'Beta' ] ✅
console.log(TEST_RE2, testText.match(TEST_RE2)) // -> /(?:beta|dev)(?:[\-_.])?(?<number>\d+)?/gi [ 'Beta.' ] ❌

🛠️ To reproduce

https://stackblitz.com/edit/github-qhumtj2p?file=index.mjs

🌈 Expected behaviour

maybe() should wrap all its contents as a whole into a non-capturing group and then make the entire group optional, regardless of whether it contains named capture groups internally.

pattern2 should generate:

// correct
/(?:beta|dev)(?:(?:[\-_.])?(?<number>\d+))?/gi
// Instead of:
/(?:beta|dev)(?:[\-_.])?(?<number>\d+)?/gi

ℹ️ Additional context

The root cause of this issue is that the implementation logic of maybe() doesn't correctly apply the "make the whole thing optional" semantics when handling named capture groups. When it contains named capture groups created by .as(), it makes each internal component optional separately, instead of making the whole unit optional.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinggood first issueGood for newcomers

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions