rspack: .meteorignore negation patterns are dropped, so a sharded test run silently loads zero test files
Summary
The Rspack integration builds its test-file filter from .meteorignore but discards every negation (!) pattern. A .meteorignore that says "ignore all test files, then re-include these specific ones" — the standard way to shard a suite across CI containers — therefore excludes every test file and the run loads nothing.
The damaging part is that this is silent. Mocha reports 0 passing, the process exits 0, and CI goes green. We only noticed because one unsharded job in the same workflow still ran and failed; the two sharded jobs had been reporting success while executing zero tests.
Reproduce
In any Rspack-enabled app with server tests, write a .meteorignore:
*.tests.ts
!/imports/localization/LanguageSections.tests.ts
then run the test command, e.g.
meteor test --once --driver-package meteortesting:mocha --port localhost:3055
Observed:
0 passing (1ms)
exit code 0. Expected: the one re-included file's tests run.
.meteor/local/test/server-eager-tests.mjs shows why — the generated regex keeps the blanket ignore and contains no trace of the negation:
const MeteorIgnoreRegex = /…|(?:^|\/).*\/[^/]*\.tests\.ts/;
{
const ctx = import.meta.webpackContext('…', { recursive: true, regExp: /\.(?:test|spec)s?\.[^.]+$/, … });
await Promise.all(ctx.keys().filter((k) => {
return !MeteorIgnoreRegex.test(k);
}).map(ctx));
}
Cause
createIgnoreGlobConfig deliberately preserves negations, re-attaching the ! prefix:
but createIgnoreRegex then drops them on the floor:
const regexPatterns = globPatterns.map(pattern => {
// Skip negation patterns for the regex
if (pattern.startsWith('!')) {
return null;
}
The result is a single alternation of every positive pattern, consumed as a pure "does this path match anything ignored" test:
https://github.com/meteor/meteor/blob/14dfaa2531f3496a89e5648a94b770d1868e7f07/npm-packages/meteor-rspack/lib/test.js#L50-L53 https://github.com/meteor/meteor/blob/14dfaa2531f3496a89e5648a94b770d1868e7f07/npm-packages/meteor-rspack/lib/test.js#L78-L84
The degenerate all-negations case is handled (lib/ignore.js L133-136 returns a regex matching nothing), so the author clearly knew negations could appear — it is the mixed case, which is the useful one, that misbehaves.
Expected semantics
.meteorignore is documented as using gitignore syntax, where the last matching pattern wins and a later !pattern re-includes a path that an earlier pattern excluded. Meteor's own (non-Rspack) ignore handling honours that, which is why this shape of .meteorignore works on the classic bundler and breaks under Rspack.
Suggested fix
createIgnoreRegex returning a single RegExp cannot express last-match-wins, so the shape needs to change. Two options:
- Return a predicate rather than a regex — compile the patterns in order into
{ regex, negated }pairs and evaluate last-match-wins.lib/test.jswould serialise that predicate into the generated eager-test module instead of a bare regex. - Return a pair of regexes (ignored, re-included) and emit
!ignored.test(k) || reincluded.test(k)in the generated module. Less general — it gets the ordering wrong for alternating patterns — but a much smaller change and enough for the common "ignore a glob, re-include specific files" case.
Happy to put up a PR for either; say which you'd prefer.
Secondary, possibly worth folding in
getMeteorIgnoreEntries reads only the .meteorignore file (https://github.com/meteor/meteor/blob/14dfaa2531f3496a89e5648a94b770d1868e7f07/npm-packages/meteor-rspack/lib/ignore.js#L11-L28), so the METEOR_IGNORE environment variable never reaches the eager-test filter. The Atmosphere rspack package also lists METEOR_IGNORE in RSPACK_UNSET_ENV, so it is cleared for the Rspack child process. An app that shards via METEOR_IGNORE rather than the file gets the opposite failure — no filtering at all, every test file loaded in every container — which is at least loud rather than silent, but is equally not what was asked for.
Versions
- Meteor
[email protected] - Atmosphere
[email protected],@meteorjs/[email protected],@rspack/core/@rspack/cli1.7.11 - macOS 26.6.2 (arm64) and
cimg/base:2025.12on CircleCI — same behaviour on both
Source: meteor/meteor