Skip to content

fix: replace +(|x) patterns with {,x} for picomatch 2.3.2 compatibility#481

Merged
ezolenko merged 4 commits into
ezolenko:masterfrom
koizuka:fix/picomatch-2.3.2-include-pattern
Mar 29, 2026
Merged

fix: replace +(|x) patterns with {,x} for picomatch 2.3.2 compatibility#481
ezolenko merged 4 commits into
ezolenko:masterfrom
koizuka:fix/picomatch-2.3.2-include-pattern

Conversation

@koizuka

@koizuka koizuka commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #480

picomatch 2.3.2 is a security release (CVE-2026-33671, CVE-2026-33672) that changed extglob behavior as part of its fix (see the 2.3.2 commit): +(|x) no longer matches an empty string. This causes the default include pattern *.ts+(|x) to fail to match plain .ts files, breaking TypeScript compilation for projects that have picomatch 2.3.2 installed.

Root cause

The pattern *.ts+(|x) uses an extglob +(...) which means "one or more of the contained alternatives". In picomatch 2.3.1, +(|x) matched an empty string (the | before x means "empty string or x"). In picomatch 2.3.2, this behavior was corrected — +(...) no longer matches zero occurrences, so +(|x) with an empty alternative no longer matches an empty string. As a result, *.ts+(|x) only matches *.tsx, not *.ts.

const pm = require('picomatch');
// picomatch 2.3.2
pm('**/*.ts+(|x)')('src/index.ts') // false  ← broken
pm('**/*.ts{,x}')('src/index.ts')  // true   ← works

Fix

Replace +(|x) with {,x} (brace expansion), which correctly matches both *.ts and *.tsx across picomatch 2.3.1, 2.3.2, and v4.x.

Changed patterns:

  • *.ts+(|x)*.ts{,x}
  • **/*.ts+(|x)**/*.ts{,x}

Files changed

  • src/index.ts: Fix default include option
  • __tests__/fixtures/options.ts: Fix test fixture
  • __tests__/get-options-overrides.spec.ts: Fix test case
  • README.md: Update documented default patterns to match
  • package-lock.json: Update picomatch to 2.3.2 so CI self-builds validate the fix against the affected version

Testing

Unit tests: All 44 tests pass across 11 suites, verified locally with picomatch 2.3.2.

