view-ignored
v0.12.2
Published
Retrieve list of files ignored/included by Git, NPM, Yarn, JSR, Deno, Bun, VSCode extension CLI and other tools.
Maintainers
Readme
Retrieve a list of files ignored or included by Git, NPM, Yarn, JSR, Deno, Bun, VS Code extension CLI, and other tools.
Highlights
- Reader. Get a list of included files by parsing configuration files directly, without wrapping command-line tools.
- Reasoning. Retrieve detailed information explaining why specific files are included or excluded, matching original rule paths and pattern details.
- Fast. Highly optimized for performance with minimal memory overhead.
- Plugins. Built-in targets for popular tools. Create custom targets by implementing the
Targetinterface. - Streaming. Native
scanStreamsupport for processing massive file trees with minimal memory overhead. - Execution Control. Fine-tune traversal depth and skip unnecessary directory checks using the
skipDepthandskipInternaloptions. Supports standardAbortSignalto cancel long-running scans instantly. - Lightweight. Minimal dependencies for fast performance and a small bundle size.
- Browser. Fully compatible with browser environments when bundled.
- Windows. Converts Windows-style paths to Unix format to guarantee compatibility across test frameworks (like
memfs) and browser bundles.
[!NOTE] Despite its name, the library's default behavior is to retrieve included files (i.e., files that are not ignored). If you want ignored files, or both, set the
invertoption:truereturns only ignored files, while2returns all files annotated with their exact ignore status.
v1 Roadmap
- [x] Perfect API. Designed and finalized a clean, type-safe API for scanning and stream consumption.
- [x] Works for common use cases. Production-ready for general project directory walking and status reports.
- [x] Follow
.gitignorespec. Ensure strict alignment with Git's wildmatch algorithm (character classes, brackets, and negative matches), asignoredoes. - [x] Handle Git config. Parse and support Git system/global settings (such as local
.git/configreference rules andcore.excludesfileparsing). - [x] Include node_modules bundled dependencies correctly. Walk subdependency folders under
bundledDependenciesfor accurate package manager packing emulation. - [ ] Ensure compatibility and references. Perfect self-tests and comparisons against real CLI packaging output.
- [ ] *Move targets into separate packages. Decouple individual target modules into scoped sub-packages to reduce core bundle size (optional).
* - Optional.
Why this library exists
This library was created to solve several long-standing issues in the JavaScript ecosystem:
- Inconsistent Ignore Behavior: Tools like VS Code, CLI bundlers, and custom scripts often differ in how they evaluate
.gitignoreand.npmignorepatterns. - Heavy Dependencies: Alternative analysis libraries (such as
npm-packlistorignore-walk) carry deep, complex, and heavy dependency trees. - Lack of Wildmatch Support: Standard JS glob engines do not strictly adhere to Git's native wildmatch algorithm.
- No Explanability: There was no lightweight, high-performance way to query why a particular file was included or excluded with a traceable rule-origin path.
Usage
Basic example
import * as vign from "view-ignored"
// also available:
// "/scan", "/stream"
// "/browser", "/browser/scan", "/browser/stream"
import { makeGit } from "view-ignored/targets"
import { RuleMatchKind } from "view-ignored/patterns"
const ctx = await vign.scan({ target: makeGit() })
ctx.paths.has(".git/HEAD") // false
ctx.paths.has("src") // true
const match = ctx.paths.get("src")!
if (match.kind === RuleMatchKind.external) {
console.log(match.source.path) // ".gitignore"
console.log(match.pattern) // "src/**"
}Using a custom target
You can create custom targets by implementing the Target interface.
This example demonstrates a Docker-like target that caches its compiled glob rules to avoid redundant parsing across runs:
import type { Target } from "view-ignored/targets"
import {
type Extractor,
extractGitignore,
ruleTest,
ruleCompile,
type InternalRules,
type GlobRule,
} from "view-ignored/patterns"
let cachedDockerRule: GlobRule | null = null
export function makeDocker(): Target {
const extractors: Extractor[] = [
{
extract: extractGitignore,
path: ".dockerignore",
},
]
cachedDockerRule ||= ruleCompile({
compiled: null,
excludes: true,
list: [".git/", "node_modules/", ".DS_Store"],
})
const internal: InternalRules = {
before: [cachedDockerRule],
after: [],
}
return {
extractors,
ignores: ruleTest,
internalRules: internal,
root: ".",
}
}Streaming Results
import * as vign from "view-ignored"
// or import * as vign from "view-ignored/stream"
import { makeNPM } from "view-ignored/targets"
const stream = vign.scanStream({ target: makeNPM() })
stream.addEventListener("dirent", console.log)
stream.addEventListener(
"end",
({ detail: ctx }) => {
console.log(ctx.paths.has(".git/HEAD")) // false
console.log(ctx.paths.has("node_modules/")) // false
console.log(ctx.paths.has("package.json")) // true
},
{ once: true },
)
stream.start() // importantBrowser and Custom Filesystem Compatibility
To eliminate dependency on Node.js built-in modules (node:fs and node:process), import from the browser-specific subpaths and provide a custom filesystem adapter:
import * as vign from "view-ignored/browser"
// or "/browser/scan"
import { makeGit } from "view-ignored/targets"
import { readFile, readdir } from "original-fs"
export const cwd = process.cwd()
const customFs = { readFile, readdir }
await vign.scan({ cwd, fs: customFs, target: makeGit() })Watching for Changes
You can use the built-in context patchers to incrementally update the scan results without rescanning the entire directory tree. This is highly efficient for file watching services.
[!IMPORTANT] Directory paths must have a trailing slash.
import { matcherContextAddPath, matcherContextRemovePath } from "view-ignored/patterns"
// Handle "created"
await matcherContextAddPath(ctx, options, "src/new-file.ts")
// Handle "removed"
await matcherContextRemovePath(ctx, options, "src/old-file.ts")
// Handle "changed"
// Best approach: remove and re-add
await matcherContextRemovePath(ctx, options, "src/file.ts")
await matcherContextAddPath(ctx, options, "src/file.ts")Edge Cases and Limitations
- Idempotency: Patcher functions for files are not idempotent. Calling
matcherContextAddPathmultiple times for the same path without removing it first will corrupt thetotalFilesandtotalMatchedFilescounts inctx.total. Always callmatcherContextRemovePathbeforematcherContextAddPathif the path might already exist in the context. - Directories: Directory paths must end with a slash (e.g.,
src/). If you omit the slash, it will be treated as a file, and its contents will not be tracked or updated correctly. - Renames: To handle a file or directory rename, first call
matcherContextRemovePathon the old path, thenmatcherContextAddPathon the new path. - Source Files: If a file that acts as an ignore source (like
.gitignoreorpackage.json) is added or changed, the patcher will automatically rescan the directory containing that source file to update the matching rules and state for all affected files. - Depth: Patchers respect the
depthoption provided in theScanOptions. If you add a path deeper than the specified depth, it might not be fully processed or added toctx.paths.
Targets
We provide optimized, high-performance re-implementations of various CLI/packer matching algorithms in TypeScript. These re-implementations emulate the exact ignore behavior of each target.
Git (our implementation)
- Original Algorithm & CLI Logic: Git uses
dir.cand standard globbing patterns (defined by its nativewildmatchspec) to walk files. It resolves configurations starting from/(the system's root), loading rules sequentially:- Built-in defaults (such as ignoring
.git/metadata itself). - Global configuration rules specified by the
core.excludesfilevariable in~/.gitconfigor system-wide settings. - Local configuration overrides within
.git/info/exclude. - Local
.gitignorefiles parsed on a per-directory basis.
- Built-in defaults (such as ignoring
- How We Emulate It: Our target reads
.gitignoreand.git/info/excludeconfigurations, maps global settings via standard paths (HOME/XDG_CONFIG_HOME), and matches paths with optimized glob rules cached at the module level. - Verification CLI Command:
git ls-files --others --exclude-standard --cached
NPM (our implementation)
- Original Algorithm & CLI Logic: The official
npm packcommand relies onnpm-packlistto list directory contents. It runs a priority-based resolution algorithm:- Strictly Included: Essential package files like
package.json,README,LICENSE,LICENCE,CHANGES, and files referenced in package-level configuration fields. - Inverted Allow-list: If the
filesarray is defined inpackage.json, NPM operates in an inverted matching mode (allowing only matches fromfilesplus mandatory files). - Conditional Exclude: If no
filesfield is defined, it extracts rules from.npmignorefiles, falling back to.gitignorefiles if.npmignoreis absent. - Strictly Excluded: Hardcoded ignores like
node_modules, VCS directories (.git,.hg), lockfiles, and debug logs are always skipped.
- Strictly Included: Essential package files like
- How We Emulate It: Our target parses the root
package.jsonto extractname,version, and entrypoints (main,module,browser,bin), converts target entry paths into exact forced inclusions, and executes priority-ordered cascading glob rules. - Verification CLI Command:
npm pack --dry-run
Bun (our implementation)
- Original Algorithm & CLI Logic: Bun's native Rust implementation of the
bun pm packcommand mimics NPM's packing behavior but with subtle differences. It validates the manifest and processes hardcoded inclusions (package.json, standard documentation, andbinfile paths) and excludes lockfiles (likebun.lockbandbun.lock) and system environments by default. - How We Emulate It: Our target initializes from the root
package.json, extractingbinconfigurations and enforcing the specific hardcoded defaults mapped inside Bun's Rust packer engine. - Verification CLI Command:
bun pm pack --dry-run
Yarn (our implementation)
- Original Algorithm & CLI Logic: Modern Yarn (Berry) uses
@yarnpkg/plugin-packto package workspaces. It applies case-insensitive matching rules to package manifests and standard docs, extracts ignore lists from.npmignoreand fallback.gitignorefiles, and prevents the packaging of its own workspace metadata (such as.yarnrc.yml,.yarn, and output.tgzfiles). - How We Emulate It: Our target maps Berry's exact exclude and include lists, parses entry fields, and case-insensitively extracts fallback rules from local ignore manifests.
- Verification CLI Command: Runs modern Yarn's workspace packaging checks.
Yarn Classic (our implementation)
- Original Algorithm & CLI Logic: Legacy Yarn v1 packs files by evaluating
.yarnignore,.npmignore, and.gitignorecase-insensitively, alongside standard packing exclusions and a standard case-insensitive allow-list for documentation, license templates, and change logs. - How We Emulate It: Our target matches Classic's specific built-in excludes, performs case-insensitive rule extraction across all supported ignore manifests, and guarantees identical rule evaluation.
VSCE (our implementation)
- Original Algorithm & CLI Logic: The
vsce packagetool requires standard workspace fields (name,version, andengines.vscodeinpackage.json). It reads.vscodeignorepatterns, falling back to.gitignorewhen.vscodeignoreis absent. It automatically ignores non-production templates, administrative Markdown directories (.github), linter configuration templates, and testing rigs. - How We Emulate It: Our target validates VS Code engines in the manifest and registers
.vscodeignorefallback rules over VSCE's predefined default blocklist. - Verification CLI Command:
vsce ls
JSR (our implementation)
- Original Algorithm & CLI Logic: JSR's publishing pipeline reads
jsr.jsonorjsr.jsonc. It evaluates the fieldspublish.include/includeandpublish.exclude/exclude. When inclusion rules are present, it acts as an inverted allow-list, while ensuring VCS folders (.git) and local OS configurations are skipped. - How We Emulate It: Our target parses JSR configurations, validates the manifest keys, and dynamically toggles target list matching modes.
Deno (our implementation)
- Original Algorithm & CLI Logic: Deno's publishing system behaves identically to JSR, but searches sequentially for configuration files in the root workspace (
deno.json,deno.jsonc,jsr.json, orjsr.jsonc) to extract publishing metadata. - How We Emulate It: Our target scans for any of the supported manifest formats and compiles matching publish and exclude rules.
CLI
A diagnostic utility to hunt for bugs by comparing view-ignored results against real system CLIs.
vign-diff [command] <target> [flags]diff(default): Compare against system CLI.list,ls: List all files included byview-ignoredwith high-precision timing.-i, --issue: Automatically open a prefilled GitHub issue on discrepancy.-V, --verbose: Show raw report.all: Run diagnostics against every supported tool on your system.
vign-diff git # Compare against git
vign-diff list npm # List files for npm package
vign-diff all -i # Scan all and open issuesSee also
- There are references in our implementations.
- https://jsr.io/@m234/path - Utility to sort, convert and format paths.
- https://github.com/git/git/blob/master/wildmatch.c - The original wildmatch implementation.
- https://npmx.dev/package/ignore-walk - A Node.js module for walking directories while respecting ignore files. Not good when Git's spec needed.
- https://npmx.dev/package/npm-packlist - A Node.js module for listing files to be included in an npm package. Heavy.
- https://npmx.dev/package/ignore - A Node.js module for parsing gitignore files according to spec (wildmatch's pathname mode). Git configs are ignored.
Benchmarks
See benchmarks directory.
License
MIT License. See LICENSE.txt for details.
