@any-nav/scan 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +39 -12
- package/dist/budget.d.ts +26 -0
- package/dist/budget.js +25 -0
- package/dist/budget.js.map +1 -0
- package/dist/description.d.ts +24 -13
- package/dist/description.js +46 -27
- package/dist/description.js.map +1 -1
- package/dist/engine-mapping.d.ts +48 -0
- package/dist/engine-mapping.js +127 -0
- package/dist/engine-mapping.js.map +1 -0
- package/dist/hydration.d.ts +49 -0
- package/dist/hydration.js +181 -0
- package/dist/hydration.js.map +1 -0
- package/dist/index.d.ts +17 -6
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/scan.d.ts +6 -42
- package/dist/scan.js +39 -488
- package/dist/scan.js.map +1 -1
- package/dist/tree-replay.d.ts +61 -0
- package/dist/tree-replay.js +256 -0
- package/dist/tree-replay.js.map +1 -0
- package/dist/types.d.ts +18 -6
- package/package.json +11 -3
- package/dist/gitignore.d.ts +0 -9
- package/dist/gitignore.js +0 -128
- package/dist/gitignore.js.map +0 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree replay — the Markdown layer over the engine's in-scope set (file
|
|
3
|
+
* split 2026-10-24), in the pre-facade walk's exact order and shape: the
|
|
4
|
+
* md-only post-filter with the case-insensitive excludeFiles check and the
|
|
5
|
+
* regular-file gate, the name-sorted (localeCompare) DFS with
|
|
6
|
+
* empty-directory pruning, the budget's fact-safe cut and its floor-count
|
|
7
|
+
* chain, NavNode/markdown-locator assembly, and the folder description
|
|
8
|
+
* chains (carrier read at entry, README fold after the settle). The engine
|
|
9
|
+
* bridge lives in engine-mapping.ts, the budget state and diagnostics in
|
|
10
|
+
* budget.ts, the read pipeline in hydration.ts; the two entry points that
|
|
11
|
+
* assemble their outputs from this walk live in scan.ts.
|
|
12
|
+
*/
|
|
13
|
+
import { lstat, readFile } from 'node:fs/promises';
|
|
14
|
+
import { basename, extname, join } from 'node:path';
|
|
15
|
+
import { extractManifestDescription, extractPackageJsonDescription } from './description.js';
|
|
16
|
+
import { countTruncatedDiagnostic } from './budget.js';
|
|
17
|
+
import { resolveScanOptions, scopeLevels } from './engine-mapping.js';
|
|
18
|
+
import { BoundedReads } from './hydration.js';
|
|
19
|
+
const MARKDOWN_EXT = '.md';
|
|
20
|
+
export function isExcludedFileName(name, excludeFiles) {
|
|
21
|
+
if (excludeFiles.length === 0)
|
|
22
|
+
return false;
|
|
23
|
+
const lower = name.toLowerCase();
|
|
24
|
+
return excludeFiles.some((excluded) => excluded.toLowerCase() === lower);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Directory description carriers: standardized files a folder
|
|
28
|
+
* may already carry, in descending order of standardization — the npm
|
|
29
|
+
* `package.json` description, then the manifest-authoring `manifest.yml`
|
|
30
|
+
* `metadata.description` gloss. Priority order, per stage: present with a
|
|
31
|
+
* usable value wins; present but empty or key-less falls through to the
|
|
32
|
+
* next stage (never fabricates); unreadable or invalid fires a loud
|
|
33
|
+
* maintainer diagnostic and falls through the same way. The README chain
|
|
34
|
+
* remains the tail after the last carrier. The files are never Markdown so
|
|
35
|
+
* they never enter the tree, and they never consume the maxFiles budget.
|
|
36
|
+
*/
|
|
37
|
+
export const DIR_DESCRIPTION_CARRIERS = ['package.json', 'manifest.yml'];
|
|
38
|
+
export function isDirDescriptionCarrier(name) {
|
|
39
|
+
const lower = name.toLowerCase();
|
|
40
|
+
return DIR_DESCRIPTION_CARRIERS.some((carrier) => carrier === lower);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read the directory's carrier description from the engine's in-scope set,
|
|
44
|
+
* carrier by carrier in DIR_DESCRIPTION_CARRIERS order (case-insensitive on
|
|
45
|
+
* the on-disk name) — scope membership IS the gitignore verdict the engine
|
|
46
|
+
* computed with its accumulated rules (a gitignored carrier falls through),
|
|
47
|
+
* and respectGitignore: false leaves the carrier in scope the same way.
|
|
48
|
+
* Returns undefined when no carrier yields a description — the README chain
|
|
49
|
+
* then fills in.
|
|
50
|
+
*/
|
|
51
|
+
async function carrierDescription(relDir, level, ctx) {
|
|
52
|
+
for (const carrierName of DIR_DESCRIPTION_CARRIERS) {
|
|
53
|
+
const name = [...level.files].find((candidate) => candidate.toLowerCase() === carrierName);
|
|
54
|
+
if (name === undefined)
|
|
55
|
+
continue;
|
|
56
|
+
const rel = relDir === '' ? name : `${relDir}/${name}`;
|
|
57
|
+
let content;
|
|
58
|
+
try {
|
|
59
|
+
content = await readFile(join(ctx.root, rel), 'utf8');
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
ctx.events.push({
|
|
63
|
+
kind: 'diag',
|
|
64
|
+
diagnostic: {
|
|
65
|
+
code: 'description-carrier-unreadable',
|
|
66
|
+
severity: 'warning',
|
|
67
|
+
message: `Unable to read directory description carrier ${rel}.`,
|
|
68
|
+
path: rel,
|
|
69
|
+
audience: 'maintainer',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const read = carrierName === 'package.json'
|
|
75
|
+
? extractPackageJsonDescription(content)
|
|
76
|
+
: extractManifestDescription(content);
|
|
77
|
+
if ('error' in read) {
|
|
78
|
+
ctx.events.push({
|
|
79
|
+
kind: 'diag',
|
|
80
|
+
diagnostic: {
|
|
81
|
+
code: 'description-carrier-unreadable',
|
|
82
|
+
severity: 'warning',
|
|
83
|
+
message: `Directory description carrier ${rel}: ${read.error}.`,
|
|
84
|
+
path: rel,
|
|
85
|
+
audience: 'maintainer',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (read.description !== null && read.description.trim() !== '')
|
|
91
|
+
return read.description;
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
export async function performWalk(root, options, eager) {
|
|
96
|
+
const opts = options ?? resolveScanOptions();
|
|
97
|
+
const budget = { files: 0, limitHit: opts.maxFiles <= 0 };
|
|
98
|
+
const ctx = {
|
|
99
|
+
root,
|
|
100
|
+
opts,
|
|
101
|
+
events: [],
|
|
102
|
+
files: new Map(),
|
|
103
|
+
carrierDescriptions: new Map(),
|
|
104
|
+
reads: new BoundedReads(),
|
|
105
|
+
eager,
|
|
106
|
+
countIncompletePaths: [],
|
|
107
|
+
};
|
|
108
|
+
const level = await scopeLevels(root, opts);
|
|
109
|
+
const excludeDirNames = new Set(opts.excludeDirs);
|
|
110
|
+
const rootOutcome = await scanLevel('', level, ctx, budget, excludeDirNames);
|
|
111
|
+
const tree = rootNode(root);
|
|
112
|
+
if (rootOutcome.children.length > 0) {
|
|
113
|
+
tree.children = rootOutcome.children;
|
|
114
|
+
const markdown = readmeChildPath(tree);
|
|
115
|
+
if (markdown !== undefined)
|
|
116
|
+
tree.markdown = markdown;
|
|
117
|
+
}
|
|
118
|
+
ctx.carrierDescriptions.set(tree, rootOutcome.carrierDescription);
|
|
119
|
+
return { ctx, tree, itemCount: budget.files, events: ctx.events, limitHit: budget.limitHit };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Replay one directory level in the pre-facade walk's exact order and shape:
|
|
123
|
+
* entries name-sorted (localeCompare, directories and files together), the
|
|
124
|
+
* budget checked at every entry start (an entry past the cut is never
|
|
125
|
+
* examined), directories descending recursively and pruning when nothing
|
|
126
|
+
* Markdown survives beneath, the fact-safe-cut chain recorded on the way
|
|
127
|
+
* out. Markdown files additionally pass the case-insensitive excludeFiles
|
|
128
|
+
* check and a regular-file gate: symlinks never entered the old walk (the
|
|
129
|
+
* readdir dirent types said so) while the unresolved link IS an in-scope
|
|
130
|
+
* entry for the engine, so the lstat gate restores the old shape. Eager
|
|
131
|
+
* walks kick reads at walk positions; structure walks record files for
|
|
132
|
+
* describe().
|
|
133
|
+
*/
|
|
134
|
+
async function scanLevel(relDir, level, ctx, budget, excludeDirNames) {
|
|
135
|
+
const { opts } = ctx;
|
|
136
|
+
const carrier = await carrierDescription(relDir, level, ctx);
|
|
137
|
+
const children = [];
|
|
138
|
+
const names = [...level.dirs.keys(), ...level.files].sort((a, b) => a.localeCompare(b));
|
|
139
|
+
for (const name of names) {
|
|
140
|
+
if (budget.files >= opts.maxFiles) {
|
|
141
|
+
budget.limitHit = true;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const rel = relDir === '' ? name : `${relDir}/${name}`;
|
|
145
|
+
const dirLevel = level.dirs.get(name);
|
|
146
|
+
if (dirLevel !== undefined) {
|
|
147
|
+
// Literal-name floor semantics: the old engine compared directory names
|
|
148
|
+
// by equality. Plain names already rode the engine's policy channel;
|
|
149
|
+
// this check also carries any non-plain entry verbatim.
|
|
150
|
+
if (excludeDirNames.has(name))
|
|
151
|
+
continue;
|
|
152
|
+
const outcome = await scanLevel(rel, dirLevel, ctx, budget, excludeDirNames);
|
|
153
|
+
if (outcome.children.length === 0)
|
|
154
|
+
continue; // empty-directory pruning (root exempt)
|
|
155
|
+
const node = { name, path: rel, kind: 'dir', children: outcome.children };
|
|
156
|
+
// Enumeration-known locator: set when the README child is in
|
|
157
|
+
// the tree — a budget cut past the README or an exclusion leaves it
|
|
158
|
+
// absent (the field mirrors the tree, never the filesystem).
|
|
159
|
+
const markdown = readmeChildPath(node);
|
|
160
|
+
if (markdown !== undefined)
|
|
161
|
+
node.markdown = markdown;
|
|
162
|
+
ctx.carrierDescriptions.set(node, outcome.carrierDescription);
|
|
163
|
+
// Fact-safe cut: the budget flipped inside this folder's
|
|
164
|
+
// span, so it survives with partial children — as does every surviving
|
|
165
|
+
// ancestor, which marks itself the same way at its own loop's end (the
|
|
166
|
+
// chain of folders whose counts are floors). A partial count must never
|
|
167
|
+
// present as the total: the path joins the floor-count set and the
|
|
168
|
+
// per-row diagnostic fires (consumer audience). Folders never entered
|
|
169
|
+
// after the flip vanish instead — the global file-limit-reached covers
|
|
170
|
+
// those, unchanged.
|
|
171
|
+
if (budget.limitHit) {
|
|
172
|
+
ctx.countIncompletePaths.push(rel);
|
|
173
|
+
ctx.events.push({ kind: 'diag', diagnostic: countTruncatedDiagnostic(rel) });
|
|
174
|
+
}
|
|
175
|
+
children.push(node);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (extname(name).toLowerCase() !== MARKDOWN_EXT)
|
|
179
|
+
continue;
|
|
180
|
+
if (isExcludedFileName(name, opts.excludeFiles))
|
|
181
|
+
continue;
|
|
182
|
+
const abs = join(ctx.root, rel);
|
|
183
|
+
let regular = true;
|
|
184
|
+
try {
|
|
185
|
+
regular = (await lstat(abs)).isFile();
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Vanished between the engine walk and the replay: keep the node — the
|
|
189
|
+
// read settles through the unreadable-file path, as the old walk did.
|
|
190
|
+
}
|
|
191
|
+
if (!regular)
|
|
192
|
+
continue;
|
|
193
|
+
const node = { name: stem(name), path: rel, kind: 'file' };
|
|
194
|
+
// Eager start, deferred settle: the read overlaps the rest of the walk
|
|
195
|
+
// while the budget counts synchronously at kick-off — the cut point
|
|
196
|
+
// (first maxFiles in sorted DFS order) is the serial walk's.
|
|
197
|
+
if (ctx.eager) {
|
|
198
|
+
await ctx.reads.gate();
|
|
199
|
+
const item = { name: node.name, description: '', locator: { kind: 'file', path: rel } };
|
|
200
|
+
node.item = item;
|
|
201
|
+
ctx.events.push({ kind: 'file', read: { rel, content: ctx.reads.start(abs), item } });
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
ctx.files.set(rel, { rel, abs, node });
|
|
205
|
+
}
|
|
206
|
+
budget.files += 1;
|
|
207
|
+
children.push(node);
|
|
208
|
+
}
|
|
209
|
+
return { children, carrierDescription: carrier };
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* The representative README child's path — the folder-description chain's
|
|
213
|
+
* own name rule (file node names are stems, so the stem 'readme',
|
|
214
|
+
* case-insensitive; children are name-sorted, the first match represents).
|
|
215
|
+
* Mirrors the tree, not the filesystem: the walk sets it at assembly, so
|
|
216
|
+
* an excluded README leaves it absent.
|
|
217
|
+
*/
|
|
218
|
+
function readmeChildPath(node) {
|
|
219
|
+
for (const child of node.children ?? []) {
|
|
220
|
+
if (child.kind === 'file' && child.name.toLowerCase() === 'readme')
|
|
221
|
+
return child.path;
|
|
222
|
+
}
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Post-walk folder descriptions, bottom-up over the assembled tree — the
|
|
227
|
+
* same chain the serial walk computed inline: carrier description first
|
|
228
|
+
* (package.json, then manifest.yml), else the folder's own first
|
|
229
|
+
* README child (children are name-sorted) carrying a real description. Runs
|
|
230
|
+
* after the settle, so file descriptions are final.
|
|
231
|
+
*/
|
|
232
|
+
export function assignFolderDescriptions(node, ctx) {
|
|
233
|
+
let readmeDescription;
|
|
234
|
+
for (const child of node.children ?? []) {
|
|
235
|
+
if (child.kind === 'dir')
|
|
236
|
+
assignFolderDescriptions(child, ctx);
|
|
237
|
+
if (readmeDescription === undefined &&
|
|
238
|
+
child.kind === 'file' &&
|
|
239
|
+
child.name.toLowerCase() === 'readme' &&
|
|
240
|
+
child.item !== undefined &&
|
|
241
|
+
!child.item.description.startsWith('Undocumented')) {
|
|
242
|
+
readmeDescription = child.item.description;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const description = ctx.carrierDescriptions.get(node) ?? readmeDescription;
|
|
246
|
+
if (description !== undefined)
|
|
247
|
+
node.description = description;
|
|
248
|
+
}
|
|
249
|
+
export function rootNode(root) {
|
|
250
|
+
const name = basename(root);
|
|
251
|
+
return { name: name === '' || name === '.' || name === '/' ? 'root' : name, path: '', kind: 'dir' };
|
|
252
|
+
}
|
|
253
|
+
function stem(fileName) {
|
|
254
|
+
return fileName.slice(0, fileName.length - extname(fileName).length);
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=tree-replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree-replay.js","sourceRoot":"","sources":["../src/tree-replay.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -33,16 +33,18 @@ export interface NavNode {
|
|
|
33
33
|
children?: NavNode[];
|
|
34
34
|
/**
|
|
35
35
|
* Dir nodes only (the root included): description from the directory's
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* from the directory's own README.md (routes
|
|
39
|
-
* semantics). Absent when
|
|
36
|
+
* standardized carriers in order — its `package.json` `description`, then
|
|
37
|
+
* its `manifest.yml` `metadata.description` — else the
|
|
38
|
+
* extraction from the directory's own README.md (routes
|
|
39
|
+
* folder-representative semantics). Absent when none carries one.
|
|
40
40
|
*/
|
|
41
41
|
description?: string;
|
|
42
|
+
/** Dir nodes only (root included): the representative README child's POSIX-relative path; present iff it is in the tree. */
|
|
43
|
+
markdown?: string;
|
|
42
44
|
}
|
|
43
45
|
/**
|
|
44
|
-
* Global scan boundary policy (
|
|
45
|
-
*
|
|
46
|
+
* Global scan boundary policy (routes traversal semantics; exclude
|
|
47
|
+
* lists are additive). All keys default — the config
|
|
46
48
|
* states only exceptions, and stating one never drops the built-in floor.
|
|
47
49
|
*/
|
|
48
50
|
export interface ScanConfig {
|
|
@@ -54,6 +56,16 @@ export interface ScanConfig {
|
|
|
54
56
|
excludeDotEntries?: boolean;
|
|
55
57
|
/** Whether traversal respects .gitignore files. */
|
|
56
58
|
respectGitignore?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Subtrees whose own nested `.git` does not cut them from this view — the
|
|
61
|
+
* engine's named-subtree core-stone exemption (git's
|
|
62
|
+
* submodule-registration face: an unregistered embedded repo is foreign
|
|
63
|
+
* territory, a registered one participates). Entries are gitignore-style
|
|
64
|
+
* patterns anchored at the view's scan root (e.g. 'deepseek-harness/');
|
|
65
|
+
* registration is not rescue — the subtree still passes every exclusion
|
|
66
|
+
* floor, and deeper embedded repos inside it still cut.
|
|
67
|
+
*/
|
|
68
|
+
boundaryExempt?: string[];
|
|
57
69
|
/** Maximum Markdown files examined per source walk; past the cap entries go unexamined — diagnostics fire and folders whose descent was cut disclose floor (≥) counts (never a silent cut). */
|
|
58
70
|
maxFiles?: number;
|
|
59
71
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@any-nav/scan",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Any-Nav scan core: the
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Any-Nav scan core: the engine facade that walks a Markdown folder into a nav tree under the routes traversal semantics (exclusions, gitignore inheritance, a loud maxFiles budget, folder descriptions) with the walk delegated to @catheadowl/pathscope. Embedders consume this directly; @any-nav/core composes it with curation, render, and search.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/CatheadOwl/any-nav.git"
|
|
9
|
+
},
|
|
6
10
|
"type": "module",
|
|
7
11
|
"main": "./dist/index.js",
|
|
8
12
|
"types": "./dist/index.d.ts",
|
|
@@ -11,7 +15,11 @@
|
|
|
11
15
|
"CHANGELOG.md"
|
|
12
16
|
],
|
|
13
17
|
"publishConfig": {
|
|
14
|
-
"access": "public"
|
|
18
|
+
"access": "public",
|
|
19
|
+
"provenance": false
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@catheadowl/pathscope": "^0.1.1"
|
|
15
23
|
},
|
|
16
24
|
"keywords": [
|
|
17
25
|
"agent",
|
package/dist/gitignore.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export interface GitignoreRule {
|
|
2
|
-
baseDir: string;
|
|
3
|
-
pattern: string;
|
|
4
|
-
negated: boolean;
|
|
5
|
-
directoryOnly: boolean;
|
|
6
|
-
}
|
|
7
|
-
export declare function readInheritedGitignoreRules(root: string, scanRoot: string): Promise<GitignoreRule[]>;
|
|
8
|
-
export declare function readGitignoreRules(dir: string): Promise<GitignoreRule[]>;
|
|
9
|
-
export declare function isGitignored(filePath: string, isDirectory: boolean, rules: readonly GitignoreRule[]): boolean;
|
package/dist/gitignore.js
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gitignore-aware traversal support, ported verbatim from the routes module
|
|
3
|
-
* (dsh-plugin-dev/extras/modules/routes/src/gitignore.ts, ADR-0010: port,
|
|
4
|
-
* don't rewrite). Known subset boundary (routes-documented): `!` re-includes
|
|
5
|
-
* inside one file are honored; cross-file re-include of an ignored path is
|
|
6
|
-
* not — revisit via a port if real corpora hit it (ADR-0010 reversal).
|
|
7
|
-
*/
|
|
8
|
-
import { readFile } from 'node:fs/promises';
|
|
9
|
-
import { join, relative, resolve } from 'node:path';
|
|
10
|
-
export async function readInheritedGitignoreRules(root, scanRoot) {
|
|
11
|
-
const rootPath = resolve(root);
|
|
12
|
-
const scanRootPath = resolve(scanRoot);
|
|
13
|
-
const relativeScanRoot = normalizeSlash(relative(rootPath, scanRootPath));
|
|
14
|
-
const segments = relativeScanRoot && !relativeScanRoot.startsWith('..')
|
|
15
|
-
? relativeScanRoot.split('/').filter(Boolean)
|
|
16
|
-
: [];
|
|
17
|
-
const dirs = [rootPath];
|
|
18
|
-
for (let index = 0; index < segments.length; index += 1) {
|
|
19
|
-
dirs.push(join(rootPath, ...segments.slice(0, index + 1)));
|
|
20
|
-
}
|
|
21
|
-
const rules = await Promise.all(dirs.map((dir) => readGitignoreRules(dir)));
|
|
22
|
-
return rules.flat();
|
|
23
|
-
}
|
|
24
|
-
export async function readGitignoreRules(dir) {
|
|
25
|
-
let content;
|
|
26
|
-
try {
|
|
27
|
-
content = await readFile(join(dir, '.gitignore'), 'utf8');
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return [];
|
|
31
|
-
}
|
|
32
|
-
return content
|
|
33
|
-
.split(/\r?\n/u)
|
|
34
|
-
.map((line) => parseGitignoreLine(dir, line))
|
|
35
|
-
.filter((rule) => rule !== null);
|
|
36
|
-
}
|
|
37
|
-
export function isGitignored(filePath, isDirectory, rules) {
|
|
38
|
-
let ignored = false;
|
|
39
|
-
// Perf (2026-10-20, semantics identical): one relative() per distinct
|
|
40
|
-
// baseDir per call — a typical tree carries many rules from the same
|
|
41
|
-
// ancestor .gitignore, and the path result is rule-invariant.
|
|
42
|
-
const relativeByBaseDir = new Map();
|
|
43
|
-
for (const rule of rules) {
|
|
44
|
-
if (rule.directoryOnly && !isDirectory)
|
|
45
|
-
continue;
|
|
46
|
-
if (!matchesRule(filePath, rule, relativeByBaseDir))
|
|
47
|
-
continue;
|
|
48
|
-
ignored = !rule.negated;
|
|
49
|
-
}
|
|
50
|
-
return ignored;
|
|
51
|
-
}
|
|
52
|
-
function parseGitignoreLine(baseDir, rawLine) {
|
|
53
|
-
let line = rawLine.trim();
|
|
54
|
-
if (!line || line.startsWith('#'))
|
|
55
|
-
return null;
|
|
56
|
-
let negated = false;
|
|
57
|
-
if (line.startsWith('!')) {
|
|
58
|
-
negated = true;
|
|
59
|
-
line = line.slice(1).trim();
|
|
60
|
-
}
|
|
61
|
-
if (!line)
|
|
62
|
-
return null;
|
|
63
|
-
const directoryOnly = line.endsWith('/');
|
|
64
|
-
line = line.replace(/^\/+/u, '').replace(/\/+$/u, '');
|
|
65
|
-
if (!line)
|
|
66
|
-
return null;
|
|
67
|
-
return {
|
|
68
|
-
baseDir,
|
|
69
|
-
pattern: normalizeSlash(line),
|
|
70
|
-
negated,
|
|
71
|
-
directoryOnly,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function matchesRule(filePath, rule, relativeByBaseDir) {
|
|
75
|
-
let relativePath = relativeByBaseDir.get(rule.baseDir);
|
|
76
|
-
if (relativePath === undefined) {
|
|
77
|
-
relativePath = normalizeSlash(relative(rule.baseDir, filePath));
|
|
78
|
-
relativeByBaseDir.set(rule.baseDir, relativePath);
|
|
79
|
-
}
|
|
80
|
-
if (!relativePath || relativePath.startsWith('..'))
|
|
81
|
-
return false;
|
|
82
|
-
if (!rule.pattern.includes('/')) {
|
|
83
|
-
return relativePath.split('/').some((part) => matchesGlob(part, rule.pattern));
|
|
84
|
-
}
|
|
85
|
-
return matchesGlob(relativePath, rule.pattern);
|
|
86
|
-
}
|
|
87
|
-
/** Compiled-glob memo (perf 2026-10-20, semantics identical): the regex
|
|
88
|
-
* depends only on the pattern, and a scan checks the same handful of
|
|
89
|
-
* patterns once per entry — compiling per check was ~70% of large-tree
|
|
90
|
-
* scan CPU. Patterns are few and bounded by the corpus's .gitignore lines. */
|
|
91
|
-
const globRegexCache = new Map();
|
|
92
|
-
function matchesGlob(value, pattern) {
|
|
93
|
-
let regex = globRegexCache.get(pattern);
|
|
94
|
-
if (regex === undefined) {
|
|
95
|
-
regex = new RegExp(`^${globToRegex(pattern)}$`, 'u');
|
|
96
|
-
globRegexCache.set(pattern, regex);
|
|
97
|
-
}
|
|
98
|
-
return regex.test(value);
|
|
99
|
-
}
|
|
100
|
-
function globToRegex(pattern) {
|
|
101
|
-
let regex = '';
|
|
102
|
-
for (let index = 0; index < pattern.length; index += 1) {
|
|
103
|
-
const char = pattern[index];
|
|
104
|
-
const next = pattern[index + 1];
|
|
105
|
-
if (char === '*' && next === '*') {
|
|
106
|
-
regex += '.*';
|
|
107
|
-
index += 1;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
if (char === '*') {
|
|
111
|
-
regex += '[^/]*';
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
if (char === '?') {
|
|
115
|
-
regex += '[^/]';
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
regex += escapeRegex(char);
|
|
119
|
-
}
|
|
120
|
-
return regex;
|
|
121
|
-
}
|
|
122
|
-
function escapeRegex(value) {
|
|
123
|
-
return value.replace(/[|\\{}()[\]^$+*?.]/gu, '\\$&');
|
|
124
|
-
}
|
|
125
|
-
function normalizeSlash(value) {
|
|
126
|
-
return value.replace(/\\/gu, '/');
|
|
127
|
-
}
|
|
128
|
-
//# sourceMappingURL=gitignore.js.map
|
package/dist/gitignore.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gitignore.js","sourceRoot":"","sources":["../src/gitignore.ts"],"names":[],"mappings":"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"}
|