-
Notifications
You must be signed in to change notification settings - Fork 5
fix: P2 quick wins — version pinning, provider guard, micromatch glob #199
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,6 @@ | ||
| import { resolve } from "path"; | ||
| import { pathToFileURL } from "url"; | ||
| import micromatch from "micromatch"; | ||
| import type { ScanResult, FlagUsage, CallType } from "../types.js"; | ||
|
|
||
| // ── Types ───────────────────────────────────────────────────────────────────── | ||
|
|
@@ -42,44 +43,10 @@ export interface ValidateOptions { | |
|
|
||
| // ── Bootstrap-file glob matching ────────────────────────────────────────────── | ||
|
|
||
| /** | ||
| * Returns true when `file` matches any of the `patterns`. | ||
| * | ||
| * Supports: | ||
| * exact path "src/provider/setup.ts" | ||
| * * wildcard "src/provider/*.ts" — matches within one directory level | ||
| * ** wildcard "src/bootstrap/**" — matches across directory levels | ||
| * | ||
| * No external dependency — avoids importing micromatch/minimatch directly. | ||
| */ | ||
| export function matchesBootstrapPattern(file: string, patterns: string[]): boolean { | ||
| // Normalise leading "./" | ||
| if (patterns.length === 0) return false; | ||
| const clean = (s: string): string => s.replace(/^\.\//, ""); | ||
| const cleanFile = clean(file); | ||
|
|
||
| return patterns.some((pattern) => { | ||
| const cleanPattern = clean(pattern); | ||
| if (cleanFile === cleanPattern) return true; | ||
|
|
||
| // Convert glob to a regular expression. | ||
| const regexStr = cleanPattern | ||
| // Escape regex-special chars except * and ? | ||
| .replace(/[.+^${}()|[\]\\]/g, "\\$&") | ||
| // Temporarily hide ** before processing single * | ||
| .replace(/\*\*/g, "\x00") | ||
| // Single * → match any sequence that does NOT cross a / | ||
| .replace(/\*/g, "[^/]*") | ||
| // ** → match anything including / | ||
| .replace(/\x00/g, ".*") | ||
| // ? → single non-separator char | ||
| .replace(/\?/g, "[^/]"); | ||
|
|
||
| try { | ||
| return new RegExp(`^${regexStr}$`).test(cleanFile); | ||
| } catch { | ||
| return false; | ||
| } | ||
| }); | ||
| return micromatch.isMatch(clean(file), patterns.map(clean)); | ||
|
Comment on lines
46
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🌐 Web query:
💡 Result: In micromatch, the handling of negation patterns in arrays differs significantly between boolean checks and list filtering due to how they resolve inclusion and exclusion [1][2]. Micromatch follows these established conventions regarding negation: 1. When a pattern is a string, patterns are inclusive by default [1]. 2. When an array of patterns is passed, patterns are exclusive by default [1]. Boolean Matching (isMatch) The isMatch(str, patterns) function returns true if any of the given glob patterns match the specified string [3][4]. When using an array of patterns, the behavior of negations (e.g., ['!pattern']) can be counterintuitive because a positive match from any non-negated pattern will still result in a true value [5]. Effectively, isMatch checks if the string matches any of the "include" patterns; it does not inherently act as a filter that must pass every negation constraint unless specifically structured to do so [5]. Array Filtering (match and filter) Methods like match(list, patterns) or the function returned by filter(patterns) are designed to perform set-based filtering [1][2]. These methods implement exclusive-by-default logic: - They start with a set of candidates and apply the patterns to filter the list [1]. - When an array of patterns includes both positive and negative glob patterns, the negative patterns (e.g., '!*.js') correctly exclude matching items from the final result set [1]. Key Differences - isMatch is designed for testing a single string against a set of rules and returns a boolean [3][4]. Because it is "any-match" based, simply providing negative patterns in an array may not exclude the string if other patterns also match it [5]. - filter/match is designed for processing lists [1][2]. It respects the "exclusive by default" behavior, where negative patterns are effectively used to subtract items from the result set [1]. If you require a boolean check that respects all negation patterns as "must not match," you may need to use a custom wrapper or check the string against match to see if it remains in the result set [5]. Citations:
🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== File under review ==\n'
sed -n '1,140p' src/validator/index.ts | cat -n
printf '\n== Micromatch dependency context ==\n'
for f in package.json package-lock.json pnpm-lock.yaml yarn.lock; do
if [ -f "$f" ]; then
echo "--- $f"
rg -n 'micromatch' "$f" -n -C 2 || true
fi
done
printf '\n== Search for matching path normalization / negation handling ==\n'
rg -n 'replace\(/\\\\/g|isMatch\(|micromatch\(' src test . || trueRepository: flaglint/flaglint-js Length of output: 8195 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== Path normalization in scanner/usage flow ==\n'
rg -n 'replace\(/\\\\/g, "/"\)|usage\.file|filePath|scannedFiles|usages' src -C 2
printf '\n== Scanner fingerprint helper ==\n'
sed -n '1,80p' src/scanner/fingerprint.ts | cat -n
printf '\n== Validation call sites / usage shape ==\n'
sed -n '1,140p' src/types.ts | cat -nRepository: flaglint/flaglint-js Length of output: 50377 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== File source / path shape ==\n'
sed -n '1,220p' src/scanner/source.ts | cat -n 2>/dev/null || true
sed -n '1,220p' src/scanner/index.ts | awk 'NR>=640 && NR<=750 {print NR ":" $0}'
sed -n '1,220p' src/validator/tests/validate.test.ts | awk 'NR>=340 && NR<=390 {print NR ":" $0}'
printf '\n== Micromatch docs in repo ==\n'
rg -n 'bootstrapExclude|matchesBootstrapPattern|isMatch|micromatch' README.md src -C 2Repository: flaglint/flaglint-js Length of output: 12050 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== File source / path shape ==\n'
sed -n '1,220p' src/scanner/source.ts | cat -n 2>/dev/null || true
sed -n '640,750p' src/scanner/index.ts | cat -n
sed -n '340,390p' src/validator/tests/validate.test.ts | cat -n
printf '\n== Micromatch/docs references ==\n'
rg -n 'bootstrapExclude|matchesBootstrapPattern|isMatch|micromatch' README.md src -C 2Repository: flaglint/flaglint-js Length of output: 18080 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== FileSource / path contracts ==\n'
sed -n '1,140p' src/types.ts | cat -n
printf '\n== Source implementations ==\n'
fd -a 'source.ts' src
fd -a 'source.*' src
fd -a '*source*' src
printf '\n== Fast-glob / path normalization usage ==\n'
rg -n 'fast-glob|fg\.|listFiles\(' src -C 2Repository: flaglint/flaglint-js Length of output: 4780 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '\n== local-source implementation ==\n'
sed -n '1,220p' src/scanner/local-source.ts | cat -n
printf '\n== any Windows-path normalization/tests ==\n'
rg -n '\\\\|replace\(/\\\\/g, "/"\)|file: .*\\|scanRoot' src test -C 2Repository: flaglint/flaglint-js Length of output: 13381 Normalize 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| // ── Core validation logic ───────────────────────────────────────────────────── | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.