Compatibility
Published
Every publish step is an ignore list. Something walks a directory, matches each path against a set of patterns, and decides what goes into the artifact. The patterns are strings, which is why this looks safe. The switches beside them, case sensitivity, whether a bare name matches at any depth, whether braces expand, whether dotfiles are matched, are properties on an options object that the matcher reads. Half a matching rule is still a matching rule; it just matches different files.
What was measured
One file against minimatch 3.1.5, the real installed library, driven through five protection profiles for the base column and both member-renaming profiles for the rest. It builds a small tree with the files every repository has, an entry point, an environment file, a registry configuration file, a readme, application source, a capitalised test file, a log and a private key, then filters it through the ignore list a packager would write and prints what shipped.
The base column is clean on all five profiles: the same three files shipped, the same five excluded, the same manifest counts. Protecting a build tool does not change what it packages.
Everything below is the member-renaming column. The patterns themselves never move, because they are string literals. Only the option names move, and the library sees an options object with keys it does not recognise, which is the same as no options at all.
A test file that walks into the artifact
The case-insensitivity switch produced the result worth publishing. The ignore list contains a pattern for test files written in lower case, and the tree contains a file capitalised the way a component test usually is. With the switch applied, that file is excluded. With the switch renamed, it ships: the shipped list gained an entry, the count went from three to four, and the manifest the packager prints reported four files and four exclusions rather than three and five.
Nothing about that looks like a failure. There is no error, the publish succeeds, and the artifact is well formed. It simply contains a file that the ignore list, as written, excludes.
The line that makes it worse is the audit line at the end. The packager prints a leak check over the shipped list, and in the same run it still reported nothing found, because the shipped file is a test file rather than a dotfile or a key. So the artifact grew a file the rules exclude, and the check that exists to notice extra files agreed that nothing was wrong. That combination, a rule that half-applies and an audit narrower than the rule, is how files reach registries.
Switches that invert rather than disappear
Two more switches were measured in isolation so the direction is unambiguous. The base-name switch, which lets a bare pattern match a file at any depth, went from matching to not matching, so an ignore rule written as a bare filename silently stops covering nested copies of that file. The brace-expansion switch went the other way: it had been disabled deliberately, and renaming it re-enabled expansion, so a pattern containing braces started matching a set of names rather than the literal one.
Both are ordinary object properties and neither reports anything. The pattern is unchanged in the source, the library is unchanged, and the set of matched files is different.
One arm was genuinely inert and is worth recording honestly rather than dropping. The dotfile switch measured identical, and the reason is a documented behaviour rather than an accident: minimatch matches a dotfile when the pattern itself begins with a dot, regardless of the option. The switch matters only for star-shaped rules, which is what most projects actually write, so the inert result here is narrower than it looks.
The loud arm is a name collision
The pattern that matched the library's helper names threw immediately, with a TypeError naming a generated identifier on an array. The reason is the one that keeps recurring in this series: an option name is also a method name, and a member pattern matches names, not roles. Matching a helper called filter also matches the array method of the same name three lines away.
That is the good failure mode, and it is worth wanting. It fires in development, on the first run, before anything is published. The silent arms above are the ones that reach a registry.
The control arm renamed the manifest keys the packager prints for itself. Behaviour was unaffected and the printed manifest became generated identifiers, which is the same lesson as elsewhere: the record of what a build did is not protected from the build.
Why this one deserves attention beyond correctness
Most compatibility results in this series cost you a feature. This one costs you the contents of a release, and releases are not revocable in the way a deploy is. A file published to a registry is fetched, cached and mirrored within minutes.
The material actually at risk is standard: environment files, registry credentials, private keys, internal fixtures, test files that name internal hosts, and the debug logs a CI run leaves behind. An ignore list is usually the only control keeping those out of a tarball, because everything else in the pipeline treats the working directory as trusted.
It is also worth saying plainly that this has nothing to do with whether the shipped code is protected. Protection is applied to the JavaScript in the artifact. What is in the artifact is decided before that, by matching rules that a member pattern can reach if the tooling itself is protected.
What to do about it
The first mitigation is scope. Build tooling rarely needs member renaming at all: the value of protecting a packager is low, and the surface, as measured, is the set of decisions it makes about your files. Protect the product and leave the tooling alone, or protect it with identifier renaming only.
If the tooling is protected, keep matcher vocabulary out of the pattern: dot, nocase, matchBase, nobrace, noglobstar, noext, nonegate, ignore, cwd, absolute, followSymbolicLinks, and the helper names filter and match. Anchoring the pattern to your own naming convention covers all of them.
Then check the artifact rather than the rules. Pack it, list the contents, and assert the list against an expected set in CI. That check is independent of every option dictionary in the pipeline, it is the only thing in this measurement that catches the case-sensitivity arm, and it is roughly four lines of script.
Frequently asked questions
Does obfuscation break glob and ignore patterns?
Not in the default configuration. A packager filtering a file tree through an ignore list with the real minimatch library produced identical results on all five protection profiles measured. It becomes a surface only when member renaming is applied to the build tooling and the pattern reaches the option names the matcher reads.
Why do the patterns still work when the options do not?
Because patterns are string literals and strings are never a rename site. The switches that qualify a pattern are properties on an options object, so the matcher receives keys it does not recognise and behaves as though no options were passed at all.
What was the measured consequence of renaming the case-insensitivity switch?
A capitalised test file that the ignore list excludes was included in the artifact. The shipped count went from three files to four, the packager's manifest reported the new totals as though they were correct, and the leak audit still reported nothing because it looks for dotfiles and keys rather than test files.
Can renaming turn an option on rather than off?
Yes. A brace-expansion switch that had been deliberately disabled was re-enabled, so a pattern containing braces began matching a set of names rather than the literal one. Restrictive switches fail open and permissive switches fail closed, exactly as with validation options.
Did any glob option measure as unaffected?
One did, for a documented reason rather than by accident: the dotfile switch, because minimatch matches a dotfile when the pattern itself starts with a dot regardless of the option. That makes the inert result narrow, since most projects write star-shaped ignore rules where the switch does matter.
Which glob option names should stay out of a rename pattern?
dot, nocase, matchBase, nobrace, noglobstar, noext, nonegate, ignore, cwd, absolute and followSymbolicLinks, plus helper names such as filter and match, which also collide with array methods and produced the one thrown error in this area.
What is the cheapest check for a protected build pipeline?
Pack the artifact and assert its file list against an expected set in CI. It is independent of every option dictionary in the pipeline and it was the only check in this measurement that would have caught a file walking into the release.
Related reading