Skip to content

Keep the cwd out of glob patterns and matchers - #2107

Merged
webpro merged 5 commits into
webpro-nl:mainfrom
kaanisthatyou:fix/glob-chars-in-cwd
Oct 9, 2026
Merged

webpro merged 5 commits into
webpro-nl:mainfrom
kaanisthatyou:fix/glob-chars-in-cwd

Conversation

@kaanisthatyou

@kaanisthatyou kaanisthatyou commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

When the project lives in a directory with glob characters, like ~/Dropbox (Team)/app, absolute patterns repeat the cwd and match nothing: plugin config files aren't loaded, ignore and production exclusions miss, and subpath imports, tsconfig include and import.meta.glob resolve to nothing.

  • Absolute patterns are made relative to the cwd before globbing (prependDirToPatterns, plus _syncGlob and _dirGlob).
  • ignore, ignoreFiles, ignoreIssues, production and configuration hint patterns are matched against cwd-relative paths, like the gitignore matcher already does.
  • The compiler extension glob takes the base of include from the relative pattern (it was cut at the ( and globbed from a parent dir).
  • Nx and moonrepo expand {workspaceRoot}/$workspaceRoot to relative paths: the commands are parsed as shell scripts, and the space and parentheses in the absolute path broke them.

test/infra/glob-characters-in-cwd.test.ts copies 12 fixtures covering these cases into knip (fixture) [x]-… and expects the same issues, counters and hints as in their original location; all 12 fail without the change. From a checkout under Coding(Real)/, the full suite now gives the same results as from a plain directory (42 tests failed only there before).

@webpro webpro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the pull request! Knip should just work from a directory like ~/Dropbox (Team)/app, and this fixes the reported case. Running the test suite from a checkout in such a directory, 45 tests still fail with this change, for example in ignore patterns, production mode (Vitest, node-test-runner), subpath imports, tsconfig include and import.meta.glob. Could you make the existing tests pass from such a directory, and add tests that cover these cases? One more note inline.

Comment thread packages/knip/src/util/glob-core.ts Outdated
Comment on lines +292 to +302
const toCwdRelative = (pattern: string, cwd: string) =>
pattern.startsWith('!') ? `!${toRelative(pattern.slice(1), cwd)}` : toRelative(pattern, cwd);

export async function glob(_patterns: string[], options: GlobOptions): Promise<string[]> {
if (Array.isArray(_patterns) && _patterns.length === 0) return [];

const cachedIgnores = options.gitignore ? cachedGlobIgnores.get(options.dir) : undefined;

const _ignore: string[] = [...GLOBAL_IGNORE_PATTERNS];
const [negatedPatterns, patterns] = partition(_patterns, pattern => pattern.startsWith('!'));
const [negatedPatterns, patterns] = partition(
_patterns.map(pattern => toCwdRelative(pattern, options.cwd)),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tinyglobby relativizes absolute patterns against the escaped cwd, so the cwd does need to come out of the patterns on our side. But this adds another pass over every pattern with its own ! handling. prependDirToPatterns in glob.ts already maps every pattern and checks for absolute ones, so the conversion fits in there, for example:

const prepend = (pattern: string, cwd: string, relativePath: string) => {
  const isNegated = pattern.startsWith('!');
  const id = isNegated ? pattern.slice(1) : pattern;
  const globPattern = isAbsolute(id) ? relative(cwd, id) : join(relativePath, id);
  return isNegated ? `!${globPattern}` : globPattern;
};

With that (and cwd passed in from prependDirToPatterns) instead of the change here, the new test passes, and so does every test that passes with this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in f0e99ff: the conversion now lives in prependDirToPatterns as you suggested, and the glob-core.ts change is gone. _syncGlob and _dirGlob use the same prepend, since tsconfig include, subpath imports and import.meta.glob reach tinyglobby through _syncGlob with absolute patterns.

The cwd may contain glob characters, as in `~/Dropbox (Team)/app`.
Absolute patterns that repeat it match nothing, so make them relative
to the cwd before globbing (in `prependDirToPatterns`, `_syncGlob` and
`_dirGlob`), and match ignore, production and configuration hint
patterns against cwd-relative paths, like the gitignore matcher does.

Nx and moonrepo expand `{workspaceRoot}`/`$workspaceRoot` into commands
that are parsed as shell scripts, so expand them to relative paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kaanisthatyou
kaanisthatyou force-pushed the fix/glob-chars-in-cwd branch from 2304c47 to f0e99ff Compare October 5, 2026 06:52
@kaanisthatyou kaanisthatyou changed the title Match absolute plugin patterns when the cwd has glob characters Keep the cwd out of glob patterns and matchers Oct 5, 2026
@kaanisthatyou

Copy link
Copy Markdown
Contributor Author

Thanks for the review and for running it from such a directory. I reproduced it with a checkout under Coding(Real)/ and compared the full suite against a copy in a plain directory: 42 tests failed only in the first one, and with these changes both give the same results. Besides the cases you listed, the configuration hints, the --use-tsconfig-files compiler extension glob and the Nx/moonrepo {workspaceRoot} expansion had the same problem. The new test/infra/glob-characters-in-cwd.test.ts covers each of them; the PR description has the details.

webpro added 4 commits October 7, 2026 17:04
An empty pattern used to become `.` for the root workspace and match
nothing; now that relative patterns pass through untouched it is dropped,
which left `defaultGlob` dereferencing the first of zero patterns.
@webpro

webpro commented Oct 7, 2026

Copy link
Copy Markdown
Member

I pushed a few commits on top of yours that keep the fix but stop converting the same paths back and forth: the glob layer relativizes the workspace dir once per call and only rewrites absolute patterns, the configuration hints relativize file paths only up to the first match, package.json entry specifiers are no longer made absolute just for the glob, and tsconfig include/exclude stay relative to the tsconfig dir (rebased onto the workspace dir once for the compiler-extension glob). Knip's output on this repo is identical to your commit, the full suite passes here, and the new cases pass from a directory named knip (glob) [x] too.

Could you give it a final spin from your directory with glob characters?

@pkg-pr-new

pkg-pr-new Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/knip@2107
npm i https://pkg.pr.new/@knip/language-server@2107
npm i https://pkg.pr.new/@knip/mcp@2107

commit: a56b827

@kaanisthatyou

Copy link
Copy Markdown
Contributor Author

Ran the full suite at 194458c from C:\Users\ADAS\Desktop\Coding(Real)\oss-work\knip: 1392 of 1404 pass, including every "Same results in a directory with glob characters" case. The 12 failures (9 in e2e/fix-tsc, 2 in fix/format, 1 in infra/glob-symlinks) fail the same way in a copy under a plain path on this Windows machine, so they're local and unrelated to this change. Thanks for the cleanup.

@webpro webpro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for the confirmation! Much appreciated.

@webpro
webpro merged commit 1e82fb8 into webpro-nl:main Oct 9, 2026
32 checks passed
@webpro

webpro commented Oct 9, 2026

Copy link
Copy Markdown
Member

🚀 This pull request is included in v6.41.0. See Release 6.41.0 for release notes.

Using Knip in a commercial project? Please consider becoming a sponsor.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants