@dbx-tools/path 0.9.118 → 0.9.120

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/lib/src/watch.js CHANGED
@@ -8,26 +8,30 @@
8
8
  *
9
9
  * @module
10
10
  */
11
+ import { readFile } from "node:fs/promises";
11
12
  import { isAbsolute, relative, resolve } from "path";
13
+ import { errorUtils, log } from "@dbx-tools/shared-core";
12
14
  import { watch } from "chokidar";
13
15
  import { hasMagic } from "glob";
14
16
  import { findFiles } from "./find.js";
17
+ import { GitIgnore } from "./git-ignore.js";
15
18
  import { ignorePathMatcher } from "./ignore.js";
16
19
  import { pathMatchTests } from "./match.js";
17
20
  import { FOLLOW_SYMLINKS_DEFAULT } from "./scan.js";
21
+ const logger = log.logger("path/watch");
18
22
  /**
19
23
  * Watches files and directories for real-time changes, applying the shared
20
24
  * ignore groups so it prunes the same paths {@link findFiles} skips for the
21
25
  * same patterns.
22
26
  */
23
- export function watchFiles(paths, options) {
24
- const normalized = toNormalizedFileWatchOptions(options);
25
- const { ignored, cwd, followSymlinks, ...chokidarOptions } = normalized;
27
+ export async function watchFiles(paths, options) {
28
+ const normalized = await toNormalizedFileWatchOptions(options);
29
+ const { gitIgnore, ignored, cwd, followSymlinks, ...chokidarOptions } = normalized;
26
30
  const { paths: literals, globs } = partitionPaths(paths);
27
31
  let watchPaths;
28
32
  if (globs.length > 0) {
29
33
  const distinctWatchPaths = new Set(literals);
30
- for (const path of findFiles(globs, toFindFilesOptions(normalized))) {
34
+ for await (const path of findFiles(globs, toFindFilesOptions(normalized))) {
31
35
  distinctWatchPaths.add(path);
32
36
  }
33
37
  watchPaths = [...distinctWatchPaths];
@@ -35,12 +39,21 @@ export function watchFiles(paths, options) {
35
39
  else {
36
40
  watchPaths = literals;
37
41
  }
38
- return watch(watchPaths, {
42
+ const watcher = watch(watchPaths, {
39
43
  ...chokidarOptions,
40
44
  cwd,
41
45
  followSymlinks,
42
46
  ignored,
43
47
  });
48
+ if (gitIgnore) {
49
+ attachGitIgnoreWatcher(watcher, gitIgnore, watchPaths, {
50
+ cwd,
51
+ followSymlinks,
52
+ ignoreOptions: options?.ignoreOptions,
53
+ persistent: chokidarOptions.persistent,
54
+ });
55
+ }
56
+ return watcher;
44
57
  }
45
58
  function partitionPaths(paths) {
46
59
  const globs = [];
@@ -55,13 +68,18 @@ function partitionPaths(paths) {
55
68
  * built-in groups are consolidated into one chokidar {@link MatchFunction}. When
56
69
  * `stats` marks a directory, the matcher also tests `path + "/"` for pruning.
57
70
  */
58
- function toNormalizedFileWatchOptions(options) {
71
+ async function toNormalizedFileWatchOptions(options) {
59
72
  const { cwd, ignore, followSymlinks, ignoreOptions, ...chokidarOptions } = options ?? {};
73
+ const root = cwd ?? process.cwd();
74
+ const gitIgnore = ignoreOptions?.git
75
+ ? await GitIgnore.load(root, { ignoreOptions, followSymlinks })
76
+ : undefined;
60
77
  return {
61
78
  ...chokidarOptions,
62
- cwd: cwd ?? process.cwd(),
79
+ cwd: root,
63
80
  followSymlinks: followSymlinks ?? FOLLOW_SYMLINKS_DEFAULT,
64
- ignored: toWatchMatchFunction(ignore, ignoreOptions, cwd ?? process.cwd()),
81
+ ...(gitIgnore ? { gitIgnore } : {}),
82
+ ignored: toWatchMatchFunction(ignore, ignoreOptions, root, gitIgnore),
65
83
  };
66
84
  }
67
85
  /** Glob expansion reuses the normalized ignore matcher as a cwd-relative path predicate. */
@@ -78,7 +96,7 @@ function toFindFilesOptions(normalized) {
78
96
  * {@link MatchFunction}s into one ignore test. Directory pruning uses `stats`
79
97
  * when chokidar supplies it; glob expansion passes `undefined`.
80
98
  */
81
- function toWatchMatchFunction(ignore, ignoreOptions, cwd) {
99
+ function toWatchMatchFunction(ignore, ignoreOptions, cwd, gitIgnore) {
82
100
  const patterns = [];
83
101
  const functions = [];
84
102
  if (ignore !== undefined) {
@@ -94,6 +112,8 @@ function toWatchMatchFunction(ignore, ignoreOptions, cwd) {
94
112
  matcher = matcher.or(...pathMatchTests(...patterns));
95
113
  const matchPath = (path, stats) => {
96
114
  const rel = toCwdRelativePath(path, cwd);
115
+ if (gitIgnore?.ignores(rel, stats?.isDirectory() ?? false))
116
+ return true;
97
117
  if (matcher(rel))
98
118
  return true;
99
119
  for (const fn of functions) {
@@ -113,6 +133,50 @@ function toWatchMatchFunction(ignore, ignoreOptions, cwd) {
113
133
  };
114
134
  return matchPath;
115
135
  }
136
+ function attachGitIgnoreWatcher(watcher, gitIgnore, watchPaths, options) {
137
+ const builtInIgnore = ignorePathMatcher(options.ignoreOptions);
138
+ const controls = watch(".", {
139
+ atomic: false,
140
+ cwd: options.cwd,
141
+ followSymlinks: options.followSymlinks,
142
+ ignoreInitial: true,
143
+ persistent: options.persistent,
144
+ ignored(path, stats) {
145
+ const relativePath = toCwdRelativePath(path, options.cwd);
146
+ if (GitIgnore.isFile(relativePath))
147
+ return false;
148
+ if (builtInIgnore(relativePath) || builtInIgnore(`${relativePath}/`))
149
+ return true;
150
+ return stats ? !stats.isDirectory() : false;
151
+ },
152
+ });
153
+ const reload = (path) => {
154
+ void readFile(resolve(options.cwd, path), "utf8")
155
+ .then((content) => {
156
+ gitIgnore.set(path, content);
157
+ watcher.add([...watchPaths]);
158
+ logger.debug(".gitignore updated", { path });
159
+ })
160
+ .catch((error) => {
161
+ logger.warn("failed to reload .gitignore", {
162
+ path,
163
+ error: errorUtils.toError(error),
164
+ });
165
+ });
166
+ };
167
+ controls.on("add", reload);
168
+ controls.on("change", reload);
169
+ controls.on("unlink", (path) => {
170
+ gitIgnore.delete(path);
171
+ watcher.add([...watchPaths]);
172
+ logger.debug(".gitignore removed", { path });
173
+ });
174
+ controls.on("error", (error) => watcher.emit("error", error));
175
+ const close = watcher.close.bind(watcher);
176
+ watcher.close = async () => {
177
+ await Promise.all([close(), controls.close()]);
178
+ };
179
+ }
116
180
  /** Aligns chokidar paths with the cwd-relative form {@link findFiles} uses. */
117
181
  function toCwdRelativePath(path, cwd) {
118
182
  if (!isAbsolute(path))
@@ -126,7 +190,7 @@ if (import.meta.main) {
126
190
  const cwd = resolve(process.argv[2] ?? resolve(import.meta.dirname, ".."));
127
191
  console.log("--- Starting File Watcher ---");
128
192
  console.log(`Working Directory: ${cwd}`);
129
- const watcher = watchFiles("**/src/**", {
193
+ const watcher = await watchFiles("**/src/**", {
130
194
  cwd,
131
195
  ignoreInitial: false,
132
196
  ignore: ["**/index.ts", "**/core/**"],
@@ -142,4 +206,4 @@ if (import.meta.main) {
142
206
  });
143
207
  console.log("Watcher started");
144
208
  }
145
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvd2F0Y2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7OztHQVNHO0FBR0gsT0FBTyxFQUFFLFVBQVUsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ3JELE9BQU8sRUFBa0MsS0FBSyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDaEMsT0FBTyxFQUFFLFNBQVMsRUFBd0IsTUFBTSxXQUFXLENBQUM7QUFDNUQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ2hELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDNUMsT0FBTyxFQUEwQyx1QkFBdUIsRUFBRSxNQUFNLFdBQVcsQ0FBQztBQVk1Rjs7OztHQUlHO0FBQ0gsTUFBTSxVQUFVLFVBQVUsQ0FBQyxLQUF3QixFQUFFLE9BQTBCO0lBQzdFLE1BQU0sVUFBVSxHQUFHLDRCQUE0QixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3pELE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxFQUFFLGNBQWMsRUFBRSxHQUFHLGVBQWUsRUFBRSxHQUFHLFVBQVUsQ0FBQztJQUN4RSxNQUFNLEVBQUUsS0FBSyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsR0FBRyxjQUFjLENBQUMsS0FBSyxDQUFDLENBQUM7SUFFekQsSUFBSSxVQUFvQixDQUFDO0lBQ3pCLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNyQixNQUFNLGtCQUFrQixHQUFHLElBQUksR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQzdDLEtBQUssTUFBTSxJQUFJLElBQUksU0FBUyxDQUFDLEtBQUssRUFBRSxrQkFBa0IsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDcEUs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QUFHLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLElBQUksT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUM7SUFDM0UsT0FBTyxDQUFDLEdBQUcsQ0FBQywrQkFBK0IsQ0FBQyxDQUFDO0lBQzdDLE9BQU8sQ0FBQyxHQUFHLENBQUMsc0JBQXNCLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFFekMsTUFBTSxPQUFPLEdBQUcsVUFBVSxDQUFDLFdBQVcsRUFBRTtRQUN0QyxHQUFHO1FBQ0gsYUFBYSxFQUFFLEtBQUs7UUFDcEIsTUFBTSxFQUFFLENBQUMsYUFBYSxFQUFFLFlBQVksQ0FBQztRQUNyQyxhQUFhLEVBQUU7WUFDYixJQUFJLEVBQUUsS0FBSztTQUNaO0tBQ0YsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxLQUFhLEVBQUUsSUFBWSxFQUFFLEVBQUU7UUFDaEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLEtBQUssQ0FBQyxXQUFXLEVBQUUsS0FBSyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ2xELENBQUMsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsRUFBRTtRQUM1QixPQUFPLENBQUMsS0FBSyxDQUFDLGdCQUFnQixFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3pDLENBQUMsQ0FBQyxDQUFDO0lBQ0gsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0FBQ2pDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIFJlcG9zaXRvcnktY29uc2lzdGVudCBmaWxlIHdhdGNoaW5nIG92ZXIgQ2hva2lkYXIuXG4gKlxuICogUmV1c2UgYHdhdGNoRmlsZXMoKWAgd2hlbiBsaXZlIHdvcmtmbG93cyBtdXN0IG1hdGNoIGBmaW5kRmlsZXMoKWBcbiAqIGRpc2NvdmVyeSBhbmQgdGhlIGNlbnRyYWwgaWdub3JlIGNhdGFsb2d1ZS4gVGhlIHdyYXBwZXIgZXhwYW5kcyBnbG9icyBvbmNlLFxuICogcHJ1bmVzIGlnbm9yZWQgZGlyZWN0b3JpZXMgY29uc2lzdGVudGx5LCBhbmQgcHJlc2VydmVzIENob2tpZGFyJ3MgbmF0aXZlXG4gKiB3YXRjaGVyIHJlc3VsdCBmb3IgY2FsbGVycy5cbiAqXG4gKiBAbW9kdWxlXG4gKi9cblxuaW1wb3J0IHsgU3RhdHMgfSBmcm9tIFwiZnNcIjtcbmltcG9ydCB7IGlzQWJzb2x1dGUsIHJlbGF0aXZlLCByZXNvbHZlIH0gZnJvbSBcInBhdGhcIjtcbmltcG9ydCB7IENob2tpZGFyT3B0aW9ucywgTWF0Y2hGdW5jdGlvbiwgd2F0Y2ggfSBmcm9tIFwiY2hva2lkYXJcIjtcbmltcG9ydCB7IGhhc01hZ2ljIH0gZnJvbSBcImdsb2JcIjtcbmltcG9ydCB7IGZpbmRGaWxlcywgdHlwZSBGaWxlRmluZE9wdGlvbnMgfSBmcm9tIFwiLi9maW5kLnRzXCI7XG5pbXBvcnQgeyBpZ25vcmVQYXRoTWF0Y2hlciB9IGZyb20gXCIuL2lnbm9yZS50c1wiO1xuaW1wb3J0IHsgcGF0aE1hdGNoVGVzdHMgfSBmcm9tIFwiLi9tYXRjaC50c1wiO1xuaW1wb3J0IHsgRmlsZVNjYW5JZ25vcmVPcHRpb25zLCBGaWxlU2Nhbk9wdGlvbnMsIEZPTExPV19TWU1MSU5LU19ERUZBVUxUIH0gZnJvbSBcIi4vc2Nhbi50c1wiO1xuXG4vKiogQ2hva2lkYXIgb3B0aW9ucyBleHRlbmRlZCB3aXRoIHRoZSBzaGFyZWQgZGJ4LXRvb2xzIGlnbm9yZSBjb25maWd1cmF0aW9uLiAqL1xuZXhwb3J0IGludGVyZmFjZSBGaWxlV2F0Y2hPcHRpb25zXG4gIGV4dGVuZHMgT21pdDxDaG9raWRhck9wdGlvbnMsIFwiaWdub3JlZFwiPiwgT21pdDxGaWxlU2Nhbk9wdGlvbnMsIFwiaWdub3JlXCI+IHtcbiAgaWdub3JlPzogc3RyaW5nIHwgc3RyaW5nW10gfCBNYXRjaEZ1bmN0aW9uIHwgTWF0Y2hGdW5jdGlvbltdO1xufVxuXG5pbnRlcmZhY2UgTm9ybWFsaXplZEZpbGVXYXRjaE9wdGlvbnMgZXh0ZW5kcyBPbWl0PEZpbGVXYXRjaE9wdGlvbnMsIFwiaWdub3JlXCIgfCBcImlnbm9yZU9wdGlvbnNcIj4ge1xuICBpZ25vcmVkOiBNYXRjaEZ1bmN0aW9uO1xufVxuXG4vKipcbiAqIFdhdGNoZXMgZmlsZXMgYW5kIGRpcmVjdG9yaWVzIGZvciByZWFsLXRpbWUgY2hhbmdlcywgYXBwbHlpbmcgdGhlIHNoYXJlZFxuICogaWdub3JlIGdyb3VwcyBzbyBpdCBwcnVuZXMgdGhlIHNhbWUgcGF0aHMge0BsaW5rIGZpbmRGaWxlc30gc2tpcHMgZm9yIHRoZVxuICogc2FtZSBwYXR0ZXJucy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHdhdGNoRmlsZXMocGF0aHM6IHN0cmluZyB8IHN0cmluZ1tdLCBvcHRpb25zPzogRmlsZVdhdGNoT3B0aW9ucykge1xuICBjb25zdCBub3JtYWxpemVkID0gdG9Ob3JtYWxpemVkRmlsZVdhdGNoT3B0aW9ucyhvcHRpb25zKTtcbiAgY29uc3QgeyBpZ25vcmVkLCBjd2QsIGZvbGxvd1N5bWxpbmtzLCAuLi5jaG9raWRhck9wdGlvbnMgfSA9IG5vcm1hbGl6ZWQ7XG4gIGNvbnN0IHsgcGF0aHM6IGxpdGVyYWxzLCBnbG9icyB9ID0gcGFydGl0aW9uUGF0aHMocGF0aHMpO1xuXG4gIGxldCB3YXRjaFBhdGhzOiBzdHJpbmdbXTtcbiAgaWYgKGdsb2JzLmxlbmd0aCA+IDApIHtcbiAgICBjb25zdCBkaXN0aW5jdFdhdGNoUGF0aHMgPSBuZXcgU2V0KGxpdGVyYWxzKTtcbiAgICBmb3IgKGNvbnN0IHBhdGggb2YgZmluZEZpbGVzKGdsb2JzLCB0b0ZpbmRGaWxlc09wdGlvbnMobm9ybWFsaXplZCkpKSB7XG4gICAgICBkaXN0aW5jdFdhdGNoUGF0aHMuYWRkKHBhdGgpO1xuICAgIH1cbiAgICB3YXRjaFBhdGhzID0gWy4uLmRpc3RpbmN0V2F0Y2hQYXRoc107XG4gIH0gZWxzZSB7XG4gICAgd2F0Y2hQYXRocyA9IGxpdGVyYWxzO1xuICB9XG5cbiAgcmV0dXJuIHdhdGNoKHdhdGNoUGF0aHMsIHtcbiAgICAuLi5jaG9raWRhck9wdGlvbnMsXG4gICAgY3dkLFxuICAgIGZvbGxvd1N5bWxpbmtzLFxuICAgIGlnbm9yZWQsXG4gIH0pO1xufVxuXG5mdW5jdGlvbiBwYXJ0aXRpb25QYXRocyhwYXRoczogc3RyaW5nIHwgc3RyaW5nW10pOiB7IHBhdGhzOiBzdHJpbmdbXTsgZ2xvYnM6IHN0cmluZ1tdIH0ge1xuICBjb25zdCBnbG9iczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3QgbGl0ZXJhbHM6IHN0cmluZ1tdID0gW107XG4gIGZvciAoY29uc3QgcGF0aCBvZiBBcnJheS5pc0FycmF5KHBhdGhzKSA/IHBhdGhzIDogW3BhdGhzXSkge1xuICAgIChoYXNNYWdpYyhwYXRoKSA/IGdsb2JzIDogbGl0ZXJhbHMpLnB1c2gocGF0aCk7XG4gIH1cbiAgcmV0dXJuIHsgcGF0aHM6IGxpdGVyYWxzLCBnbG9icyB9O1xufVxuXG4vKipcbiAqIEJ1aWxkcyB7QGxpbmsgTm9ybWFsaXplZEZpbGVXYXRjaE9wdGlvbnN9IGZpcnN0OiBhbGwgY2FsbGVyIGlnbm9yZSBpbnB1dHMgYW5kXG4gKiBidWlsdC1pbiBncm91cHMgYXJlIGNvbnNvbGlkYXRlZCBpbnRvIG9uZSBjaG9raWRhciB7QGxpbmsgTWF0Y2hGdW5jdGlvbn0uIFdoZW5cbiAqIGBzdGF0c2AgbWFya3MgYSBkaXJlY3RvcnksIHRoZSBtYXRjaGVyIGFsc28gdGVzdHMgYHBhdGggKyBcIi9cImAgZm9yIHBydW5pbmcuXG4gKi9cbmZ1bmN0aW9uIHRvTm9ybWFsaXplZEZpbGVXYXRjaE9wdGlvbnMob3B0aW9ucz86IEZpbGVXYXRjaE9wdGlvbnMpOiBOb3JtYWxpemVkRmlsZVdhdGNoT3B0aW9ucyAmIHtcbiAgY3dkOiBzdHJpbmc7XG4gIGZvbGxvd1N5bWxpbmtzOiBib29sZWFuO1xufSB7XG4gIGNvbnN0IHsgY3dkLCBpZ25vcmUsIGZvbGxvd1N5bWxpbmtzLCBpZ25vcmVPcHRpb25zLCAuLi5jaG9raWRhck9wdGlvbnMgfSA9IG9wdGlvbnMgPz8ge307XG4gIHJldHVybiB7XG4gICAgLi4uY2hva2lkYXJPcHRpb25zLFxuICAgIGN3ZDogY3dkID8/IHByb2Nlc3MuY3dkKCksXG4gICAgZm9sbG93U3ltbGlua3M6IGZvbGxvd1N5bWxpbmtzID8/IEZPTExPV19TWU1MSU5LU19ERUZBVUxULFxuICAgIGlnbm9yZWQ6IHRvV2F0Y2hNYXRjaEZ1bmN0aW9uKGlnbm9yZSwgaWdub3JlT3B0aW9ucywgY3dkID8/IHByb2Nlc3MuY3dkKCkpLFxuICB9O1xufVxuXG4vKiogR2xvYiBleHBhbnNpb24gcmV1c2VzIHRoZSBub3JtYWxpemVkIGlnbm9yZSBtYXRjaGVyIGFzIGEgY3dkLXJlbGF0aXZlIHBhdGggcHJlZGljYXRlLiAqL1xuZnVuY3Rpb24gdG9GaW5kRmlsZXNPcHRpb25zKFxuICBub3JtYWxpemVkOiBOb3JtYWxpemVkRmlsZVdhdGNoT3B0aW9ucyAmIHsgY3dkOiBzdHJpbmc7IGZvbGxvd1N5bWxpbmtzOiBib29sZWFuIH0sXG4pOiBGaWxlRmluZE9wdGlvbnMge1xuICBjb25zdCB7IGlnbm9yZWQsIGN3ZCwgZm9sbG93U3ltbGlua3MgfSA9IG5vcm1hbGl6ZWQ7XG4gIHJldHVybiB7XG4gICAgY3dkLFxuICAgIGZvbGxvd1N5bWxpbmtzLFxuICAgIGlnbm9yZTogKHBhdGgpID0+IGlnbm9yZWQocGF0aCwgdW5kZWZpbmVkKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBDb25zb2xpZGF0ZXMgYnVpbHQtaW4gaWdub3JlIGdyb3VwcywgY2FsbGVyIGdsb2IgcGF0dGVybnMsIGFuZCBjYWxsZXJcbiAqIHtAbGluayBNYXRjaEZ1bmN0aW9ufXMgaW50byBvbmUgaWdub3JlIHRlc3QuIERpcmVjdG9yeSBwcnVuaW5nIHVzZXMgYHN0YXRzYFxuICogd2hlbiBjaG9raWRhciBzdXBwbGllcyBpdDsgZ2xvYiBleHBhbnNpb24gcGFzc2VzIGB1bmRlZmluZWRgLlxuICovXG5mdW5jdGlvbiB0b1dhdGNoTWF0Y2hGdW5jdGlvbihcbiAgaWdub3JlOiBGaWxlV2F0Y2hPcHRpb25zW1wiaWdub3JlXCJdIHwgdW5kZWZpbmVkLFxuICBpZ25vcmVPcHRpb25zOiBGaWxlU2Nhbklnbm9yZU9wdGlvbnMgfCB1bmRlZmluZWQsXG4gIGN3ZDogc3RyaW5nLFxuKTogTWF0Y2hGdW5jdGlvbiB7XG4gIGNvbnN0IHBhdHRlcm5zOiBzdHJpbmdbXSA9IFtdO1xuICBjb25zdCBmdW5jdGlvbnM6IE1hdGNoRnVuY3Rpb25bXSA9IFtdO1xuXG4gIGlmIChpZ25vcmUgIT09IHVuZGVmaW5lZCkge1xuICAgIGZvciAoY29uc3QgaXRlbSBvZiBBcnJheS5pc0FycmF5KGlnbm9yZSkgPyBpZ25vcmUgOiBbaWdub3JlXSkge1xuICAgICAgaWYgKHR5cGVvZiBpdGVtID09PSBcInN0cmluZ1wiKSBwYXR0ZXJucy5wdXNoKGl0ZW0pO1xuICAgICAgZWxzZSBmdW5jdGlvbnMucHVzaChpdGVtKTtcbiAgICB9XG4gIH1cblxuICBsZXQgbWF0Y2hlciA9IGlnbm9yZVBhdGhNYXRjaGVyKGlnbm9yZU9wdGlvbnMpO1xuICBpZiAocGF0dGVybnMubGVuZ3RoID4gMCkgbWF0Y2hlciA9IG1hdGNoZXIub3IoLi4ucGF0aE1hdGNoVGVzdHMoLi4ucGF0dGVybnMpKTtcblxuICBjb25zdCBtYXRjaFBhdGggPSAocGF0aDogc3RyaW5nLCBzdGF0cz86IFN0YXRzKTogYm9vbGVhbiA9PiB7XG4gICAgY29uc3QgcmVsID0gdG9Dd2RSZWxhdGl2ZVBhdGgocGF0aCwgY3dkKTtcbiAgICBpZiAobWF0Y2hlcihyZWwpKSByZXR1cm4gdHJ1ZTtcbiAgICBmb3IgKGNvbnN0IGZuIG9mIGZ1bmN0aW9ucykge1xuICAgICAgaWYgKGZuKHJlbCwgc3RhdHMpKSByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgaWYgKHN0YXRzPy5pc0RpcmVjdG9yeSgpKSB7XG4gICAgICBjb25zdCBkaXJlY3RvcnlQYXRoID0gcmVsICsgXCIvXCI7XG4gICAgICBpZiAobWF0Y2hlcihkaXJlY3RvcnlQYXRoKSkgcmV0dXJuIHRydWU7XG4gICAgICBmb3IgKGNvbnN0IGZuIG9mIGZ1bmN0aW9ucykge1xuICAgICAgICBpZiAoZm4oZGlyZWN0b3J5UGF0aCwgc3RhdHMpKSByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9O1xuXG4gIHJldHVybiBtYXRjaFBhdGg7XG59XG5cbi8qKiBBbGlnbnMgY2hva2lkYXIgcGF0aHMgd2l0aCB0aGUgY3dkLXJlbGF0aXZlIGZvcm0ge0BsaW5rIGZpbmRGaWxlc30gdXNlcy4gKi9cbmZ1bmN0aW9uIHRvQ3dkUmVsYXRpdmVQYXRoKHBhdGg6IHN0cmluZywgY3dkOiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoIWlzQWJzb2x1dGUocGF0aCkpIHJldHVybiBwYXRoO1xuICBjb25zdCByZWwgPSByZWxhdGl2ZShjd2QsIHBhdGgpO1xuICByZXR1cm4gcmVsID09PSBcIlwiID8gXCIuXCIgOiByZWw7XG59XG5cbi8vIE1hbnVhbCBkZW1vOiBydW4gdGhpcyBmaWxlIGRpcmVjdGx5IChgYnVuIHNyYy93YXRjaC50cyBbZGlyXWApIHRvIHdhdGNoIGEgdHJlZVxuLy8gYW5kIGxvZyBldmVyeSBmaWxlIGV2ZW50IGFzIGl0IGhhcHBlbnMuIERlZmF1bHRzIHRvIHRoaXMgcGFja2FnZS5cbmlmIChpbXBvcnQubWV0YS5tYWluKSB7XG4gIGNvbnN0IGN3ZCA9IHJlc29sdmUocHJvY2Vzcy5hcmd2WzJdID8/IHJlc29sdmUoaW1wb3J0Lm1ldGEuZGlybmFtZSwgXCIuLlwiKSk7XG4gIGNvbnNvbGUubG9nKFwiLS0tIFN0YXJ0aW5nIEZpbGUgV2F0Y2hlciAtLS1cIik7XG4gIGNvbnNvbGUubG9nKGBXb3JraW5nIERpcmVjdG9yeTogJHtjd2R9YCk7XG5cbiAgY29uc3Qgd2F0Y2hlciA9IHdhdGNoRmlsZXMoXCIqKi9zcmMvKipcIiwge1xuICAgIGN3ZCxcbiAgICBpZ25vcmVJbml0aWFsOiBmYWxzZSxcbiAgICBpZ25vcmU6IFtcIioqL2luZGV4LnRzXCIsIFwiKiovY29yZS8qKlwiXSxcbiAgICBpZ25vcmVPcHRpb25zOiB7XG4gICAgICB0ZW1wOiBmYWxzZSxcbiAgICB9LFxuICB9KTtcbiAgd2F0Y2hlci5vbihcImFsbFwiLCAoZXZlbnQ6IHN0cmluZywgcGF0aDogc3RyaW5nKSA9PiB7XG4gICAgY29uc29sZS5sb2coYFske2V2ZW50LnRvVXBwZXJDYXNlKCl9XSAke3BhdGh9YCk7XG4gIH0pO1xuICB3YXRjaGVyLm9uKFwiZXJyb3JcIiwgKGVycm9yKSA9PiB7XG4gICAgY29uc29sZS5lcnJvcihcIldhdGNoZXIgZXJyb3I6XCIsIGVycm9yKTtcbiAgfSk7XG4gIGNvbnNvbGUubG9nKFwiV2F0Y2hlciBzdGFydGVkXCIpO1xufVxuIl19
209
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2F0Y2guanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvd2F0Y2gudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7OztHQVNHO0FBR0gsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxVQUFVLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUNyRCxPQUFPLEVBQUUsVUFBVSxFQUFFLEdBQUcsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3pELE9BQU8sRUFBa0MsS0FBSyxFQUFrQixNQUFNLFVBQVUsQ0FBQztBQUNqRixPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2hDLE9BQU8sRUFBRSxTQUFTLEVBQXdCLE1BQU0sV0FBVyxDQUFDO0FBQzVELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUM1QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDaEQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLFlBQVksQ0FBQztBQUM1QyxPQUFPLEVBQTBDLHVCQUF1QixFQUFFLE1BQU0sV0FBVyxDQUFDO0FBRTVGLE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUMsWUFBWSxDQUFDLENBQUM7QUFheEM7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUM5QixLQUF3QixFQUN4QixPQUEwQjtJQUUxQixNQUFNLFVBQVUsR0FBRyxNQUFNLDRCQUE0QixDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQy9ELE1BQU0sRUFBRSxTQUFTLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBRSxjQUFjLEVBQUUsR0FBRyxlQUFlLEVBQUUsR0FBRyxVQUFVLENBQUM7SUFDbkYsTUFBTSxFQUFFLEtBQUssRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLEdBQUcsY0FBYyxDQUFDLEtB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LElBQUksQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQztJQUMzRSxPQUFPLENBQUMsR0FBRyxDQUFDLCtCQUErQixDQUFDLENBQUM7SUFDN0MsT0FBTyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUV6QyxNQUFNLE9BQU8sR0FBRyxNQUFNLFVBQVUsQ0FBQyxXQUFXLEVBQUU7UUFDNUMsR0FBRztRQUNILGFBQWEsRUFBRSxLQUFLO1FBQ3BCLE1BQU0sRUFBRSxDQUFDLGFBQWEsRUFBRSxZQUFZLENBQUM7UUFDckMsYUFBYSxFQUFFO1lBQ2IsSUFBSSxFQUFFLEtBQUs7U0FDWjtLQUNGLENBQUMsQ0FBQztJQUNILE9BQU8sQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLENBQUMsS0FBYSxFQUFFLElBQVksRUFBRSxFQUFFO1FBQ2hELE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxLQUFLLENBQUMsV0FBVyxFQUFFLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQztJQUNsRCxDQUFDLENBQUMsQ0FBQztJQUNILE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxFQUFFLENBQUMsS0FBSyxFQUFFLEVBQUU7UUFDNUIsT0FBTyxDQUFDLEtBQUssQ0FBQyxnQkFBZ0IsRUFBRSxLQUFLLENBQUMsQ0FBQztJQUN6QyxDQUFDLENBQUMsQ0FBQztJQUNILE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLENBQUMsQ0FBQztBQUNqQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBSZXBvc2l0b3J5LWNvbnNpc3RlbnQgZmlsZSB3YXRjaGluZyBvdmVyIENob2tpZGFyLlxuICpcbiAqIFJldXNlIGB3YXRjaEZpbGVzKClgIHdoZW4gbGl2ZSB3b3JrZmxvd3MgbXVzdCBtYXRjaCBgZmluZEZpbGVzKClgXG4gKiBkaXNjb3ZlcnkgYW5kIHRoZSBjZW50cmFsIGlnbm9yZSBjYXRhbG9ndWUuIFRoZSB3cmFwcGVyIGV4cGFuZHMgZ2xvYnMgb25jZSxcbiAqIHBydW5lcyBpZ25vcmVkIGRpcmVjdG9yaWVzIGNvbnNpc3RlbnRseSwgYW5kIHByZXNlcnZlcyBDaG9raWRhcidzIG5hdGl2ZVxuICogd2F0Y2hlciByZXN1bHQgZm9yIGNhbGxlcnMuXG4gKlxuICogQG1vZHVsZVxuICovXG5cbmltcG9ydCB7IFN0YXRzIH0gZnJvbSBcImZzXCI7XG5pbXBvcnQgeyByZWFkRmlsZSB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgeyBpc0Fic29sdXRlLCByZWxhdGl2ZSwgcmVzb2x2ZSB9IGZyb20gXCJwYXRoXCI7XG5pbXBvcnQgeyBlcnJvclV0aWxzLCBsb2cgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgQ2hva2lkYXJPcHRpb25zLCBNYXRjaEZ1bmN0aW9uLCB3YXRjaCwgdHlwZSBGU1dhdGNoZXIgfSBmcm9tIFwiY2hva2lkYXJcIjtcbmltcG9ydCB7IGhhc01hZ2ljIH0gZnJvbSBcImdsb2JcIjtcbmltcG9ydCB7IGZpbmRGaWxlcywgdHlwZSBGaWxlRmluZE9wdGlvbnMgfSBmcm9tIFwiLi9maW5kLnRzXCI7XG5pbXBvcnQgeyBHaXRJZ25vcmUgfSBmcm9tIFwiLi9naXQtaWdub3JlLnRzXCI7XG5pbXBvcnQgeyBpZ25vcmVQYXRoTWF0Y2hlciB9IGZyb20gXCIuL2lnbm9yZS50c1wiO1xuaW1wb3J0IHsgcGF0aE1hdGNoVGVzdHMgfSBmcm9tIFwiLi9tYXRjaC50c1wiO1xuaW1wb3J0IHsgRmlsZVNjYW5JZ25vcmVPcHRpb25zLCBGaWxlU2Nhbk9wdGlvbnMsIEZPTExPV19TWU1MSU5LU19ERUZBVUxUIH0gZnJvbSBcIi4vc2Nhbi50c1wiO1xuXG5jb25zdCBsb2dnZXIgPSBsb2cubG9nZ2VyKFwicGF0aC93YXRjaFwiKTtcblxuLyoqIENob2tpZGFyIG9wdGlvbnMgZXh0ZW5kZWQgd2l0aCB0aGUgc2hhcmVkIGRieC10b29scyBpZ25vcmUgY29uZmlndXJhdGlvbi4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgRmlsZVdhdGNoT3B0aW9uc1xuICBleHRlbmRzIE9taXQ8Q2hva2lkYXJPcHRpb25zLCBcImlnbm9yZWRcIj4sIE9taXQ8RmlsZVNjYW5PcHRpb25zLCBcImlnbm9yZVwiPiB7XG4gIGlnbm9yZT86IHN0cmluZyB8IHN0cmluZ1tdIHwgTWF0Y2hGdW5jdGlvbiB8IE1hdGNoRnVuY3Rpb25bXTtcbn1cblxuaW50ZXJmYWNlIE5vcm1hbGl6ZWRGaWxlV2F0Y2hPcHRpb25zIGV4dGVuZHMgT21pdDxGaWxlV2F0Y2hPcHRpb25zLCBcImlnbm9yZVwiIHwgXCJpZ25vcmVPcHRpb25zXCI+IHtcbiAgZ2l0SWdub3JlPzogR2l0SWdub3JlO1xuICBpZ25vcmVkOiBNYXRjaEZ1bmN0aW9uO1xufVxuXG4vKipcbiAqIFdhdGNoZXMgZmlsZXMgYW5kIGRpcmVjdG9yaWVzIGZvciByZWFsLXRpbWUgY2hhbmdlcywgYXBwbHlpbmcgdGhlIHNoYXJlZFxuICogaWdub3JlIGdyb3VwcyBzbyBpdCBwcnVuZXMgdGhlIHNhbWUgcGF0aHMge0BsaW5rIGZpbmRGaWxlc30gc2tpcHMgZm9yIHRoZVxuICogc2FtZSBwYXR0ZXJucy5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHdhdGNoRmlsZXMoXG4gIHBhdGhzOiBzdHJpbmcgfCBzdHJpbmdbXSxcbiAgb3B0aW9ucz86IEZpbGVXYXRjaE9wdGlvbnMsXG4pOiBQcm9taXNlPEZTV2F0Y2hlcj4ge1xuICBjb25zdCBub3JtYWxpemVkID0gYXdhaXQgdG9Ob3JtYWxpemVkRmlsZVdhdGNoT3B0aW9ucyhvcHRpb25zKTtcbiAgY29uc3QgeyBnaXRJZ25vcmUsIGlnbm9yZWQsIGN3ZCwgZm9sbG93U3ltbGlua3MsIC4uLmNob2tpZGFyT3B0aW9ucyB9ID0gbm9ybWFsaXplZDtcbiAgY29uc3QgeyBwYXRoczogbGl0ZXJhbHMsIGdsb2JzIH0gPSBwYXJ0aXRpb25QYXRocyhwYXRocyk7XG5cbiAgbGV0IHdhdGNoUGF0aHM6IHN0cmluZ1tdO1xuICBpZiAoZ2xvYnMubGVuZ3RoID4gMCkge1xuICAgIGNvbnN0IGRpc3RpbmN0V2F0Y2hQYXRocyA9IG5ldyBTZXQobGl0ZXJhbHMpO1xuICAgIGZvciBhd2FpdCAoY29uc3QgcGF0aCBvZiBmaW5kRmlsZXMoZ2xvYnMsIHRvRmluZEZpbGVzT3B0aW9ucyhub3JtYWxpemVkKSkpIHtcbiAgICAgIGRpc3RpbmN0V2F0Y2hQYXRocy5hZGQocGF0aCk7XG4gICAgfVxuICAgIHdhdGNoUGF0aHMgPSBbLi4uZGlzdGluY3RXYXRjaFBhdGhzXTtcbiAgfSBlbHNlIHtcbiAgICB3YXRjaFBhdGhzID0gbGl0ZXJhbHM7XG4gIH1cblxuICBjb25zdCB3YXRjaGVyID0gd2F0Y2god2F0Y2hQYXRocywge1xuICAgIC4uLmNob2tpZGFyT3B0aW9ucyxcbiAgICBjd2QsXG4gICAgZm9sbG93U3ltbGlua3MsXG4gICAgaWdub3JlZCxcbiAgfSk7XG4gIGlmIChnaXRJZ25vcmUpIHtcbiAgICBhdHRhY2hHaXRJZ25vcmVXYXRjaGVyKHdhdGNoZXIsIGdpdElnbm9yZSwgd2F0Y2hQYXRocywge1xuICAgICAgY3dkLFxuICAgICAgZm9sbG93U3ltbGlua3MsXG4gICAgICBpZ25vcmVPcHRpb25zOiBvcHRpb25zPy5pZ25vcmVPcHRpb25zLFxuICAgICAgcGVyc2lzdGVudDogY2hva2lkYXJPcHRpb25zLnBlcnNpc3RlbnQsXG4gICAgfSk7XG4gIH1cbiAgcmV0dXJuIHdhdGNoZXI7XG59XG5cbmZ1bmN0aW9uIHBhcnRpdGlvblBhdGhzKHBhdGhzOiBzdHJpbmcgfCBzdHJpbmdbXSk6IHsgcGF0aHM6IHN0cmluZ1tdOyBnbG9iczogc3RyaW5nW10gfSB7XG4gIGNvbnN0IGdsb2JzOiBzdHJpbmdbXSA9IFtdO1xuICBjb25zdCBsaXRlcmFsczogc3RyaW5nW10gPSBbXTtcbiAgZm9yIChjb25zdCBwYXRoIG9mIEFycmF5LmlzQXJyYXkocGF0aHMpID8gcGF0aHMgOiBbcGF0aHNdKSB7XG4gICAgKGhhc01hZ2ljKHBhdGgpID8gZ2xvYnMgOiBsaXRlcmFscykucHVzaChwYXRoKTtcbiAgfVxuICByZXR1cm4geyBwYXRoczogbGl0ZXJhbHMsIGdsb2JzIH07XG59XG5cbi8qKlxuICogQnVpbGRzIHtAbGluayBOb3JtYWxpemVkRmlsZVdhdGNoT3B0aW9uc30gZmlyc3Q6IGFsbCBjYWxsZXIgaWdub3JlIGlucHV0cyBhbmRcbiAqIGJ1aWx0LWluIGdyb3VwcyBhcmUgY29uc29saWRhdGVkIGludG8gb25lIGNob2tpZGFyIHtAbGluayBNYXRjaEZ1bmN0aW9ufS4gV2hlblxuICogYHN0YXRzYCBtYXJrcyBhIGRpcmVjdG9yeSwgdGhlIG1hdGNoZXIgYWxzbyB0ZXN0cyBgcGF0aCArIFwiL1wiYCBmb3IgcHJ1bmluZy5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gdG9Ob3JtYWxpemVkRmlsZVdhdGNoT3B0aW9ucyhvcHRpb25zPzogRmlsZVdhdGNoT3B0aW9ucyk6IFByb21pc2U8XG4gIE5vcm1hbGl6ZWRGaWxlV2F0Y2hPcHRpb25zICYge1xuICAgIGN3ZDogc3RyaW5nO1xuICAgIGZvbGxvd1N5bWxpbmtzOiBib29sZWFuO1xuICB9XG4+IHtcbiAgY29uc3QgeyBjd2QsIGlnbm9yZSwgZm9sbG93U3ltbGlua3MsIGlnbm9yZU9wdGlvbnMsIC4uLmNob2tpZGFyT3B0aW9ucyB9ID0gb3B0aW9ucyA/PyB7fTtcbiAgY29uc3Qgcm9vdCA9IGN3ZCA/PyBwcm9jZXNzLmN3ZCgpO1xuICBjb25zdCBnaXRJZ25vcmUgPSBpZ25vcmVPcHRpb25zPy5naXRcbiAgICA/IGF3YWl0IEdpdElnbm9yZS5sb2FkKHJvb3QsIHsgaWdub3JlT3B0aW9ucywgZm9sbG93U3ltbGlua3MgfSlcbiAgICA6IHVuZGVmaW5lZDtcbiAgcmV0dXJuIHtcbiAgICAuLi5jaG9raWRhck9wdGlvbnMsXG4gICAgY3dkOiByb290LFxuICAgIGZvbGxvd1N5bWxpbmtzOiBmb2xsb3dTeW1saW5rcyA/PyBGT0xMT1dfU1lNTElOS1NfREVGQVVMVCxcbiAgICAuLi4oZ2l0SWdub3JlID8geyBnaXRJZ25vcmUgfSA6IHt9KSxcbiAgICBpZ25vcmVkOiB0b1dhdGNoTWF0Y2hGdW5jdGlvbihpZ25vcmUsIGlnbm9yZU9wdGlvbnMsIHJvb3QsIGdpdElnbm9yZSksXG4gIH07XG59XG5cbi8qKiBHbG9iIGV4cGFuc2lvbiByZXVzZXMgdGhlIG5vcm1hbGl6ZWQgaWdub3JlIG1hdGNoZXIgYXMgYSBjd2QtcmVsYXRpdmUgcGF0aCBwcmVkaWNhdGUuICovXG5mdW5jdGlvbiB0b0ZpbmRGaWxlc09wdGlvbnMoXG4gIG5vcm1hbGl6ZWQ6IE5vcm1hbGl6ZWRGaWxlV2F0Y2hPcHRpb25zICYgeyBjd2Q6IHN0cmluZzsgZm9sbG93U3ltbGlua3M6IGJvb2xlYW4gfSxcbik6IEZpbGVGaW5kT3B0aW9ucyB7XG4gIGNvbnN0IHsgaWdub3JlZCwgY3dkLCBmb2xsb3dTeW1saW5rcyB9ID0gbm9ybWFsaXplZDtcbiAgcmV0dXJuIHtcbiAgICBjd2QsXG4gICAgZm9sbG93U3ltbGlua3MsXG4gICAgaWdub3JlOiAocGF0aCkgPT4gaWdub3JlZChwYXRoLCB1bmRlZmluZWQpLFxuICB9O1xufVxuXG4vKipcbiAqIENvbnNvbGlkYXRlcyBidWlsdC1pbiBpZ25vcmUgZ3JvdXBzLCBjYWxsZXIgZ2xvYiBwYXR0ZXJucywgYW5kIGNhbGxlclxuICoge0BsaW5rIE1hdGNoRnVuY3Rpb259cyBpbnRvIG9uZSBpZ25vcmUgdGVzdC4gRGlyZWN0b3J5IHBydW5pbmcgdXNlcyBgc3RhdHNgXG4gKiB3aGVuIGNob2tpZGFyIHN1cHBsaWVzIGl0OyBnbG9iIGV4cGFuc2lvbiBwYXNzZXMgYHVuZGVmaW5lZGAuXG4gKi9cbmZ1bmN0aW9uIHRvV2F0Y2hNYXRjaEZ1bmN0aW9uKFxuICBpZ25vcmU6IEZpbGVXYXRjaE9wdGlvbnNbXCJpZ25vcmVcIl0gfCB1bmRlZmluZWQsXG4gIGlnbm9yZU9wdGlvbnM6IEZpbGVTY2FuSWdub3JlT3B0aW9ucyB8IHVuZGVmaW5lZCxcbiAgY3dkOiBzdHJpbmcsXG4gIGdpdElnbm9yZT86IEdpdElnbm9yZSxcbik6IE1hdGNoRnVuY3Rpb24ge1xuICBjb25zdCBwYXR0ZXJuczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3QgZnVuY3Rpb25zOiBNYXRjaEZ1bmN0aW9uW10gPSBbXTtcblxuICBpZiAoaWdub3JlICE9PSB1bmRlZmluZWQpIHtcbiAgICBmb3IgKGNvbnN0IGl0ZW0gb2YgQXJyYXkuaXNBcnJheShpZ25vcmUpID8gaWdub3JlIDogW2lnbm9yZV0pIHtcbiAgICAgIGlmICh0eXBlb2YgaXRlbSA9PT0gXCJzdHJpbmdcIikgcGF0dGVybnMucHVzaChpdGVtKTtcbiAgICAgIGVsc2UgZnVuY3Rpb25zLnB1c2goaXRlbSk7XG4gICAgfVxuICB9XG5cbiAgbGV0IG1hdGNoZXIgPSBpZ25vcmVQYXRoTWF0Y2hlcihpZ25vcmVPcHRpb25zKTtcbiAgaWYgKHBhdHRlcm5zLmxlbmd0aCA+IDApIG1hdGNoZXIgPSBtYXRjaGVyLm9yKC4uLnBhdGhNYXRjaFRlc3RzKC4uLnBhdHRlcm5zKSk7XG5cbiAgY29uc3QgbWF0Y2hQYXRoID0gKHBhdGg6IHN0cmluZywgc3RhdHM/OiBTdGF0cyk6IGJvb2xlYW4gPT4ge1xuICAgIGNvbnN0IHJlbCA9IHRvQ3dkUmVsYXRpdmVQYXRoKHBhdGgsIGN3ZCk7XG4gICAgaWYgKGdpdElnbm9yZT8uaWdub3JlcyhyZWwsIHN0YXRzPy5pc0RpcmVjdG9yeSgpID8/IGZhbHNlKSkgcmV0dXJuIHRydWU7XG4gICAgaWYgKG1hdGNoZXIocmVsKSkgcmV0dXJuIHRydWU7XG4gICAgZm9yIChjb25zdCBmbiBvZiBmdW5jdGlvbnMpIHtcbiAgICAgIGlmIChmbihyZWwsIHN0YXRzKSkgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIGlmIChzdGF0cz8uaXNEaXJlY3RvcnkoKSkge1xuICAgICAgY29uc3QgZGlyZWN0b3J5UGF0aCA9IHJlbCArIFwiL1wiO1xuICAgICAgaWYgKG1hdGNoZXIoZGlyZWN0b3J5UGF0aCkpIHJldHVybiB0cnVlO1xuICAgICAgZm9yIChjb25zdCBmbiBvZiBmdW5jdGlvbnMpIHtcbiAgICAgICAgaWYgKGZuKGRpcmVjdG9yeVBhdGgsIHN0YXRzKSkgcmV0dXJuIHRydWU7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBmYWxzZTtcbiAgfTtcblxuICByZXR1cm4gbWF0Y2hQYXRoO1xufVxuXG5mdW5jdGlvbiBhdHRhY2hHaXRJZ25vcmVXYXRjaGVyKFxuICB3YXRjaGVyOiBGU1dhdGNoZXIsXG4gIGdpdElnbm9yZTogR2l0SWdub3JlLFxuICB3YXRjaFBhdGhzOiByZWFkb25seSBzdHJpbmdbXSxcbiAgb3B0aW9uczoge1xuICAgIGN3ZDogc3RyaW5nO1xuICAgIGZvbGxvd1N5bWxpbmtzOiBib29sZWFuO1xuICAgIGlnbm9yZU9wdGlvbnM/OiBGaWxlU2Nhbklnbm9yZU9wdGlvbnM7XG4gICAgcGVyc2lzdGVudD86IGJvb2xlYW47XG4gIH0sXG4pOiB2b2lkIHtcbiAgY29uc3QgYnVpbHRJbklnbm9yZSA9IGlnbm9yZVBhdGhNYXRjaGVyKG9wdGlvbnMuaWdub3JlT3B0aW9ucyk7XG4gIGNvbnN0IGNvbnRyb2xzID0gd2F0Y2goXCIuXCIsIHtcbiAgICBhdG9taWM6IGZhbHNlLFxuICAgIGN3ZDogb3B0aW9ucy5jd2QsXG4gICAgZm9sbG93U3ltbGlua3M6IG9wdGlvbnMuZm9sbG93U3ltbGlua3MsXG4gICAgaWdub3JlSW5pdGlhbDogdHJ1ZSxcbiAgICBwZXJzaXN0ZW50OiBvcHRpb25zLnBlcnNpc3RlbnQsXG4gICAgaWdub3JlZChwYXRoLCBzdGF0cykge1xuICAgICAgY29uc3QgcmVsYXRpdmVQYXRoID0gdG9Dd2RSZWxhdGl2ZVBhdGgocGF0aCwgb3B0aW9ucy5jd2QpO1xuICAgICAgaWYgKEdpdElnbm9yZS5pc0ZpbGUocmVsYXRpdmVQYXRoKSkgcmV0dXJuIGZhbHNlO1xuICAgICAgaWYgKGJ1aWx0SW5JZ25vcmUocmVsYXRpdmVQYXRoKSB8fCBidWlsdEluSWdub3JlKGAke3JlbGF0aXZlUGF0aH0vYCkpIHJldHVybiB0cnVlO1xuICAgICAgcmV0dXJuIHN0YXRzID8gIXN0YXRzLmlzRGlyZWN0b3J5KCkgOiBmYWxzZTtcbiAgICB9LFxuICB9KTtcbiAgY29uc3QgcmVsb2FkID0gKHBhdGg6IHN0cmluZyk6IHZvaWQgPT4ge1xuICAgIHZvaWQgcmVhZEZpbGUocmVzb2x2ZShvcHRpb25zLmN3ZCwgcGF0aCksIFwidXRmOFwiKVxuICAgICAgLnRoZW4oKGNvbnRlbnQpID0+IHtcbiAgICAgICAgZ2l0SWdub3JlLnNldChwYXRoLCBjb250ZW50KTtcbiAgICAgICAgd2F0Y2hlci5hZGQoWy4uLndhdGNoUGF0aHNdKTtcbiAgICAgICAgbG9nZ2VyLmRlYnVnKFwiLmdpdGlnbm9yZSB1cGRhdGVkXCIsIHsgcGF0aCB9KTtcbiAgICAgIH0pXG4gICAgICAuY2F0Y2goKGVycm9yKSA9PiB7XG4gICAgICAgIGxvZ2dlci53YXJuKFwiZmFpbGVkIHRvIHJlbG9hZCAuZ2l0aWdub3JlXCIsIHtcbiAgICAgICAgICBwYXRoLFxuICAgICAgICAgIGVycm9yOiBlcnJvclV0aWxzLnRvRXJyb3IoZXJyb3IpLFxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICB9O1xuICBjb250cm9scy5vbihcImFkZFwiLCByZWxvYWQpO1xuICBjb250cm9scy5vbihcImNoYW5nZVwiLCByZWxvYWQpO1xuICBjb250cm9scy5vbihcInVubGlua1wiLCAocGF0aCkgPT4ge1xuICAgIGdpdElnbm9yZS5kZWxldGUocGF0aCk7XG4gICAgd2F0Y2hlci5hZGQoWy4uLndhdGNoUGF0aHNdKTtcbiAgICBsb2dnZXIuZGVidWcoXCIuZ2l0aWdub3JlIHJlbW92ZWRcIiwgeyBwYXRoIH0pO1xuICB9KTtcbiAgY29udHJvbHMub24oXCJlcnJvclwiLCAoZXJyb3IpID0+IHdhdGNoZXIuZW1pdChcImVycm9yXCIsIGVycm9yKSk7XG4gIGNvbnN0IGNsb3NlID0gd2F0Y2hlci5jbG9zZS5iaW5kKHdhdGNoZXIpO1xuICB3YXRjaGVyLmNsb3NlID0gYXN5bmMgKCkgPT4ge1xuICAgIGF3YWl0IFByb21pc2UuYWxsKFtjbG9zZSgpLCBjb250cm9scy5jbG9zZSgpXSk7XG4gIH07XG59XG5cbi8qKiBBbGlnbnMgY2hva2lkYXIgcGF0aHMgd2l0aCB0aGUgY3dkLXJlbGF0aXZlIGZvcm0ge0BsaW5rIGZpbmRGaWxlc30gdXNlcy4gKi9cbmZ1bmN0aW9uIHRvQ3dkUmVsYXRpdmVQYXRoKHBhdGg6IHN0cmluZywgY3dkOiBzdHJpbmcpOiBzdHJpbmcge1xuICBpZiAoIWlzQWJzb2x1dGUocGF0aCkpIHJldHVybiBwYXRoO1xuICBjb25zdCByZWwgPSByZWxhdGl2ZShjd2QsIHBhdGgpO1xuICByZXR1cm4gcmVsID09PSBcIlwiID8gXCIuXCIgOiByZWw7XG59XG5cbi8vIE1hbnVhbCBkZW1vOiBydW4gdGhpcyBmaWxlIGRpcmVjdGx5IChgYnVuIHNyYy93YXRjaC50cyBbZGlyXWApIHRvIHdhdGNoIGEgdHJlZVxuLy8gYW5kIGxvZyBldmVyeSBmaWxlIGV2ZW50IGFzIGl0IGhhcHBlbnMuIERlZmF1bHRzIHRvIHRoaXMgcGFja2FnZS5cbmlmIChpbXBvcnQubWV0YS5tYWluKSB7XG4gIGNvbnN0IGN3ZCA9IHJlc29sdmUocHJvY2Vzcy5hcmd2WzJdID8/IHJlc29sdmUoaW1wb3J0Lm1ldGEuZGlybmFtZSwgXCIuLlwiKSk7XG4gIGNvbnNvbGUubG9nKFwiLS0tIFN0YXJ0aW5nIEZpbGUgV2F0Y2hlciAtLS1cIik7XG4gIGNvbnNvbGUubG9nKGBXb3JraW5nIERpcmVjdG9yeTogJHtjd2R9YCk7XG5cbiAgY29uc3Qgd2F0Y2hlciA9IGF3YWl0IHdhdGNoRmlsZXMoXCIqKi9zcmMvKipcIiwge1xuICAgIGN3ZCxcbiAgICBpZ25vcmVJbml0aWFsOiBmYWxzZSxcbiAgICBpZ25vcmU6IFtcIioqL2luZGV4LnRzXCIsIFwiKiovY29yZS8qKlwiXSxcbiAgICBpZ25vcmVPcHRpb25zOiB7XG4gICAgICB0ZW1wOiBmYWxzZSxcbiAgICB9LFxuICB9KTtcbiAgd2F0Y2hlci5vbihcImFsbFwiLCAoZXZlbnQ6IHN0cmluZywgcGF0aDogc3RyaW5nKSA9PiB7XG4gICAgY29uc29sZS5sb2coYFske2V2ZW50LnRvVXBwZXJDYXNlKCl9XSAke3BhdGh9YCk7XG4gIH0pO1xuICB3YXRjaGVyLm9uKFwiZXJyb3JcIiwgKGVycm9yKSA9PiB7XG4gICAgY29uc29sZS5lcnJvcihcIldhdGNoZXIgZXJyb3I6XCIsIGVycm9yKTtcbiAgfSk7XG4gIGNvbnNvbGUubG9nKFwiV2F0Y2hlciBzdGFydGVkXCIpO1xufVxuIl19
@@ -1 +1 @@
1
- {"root":["../src/find.ts","../src/ignore.ts","../src/match.ts","../src/pattern.ts","../src/scan.ts","../src/watch.ts","../index.ts"],"version":"5.9.3"}
1
+ {"root":["../src/find.ts","../src/git-ignore.ts","../src/ignore.ts","../src/match.ts","../src/pattern.ts","../src/scan.ts","../src/watch.ts","../index.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -23,10 +23,11 @@
23
23
  "typescript": "^5.9.3"
24
24
  },
25
25
  "dependencies": {
26
- "@dbx-tools/core": "^0.9.118",
27
- "@dbx-tools/shared-core": "^0.9.118",
26
+ "@dbx-tools/core": "^0.9.120",
27
+ "@dbx-tools/shared-core": "^0.9.120",
28
28
  "chokidar": "^4.0.3",
29
29
  "glob": "^13.0.6",
30
+ "ignore": "^7.0.12",
30
31
  "minimatch": "^10.2.5"
31
32
  },
32
33
  "main": "./lib/index.js",
@@ -44,6 +45,10 @@
44
45
  "types": "./lib/src/find.d.ts",
45
46
  "default": "./lib/src/find.js"
46
47
  },
48
+ "./git-ignore": {
49
+ "types": "./lib/src/git-ignore.d.ts",
50
+ "default": "./lib/src/git-ignore.js"
51
+ },
47
52
  "./ignore": {
48
53
  "types": "./lib/src/ignore.d.ts",
49
54
  "default": "./lib/src/ignore.js"
@@ -71,7 +76,7 @@
71
76
  "./package.json": "./package.json"
72
77
  }
73
78
  },
74
- "version": "0.9.118",
79
+ "version": "0.9.120",
75
80
  "types": "./lib/index.d.ts",
76
81
  "dbxToolsConfig": {
77
82
  "tags": [
@@ -88,6 +93,10 @@
88
93
  "types": "./lib/src/find.d.ts",
89
94
  "default": "./lib/src/find.js"
90
95
  },
96
+ "./git-ignore": {
97
+ "types": "./lib/src/git-ignore.d.ts",
98
+ "default": "./lib/src/git-ignore.js"
99
+ },
91
100
  "./ignore": {
92
101
  "types": "./lib/src/ignore.d.ts",
93
102
  "default": "./lib/src/ignore.js"
package/src/find.ts CHANGED
@@ -1,22 +1,29 @@
1
1
  /**
2
2
  * Repository-consistent file discovery over `glob`.
3
3
  *
4
- * Reuse {@link findFiles} when package discovery, generation, cleanup, or docs
5
- * tooling must honor the same ignore catalogue and symlink defaults as watchers.
6
- * Direct `glob` calls should be reserved for behavior that intentionally differs.
4
+ * Reuse {@link findFiles} or {@link findFilesSync} when package discovery,
5
+ * generation, cleanup, or docs tooling must honor the same ignore catalogue and
6
+ * symlink defaults as watchers. Direct `glob` calls should be reserved for
7
+ * behavior that intentionally differs.
7
8
  *
8
9
  * @module
9
10
  */
10
11
 
11
12
  import { object, type Sequence } from "@dbx-tools/shared-core";
12
- import { globIterateSync, IgnoreLike, type GlobOptionsWithFileTypesUnset } from "glob";
13
+ import {
14
+ globIterate,
15
+ globIterateSync,
16
+ IgnoreLike,
17
+ type GlobOptionsWithFileTypesUnset,
18
+ } from "glob";
19
+ import { GitIgnore } from "./git-ignore.ts";
13
20
  import { ignorePathMatcher } from "./ignore.ts";
14
21
  import { PathMatcher, PathMatchInput, pathMatchTests, toPathMatcher } from "./match.ts";
15
22
  import { FileScanIgnoreOptions, FileScanOptions, FOLLOW_SYMLINKS_DEFAULT } from "./scan.ts";
16
23
 
17
24
  type FileFindIgnore = PathMatchInput | readonly PathMatchInput[] | IgnoreLike;
18
25
 
19
- /** Glob, root, ignore, and symlink options for {@link findFiles}. */
26
+ /** Glob, root, ignore, and symlink options for {@link findFiles} and {@link findFilesSync}. */
20
27
  export interface FileFindOptions
21
28
  extends
22
29
  Omit<
@@ -33,17 +40,51 @@ export interface FileFindOptions
33
40
  * in which case only that predicate applies). The ignore list mirrors what
34
41
  * {@link watchFiles} feeds through its matchers.
35
42
  */
36
- export function findFiles(pattern: string | string[], options?: FileFindOptions): Sequence<string> {
37
- return object.sequence(globIterateSync(pattern, toGlobOptions(options)));
43
+ export async function* findFiles(
44
+ pattern: string | string[],
45
+ options?: FileFindOptions,
46
+ ): AsyncGenerator<string> {
47
+ yield* globIterate(pattern, await toGlobOptions(options));
38
48
  }
39
49
 
40
- function toGlobOptions(options: FileFindOptions | undefined): GlobOptionsWithFileTypesUnset {
50
+ /** Synchronous counterpart to {@link findFiles}. */
51
+ export function findFilesSync(
52
+ pattern: string | string[],
53
+ options?: FileFindOptions,
54
+ ): Sequence<string> {
55
+ return object.sequence(globIterateSync(pattern, toGlobOptionsSync(options)));
56
+ }
57
+
58
+ async function toGlobOptions(
59
+ options: FileFindOptions | undefined,
60
+ ): Promise<GlobOptionsWithFileTypesUnset> {
61
+ const { cwd, ignoreOptions, followSymlinks } = options ?? {};
62
+ const root = cwd ?? process.cwd();
63
+ const gitIgnore = ignoreOptions?.git
64
+ ? await GitIgnore.load(root, { ignoreOptions, followSymlinks })
65
+ : undefined;
66
+ return buildGlobOptions(options, gitIgnore);
67
+ }
68
+
69
+ function toGlobOptionsSync(options: FileFindOptions | undefined): GlobOptionsWithFileTypesUnset {
70
+ const { cwd, ignoreOptions, followSymlinks } = options ?? {};
71
+ const root = cwd ?? process.cwd();
72
+ const gitIgnore = ignoreOptions?.git
73
+ ? GitIgnore.loadSync(root, { ignoreOptions, followSymlinks })
74
+ : undefined;
75
+ return buildGlobOptions(options, gitIgnore);
76
+ }
77
+
78
+ function buildGlobOptions(
79
+ options: FileFindOptions | undefined,
80
+ gitIgnore: GitIgnore | undefined,
81
+ ): GlobOptionsWithFileTypesUnset {
41
82
  const { cwd, ignore, ignoreOptions, followSymlinks, ...restOptions } = options ?? {};
42
83
  return {
43
84
  ...restOptions,
44
85
  cwd: cwd ?? process.cwd(),
45
86
  follow: followSymlinks ?? FOLLOW_SYMLINKS_DEFAULT,
46
- ignore: normalizeIgnore(ignore, ignoreOptions),
87
+ ignore: normalizeIgnore(ignore, ignoreOptions, gitIgnore),
47
88
  };
48
89
  }
49
90
 
@@ -66,7 +107,7 @@ function isIgnoreLike(value: unknown): value is IgnoreLike {
66
107
  return false;
67
108
  }
68
109
  /**
69
- * Builds the {@link IgnoreLike} object {@link findFiles} hands to `glob`.
110
+ * Builds the {@link IgnoreLike} object file discovery hands to `glob`.
70
111
  *
71
112
  * A caller-supplied `IgnoreLike` (an object, not string/array) is consulted
72
113
  * first - its `ignored`/`childrenIgnored` win - after which the shared
@@ -78,6 +119,7 @@ function isIgnoreLike(value: unknown): value is IgnoreLike {
78
119
  function normalizeIgnore(
79
120
  ignore: FileFindIgnore | undefined,
80
121
  ignoreOptions: FileScanIgnoreOptions | undefined,
122
+ gitIgnore: GitIgnore | undefined,
81
123
  ): IgnoreLike {
82
124
  const ignoreLike: IgnoreLike | undefined = isIgnoreLike(ignore) ? ignore : undefined;
83
125
  const ignorePathMatcherInputs: PathMatchInput[] | undefined =
@@ -96,6 +138,7 @@ function normalizeIgnore(
96
138
  ignored(path) {
97
139
  if (ignoreLike?.ignored?.(path)) return true;
98
140
  const patheRelative = path.relative();
141
+ if (gitIgnore?.ignores(patheRelative, path.isDirectory())) return true;
99
142
  const ignore = ignoreMatcher(patheRelative);
100
143
  return ignore;
101
144
  },
@@ -103,6 +146,7 @@ function normalizeIgnore(
103
146
  if (ignoreLike?.childrenIgnored?.(path)) return true;
104
147
  const patheRelative = path.relative();
105
148
  if (patheRelative) {
149
+ if (gitIgnore?.ignores(patheRelative, true)) return true;
106
150
  if (ignoreMatcher(patheRelative)) {
107
151
  return true;
108
152
  } else if (ignoreMatcher(patheRelative + "/")) {
@@ -131,7 +175,7 @@ if (import.meta.main) {
131
175
  test: true,
132
176
  },
133
177
  });
134
- for (const file of files) {
178
+ for await (const file of files) {
135
179
  console.log(file);
136
180
  }
137
181
  const elapsed = performance.now() - startTime;
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Mutable, Git-compatible `.gitignore` path matching.
3
+ *
4
+ * Rule syntax is owned by the `ignore` package, whose compatibility suite
5
+ * compares behavior with `git check-ignore`. This module adds repository and
6
+ * nested-file scoping plus in-memory add, update, and removal.
7
+ *
8
+ * @module
9
+ */
10
+
11
+ import { readFileSync } from "node:fs";
12
+ import { readFile } from "node:fs/promises";
13
+ import { basename, dirname, isAbsolute, relative, resolve, sep } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+ import { glob, globSync, type IgnoreLike } from "glob";
16
+ import ignore from "ignore";
17
+ import type { IgnorePatternOptions } from "./ignore.ts";
18
+ import { ignorePathMatcher } from "./ignore.ts";
19
+ import { toPathMatcher, type PathMatcher } from "./match.ts";
20
+
21
+ interface GitIgnoreRuleSet {
22
+ content: string;
23
+ directory: string;
24
+ matcher: ReturnType<typeof ignore>;
25
+ path: string;
26
+ }
27
+
28
+ /** Options used while discovering `.gitignore` files beneath a root. */
29
+ export interface GitIgnoreLoadOptions {
30
+ /** Built-in directory groups pruned while looking for ignore files. */
31
+ ignoreOptions?: IgnorePatternOptions;
32
+ /** Follow symlinked directories during discovery. Defaults to false. */
33
+ followSymlinks?: boolean;
34
+ }
35
+
36
+ /** Mutable path predicate composed from root and nested `.gitignore` files. */
37
+ export class GitIgnore {
38
+ readonly root: string;
39
+ /** Stable composable matcher that observes later {@link set}, {@link delete}, and {@link clear} calls. */
40
+ readonly matcher: PathMatcher = toPathMatcher((path) => this.ignores(path));
41
+
42
+ private readonly files = new Map<string, GitIgnoreRuleSet>();
43
+ private rules: GitIgnoreRuleSet[] = [];
44
+
45
+ constructor(root: string | URL) {
46
+ this.root = resolve(root instanceof URL ? fileURLToPath(root) : root);
47
+ }
48
+
49
+ /** Load every applicable `.gitignore` beneath {@link root}. */
50
+ static async load(
51
+ root: string | URL,
52
+ options: GitIgnoreLoadOptions = {},
53
+ ): Promise<GitIgnore> {
54
+ const result = new GitIgnore(root);
55
+ for (const path of await glob("**/.gitignore", discoveryOptions(result.root, options))) {
56
+ result.set(path, await readFile(resolve(result.root, path), "utf8"));
57
+ }
58
+ return result;
59
+ }
60
+
61
+ /** Synchronous counterpart to {@link GitIgnore.load}. */
62
+ static loadSync(root: string | URL, options: GitIgnoreLoadOptions = {}): GitIgnore {
63
+ const result = new GitIgnore(root);
64
+ for (const path of globSync("**/.gitignore", discoveryOptions(result.root, options))) {
65
+ result.set(path, readFileSync(resolve(result.root, path), "utf8"));
66
+ }
67
+ return result;
68
+ }
69
+
70
+ /** Whether `path` names a `.gitignore` control file. */
71
+ static isFile(path: string): boolean {
72
+ return basename(path) === ".gitignore";
73
+ }
74
+
75
+ /** Add or replace one `.gitignore` file and its in-memory content. */
76
+ set(path: string, content: string): void {
77
+ const relativePath = this.relativePath(path);
78
+ if (!relativePath || !GitIgnore.isFile(relativePath)) {
79
+ throw new TypeError(`Git ignore path must name a .gitignore under ${this.root}: ${path}`);
80
+ }
81
+ this.files.set(relativePath, {
82
+ content,
83
+ directory: toPosix(dirname(relativePath)) === "." ? "" : toPosix(dirname(relativePath)),
84
+ matcher: ignore().add(content),
85
+ path: relativePath,
86
+ });
87
+ this.refreshRules();
88
+ }
89
+
90
+ /** Remove one `.gitignore` file. Returns whether it was registered. */
91
+ delete(path: string): boolean {
92
+ const relativePath = this.relativePath(path);
93
+ if (!relativePath) return false;
94
+ const deleted = this.files.delete(relativePath);
95
+ if (deleted) this.refreshRules();
96
+ return deleted;
97
+ }
98
+
99
+ /** Remove every registered `.gitignore`. */
100
+ clear(): void {
101
+ this.files.clear();
102
+ this.rules = [];
103
+ }
104
+
105
+ /** Return the retained content for a registered `.gitignore`. */
106
+ get(path: string): string | undefined {
107
+ const relativePath = this.relativePath(path);
108
+ return relativePath ? this.files.get(relativePath)?.content : undefined;
109
+ }
110
+
111
+ /** Whether a path is ignored after applying parent then nested rule files. */
112
+ ignores(path: string, directory: boolean = false): boolean {
113
+ const relativePath = this.relativePath(path);
114
+ if (!relativePath || relativePath === ".") return false;
115
+ let ignored = false;
116
+ for (const rules of this.rules) {
117
+ const scopedPath = pathWithin(rules.directory, relativePath);
118
+ if (scopedPath === undefined || scopedPath === "") continue;
119
+ if (ignoredParent(rules.matcher, scopedPath)) return true;
120
+ const result = rules.matcher.test(directory ? `${trimSlash(scopedPath)}/` : scopedPath);
121
+ if (result.ignored) ignored = true;
122
+ if (result.unignored) ignored = false;
123
+ }
124
+ return ignored;
125
+ }
126
+
127
+ private relativePath(path: string): string | undefined {
128
+ const absolute = isAbsolute(path) ? resolve(path) : resolve(this.root, path);
129
+ const relativePath = relative(this.root, absolute);
130
+ if (relativePath === "" || relativePath === ".") return ".";
131
+ if (relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
132
+ return undefined;
133
+ }
134
+ return toPosix(relativePath);
135
+ }
136
+
137
+ private refreshRules(): void {
138
+ this.rules = [...this.files.values()].sort(
139
+ (left, right) =>
140
+ pathDepth(left.directory) - pathDepth(right.directory) ||
141
+ left.path.localeCompare(right.path),
142
+ );
143
+ }
144
+ }
145
+
146
+ function discoveryOptions(
147
+ root: string,
148
+ options: GitIgnoreLoadOptions,
149
+ ): {
150
+ cwd: string;
151
+ dot: true;
152
+ follow: boolean;
153
+ ignore: IgnoreLike;
154
+ nodir: true;
155
+ } {
156
+ const matcher = ignorePathMatcher(options.ignoreOptions);
157
+ return {
158
+ cwd: root,
159
+ dot: true,
160
+ follow: options.followSymlinks ?? false,
161
+ ignore: {
162
+ ignored(path) {
163
+ const relativePath = path.relative();
164
+ return !GitIgnore.isFile(relativePath) && matcher(relativePath);
165
+ },
166
+ childrenIgnored(path) {
167
+ const relativePath = path.relative();
168
+ return matcher(relativePath) || matcher(`${relativePath}/`);
169
+ },
170
+ },
171
+ nodir: true,
172
+ };
173
+ }
174
+
175
+ function ignoredParent(matcher: ReturnType<typeof ignore>, path: string): boolean {
176
+ const segments = trimSlash(path).split("/");
177
+ const parentCount = segments.length - 1;
178
+ for (let index = 1; index <= parentCount; index++) {
179
+ if (matcher.test(`${segments.slice(0, index).join("/")}/`).ignored) return true;
180
+ }
181
+ return false;
182
+ }
183
+
184
+ function pathWithin(directory: string, path: string): string | undefined {
185
+ if (!directory) return path;
186
+ if (path === directory) return "";
187
+ return path.startsWith(`${directory}/`) ? path.slice(directory.length + 1) : undefined;
188
+ }
189
+
190
+ function pathDepth(path: string): number {
191
+ return path ? path.split("/").length : 0;
192
+ }
193
+
194
+ function trimSlash(path: string): string {
195
+ return path.replace(/^\/+|\/+$/g, "");
196
+ }
197
+
198
+ function toPosix(path: string): string {
199
+ return path.split(sep).join("/");
200
+ }
package/src/scan.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  * @module
9
9
  */
10
10
 
11
- import { IgnorePatternOptions } from "./ignore.ts";
11
+ import type { IgnorePatternOptions } from "./ignore.ts";
12
12
  import { PathMatchInput } from "./match.ts";
13
13
 
14
14
  /** Shared default used by file discovery and watching to avoid traversing symlinks. */
@@ -43,5 +43,11 @@ export interface FileScanOptions {
43
43
  signal?: AbortSignal;
44
44
  }
45
45
 
46
- /** Options controlling the generated ignore pattern list. */
47
- export type FileScanIgnoreOptions = IgnorePatternOptions;
46
+ /** Options controlling built-in and repository `.gitignore` matching. */
47
+ export interface FileScanIgnoreOptions extends IgnorePatternOptions {
48
+ /**
49
+ * Load root and nested `.gitignore` files using Git-compatible semantics.
50
+ * Defaults to false.
51
+ */
52
+ git?: boolean;
53
+ }