End-to-end verification using the reproduction repo (https://github.com/koizuka/rpt2-picomatch-repro):

git clone https://github.com/koizuka/rpt2-picomatch-repro
cd rpt2-picomatch-repro
# Confirm the failure with released rpt2 0.36.0 + picomatch 2.3.2
npm install
npm run build
# => RollupError: Expected '{', got ':' (Note that you need plugins to import files that are not JavaScript)

# Install the patched version and confirm it succeeds
npm install --save-dev /path/to/patched/rollup-plugin-typescript2
npm run build
# => created dist/index.js in Xms  ✓

Note: I used Claude (an AI assistant by Anthropic) to help with drafting and verification. The diagnosis and fix have been verified manually against the test suite and the reproduction repository.

🤖 Generated with Claude Code

…omatch 2.3.2 compatibility

picomatch 2.3.2 changed extglob behavior so that +(|x) no longer matches
an empty string, causing *.ts+(|x) to fail to match .ts files (without x).

Replace +(|x) with {,x} (brace expansion), which correctly matches both
*.ts and *.tsx in picomatch 2.3.1, 2.3.2, and v4.x.

Fixes ezolenko#480

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@agilgur5 agilgur5 changed the title fix: replace extglob +(|x) patterns with brace expansion {,x} for picomatch 2.3.2 compatibility fix: replace +(|x) patterns with {,x} for picomatch 2.3.2 compatibility Mar 26, 2026
@agilgur5 agilgur5 added kind: bug Something isn't working properly scope: dependencies Issues or PRs about updating a dependency scope: upstream Issue in upstream dependency labels Mar 26, 2026
@agilgur5

Copy link
Copy Markdown
Collaborator

The host.spec.ts failure is a pre-existing Windows symlink permission issue unrelated to this change.

What did you mean by this? You didn't provide any sort of failure log or error.

CI passes fine on Windows.
Is there an error on newer Node?
Or is that a hallucination?

@koizuka

koizuka commented Mar 27, 2026

Copy link
Copy Markdown
Contributor Author

Sorry for the confusion — that was not a hallucination, but my wording was misleading.

When I ran npm test locally on Windows, host.spec.ts failed with:

EPERM: operation not permitted, symlink '...\file.ts' -> '...\link.ts'

This turned out to be because my Windows environment did not have Developer Mode enabled, which is required for non-elevated symlink creation. After enabling Developer Mode, all 44 tests pass (11 suites, 0 failures).

I should not have described it as a "pre-existing issue" — it was purely a local environment configuration issue on my end. Apologies for the confusion.

@agilgur5

Copy link
Copy Markdown
Collaborator

When I ran npm test locally on Windows, host.spec.ts failed with:

EPERM: operation not permitted, symlink '...\file.ts' -> '...\link.ts'

For reference, that would be from this line

This turned out to be because my Windows environment did not have Developer Mode enabled, which is required for non-elevated symlink creation

Ah, I guess that might be because Windows doesn't support POSIX symlinks natively? I'm actually not entirely sure what Node uses under-the-hood for Windows envs.
We need to test for linking since some users and package managers may use them, as per #332

@agilgur5 agilgur5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code LGTM. There is one more spot that needs updating though, the documentation for include's defaults

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@agilgur5 agilgur5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We should also update picomatch in rpt2's own package-lock.json so that self-builds use the newer version. In #480 (comment), I confirmed it breaks self-builds as well; so if it's updated in the package-lock.json, CI self-builds should continue to pass in this PR with the fix

Ensures self-builds use picomatch 2.3.2 so CI validates the fix
against the affected version.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@agilgur5 agilgur5 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, thanks again for the changes!

@ezolenko do you want to take a look? Will need you to build and publish a new build

@koizuka

koizuka commented Mar 28, 2026

Copy link
Copy Markdown
Contributor Author

The macOS Node 20 CI job is failing with a build error. Looking at the log, it seems npm run build fails because the rpt2 v0.35.0 in node_modules can't handle src/index.ts with picomatch 2.3.2 (since that version has the unfixed patterns).

I'm not sure how the CI build chain is meant to work in this case — could you advise on what would be the right approach here?

@agilgur5

agilgur5 commented Mar 28, 2026

Copy link
Copy Markdown
Collaborator

Yep just saw that failed build.
My memory's a little hazy, but I think in the lockfile you can leave the transitive picomatch of 0.35.0 with the old version, while only bumping the more direct one to the newer version for 0.36.1. In other words, only change one of the picomatch entries in the lockfile, not both. I believe that's what I did locally

… picomatch 2.3.2

The build bootstrap uses the installed rpt2 (0.35.x) from node_modules,
which has the unfixed +(|x) patterns. By passing explicit include patterns
to the rpt2 call in rollup.config.js, the build succeeds even when
picomatch 2.3.2 is installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@koizuka

koizuka commented Mar 28, 2026

Copy link
Copy Markdown
Contributor Author

The root cause of the CI failure was a bootstrap problem: npm run build uses the installed rpt2 (0.35.0) from node_modules via rollup.config.js, which has the unfixed +(|x) patterns and fails with picomatch 2.3.2.

To work around this, I added an explicit include option to the rpt2 call in rollup.config.js, so that the build succeeds even with the old rpt2 installed. This unblocks the CI bootstrap.

Does this approach look okay?

@ezolenko

Copy link
Copy Markdown
Owner

LGTM, thanks again for the changes!

@ezolenko do you want to take a look? Will need you to build and publish a new build

I can't look at it right now, will check it in a couple of weeks, will be offline until then

@agilgur5

Copy link
Copy Markdown
Collaborator

Oh, inopportune timing. There's at least 3 people who've hit the bug already, so, if possible, a hotfix would be good to push.

I'm fine approving and merging it, but I don't have NPM access to publish. If you want to give me access, I can do it myself.

Otherwise, it can wait, we're FOSS volunteers after all.

@ezolenko ezolenko merged commit e158505 into ezolenko:master Mar 29, 2026
6 checks passed
@ezolenko

Copy link
Copy Markdown
Owner

Ok, published in 0.37.0, @agilgur5 could you do the release on github?

@koizuka koizuka deleted the fix/picomatch-2.3.2-include-pattern branch March 29, 2026 12:27
@agilgur5

agilgur5 commented Mar 30, 2026

Copy link
Copy Markdown
Collaborator

0.37.0 has been released!

Thanks for the assistance @ezolenko !

I think that was technically my first time pushing to master1 and creating the release 😅 (although I've edited the notes etc many times before)

Footnotes

  1. as you hadn't pushed the new build, although I double-checked the NPM build to make sure it was updated

@agilgur5

agilgur5 commented Mar 30, 2026

Copy link
Copy Markdown
Collaborator

To work around this, I added an explicit include option to the rpt2 call in rollup.config.js, so that the build succeeds even with the old rpt2 installed. This unblocks the CI bootstrap.

Does this approach look okay?

My suggestion was to instead have two different picomatch versions in the lockfile/node_modules tree, such that the old, internal rpt2 used the older picomatch.

But nbd, I've removed the temporary workaround in #483. Thanks again!

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

Labels

kind: bug Something isn't working properly scope: dependencies Issues or PRs about updating a dependency scope: upstream Issue in upstream dependency

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default .ts include patterns broken with picomatch 2.3.2 (extglob behavior change)

3 participants