view-ignored 0.10.0 → 0.11.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.
Files changed (68) hide show
  1. package/README.md +118 -62
  2. package/out/browser.d.ts +1 -0
  3. package/out/browser.js +1 -0
  4. package/out/browser_scan.d.ts +1 -3
  5. package/out/browser_scan.js +11 -46
  6. package/out/browser_stream.d.ts +6 -1
  7. package/out/browser_stream.js +6 -1
  8. package/out/index.d.ts +1 -0
  9. package/out/index.js +1 -0
  10. package/out/patterns/extractor.d.ts +7 -7
  11. package/out/patterns/gitignore.d.ts +2 -2
  12. package/out/patterns/gitignore.js +14 -6
  13. package/out/patterns/ignores.d.ts +23 -8
  14. package/out/patterns/index.d.ts +1 -0
  15. package/out/patterns/index.js +1 -0
  16. package/out/patterns/init.d.ts +2 -2
  17. package/out/patterns/initState.d.ts +0 -7
  18. package/out/patterns/jsrjson.d.ts +2 -3
  19. package/out/patterns/jsrjson.js +25 -38
  20. package/out/patterns/matcherContext.d.ts +12 -9
  21. package/out/patterns/matcherContextPatch.js +158 -75
  22. package/out/patterns/matcherStream.d.ts +30 -11
  23. package/out/patterns/matcherStream.js +70 -25
  24. package/out/patterns/packagejson.d.ts +2 -2
  25. package/out/patterns/packagejson.js +11 -14
  26. package/out/patterns/patternCompile.js +48 -19
  27. package/out/patterns/patternList.d.ts +12 -6
  28. package/out/patterns/patternList.js +8 -4
  29. package/out/patterns/resolveSources.d.ts +22 -5
  30. package/out/patterns/resolveSources.js +153 -97
  31. package/out/patterns/resource.d.ts +16 -0
  32. package/out/patterns/resource.js +1 -0
  33. package/out/patterns/rule.d.ts +63 -9
  34. package/out/patterns/rule.js +101 -66
  35. package/out/patterns/source.d.ts +9 -17
  36. package/out/scan.d.ts +11 -3
  37. package/out/scan.js +16 -4
  38. package/out/scanCb.d.ts +16 -0
  39. package/out/scanCb.js +73 -0
  40. package/out/scanParallel.d.ts +18 -0
  41. package/out/scanParallel.js +146 -0
  42. package/out/stream.d.ts +6 -1
  43. package/out/stream.js +7 -2
  44. package/out/targets/bun.js +43 -36
  45. package/out/targets/deno.js +25 -23
  46. package/out/targets/git.js +3 -3
  47. package/out/targets/jsr.js +25 -23
  48. package/out/targets/jsrManifest.d.ts +8 -7
  49. package/out/targets/jsrManifest.js +40 -9
  50. package/out/targets/npm.js +30 -23
  51. package/out/targets/npmManifest.d.ts +18 -46
  52. package/out/targets/npmManifest.js +96 -23
  53. package/out/targets/target.d.ts +8 -16
  54. package/out/targets/vsce.js +20 -27
  55. package/out/targets/vsceManifest.d.ts +7 -0
  56. package/out/targets/vsceManifest.js +18 -0
  57. package/out/targets/yarn.js +48 -39
  58. package/out/targets/yarnClassic.js +20 -18
  59. package/out/types.d.ts +8 -7
  60. package/out/unixify.d.ts +1 -1
  61. package/out/unixify.js +40 -21
  62. package/out/walk.d.ts +43 -4
  63. package/out/walk.js +146 -84
  64. package/package.json +27 -23
  65. package/out/getDepth.d.ts +0 -4
  66. package/out/getDepth.js +0 -21
  67. package/out/opendir.d.ts +0 -3
  68. package/out/opendir.js +0 -22
@@ -1,26 +1,16 @@
1
- import { type } from "arktype";
2
1
  import stripJsonComments from "strip-json-comments";
3
2
  import { ruleCompile } from "./resolveSources.js";
4
3
  import { resolveNegatable } from "./source.js";
5
- const jsrManifest = type({
6
- exclude: "string[]?",
7
- include: "string[]?",
8
- "publish?": {
9
- exclude: "string[]?",
10
- include: "string[]?",
11
- },
12
- });
13
- const parse = type("string")
14
- .pipe((s) => JSON.parse(s))
15
- .pipe(jsrManifest);
16
4
  /**
17
5
  * Extracts and compiles patterns from the file.
18
6
  *
19
7
  * @since 0.6.0
20
8
  */
21
- export function extractJsrJson(source, content, ctx) {
22
- extract(source, content, ctx);
23
- for (const element of source.pattern) {
9
+ export function extractJsrJson(source, content) {
10
+ const result = extract(source, content);
11
+ if (result instanceof Error)
12
+ return result;
13
+ for (const element of source.rules) {
24
14
  ruleCompile(element);
25
15
  }
26
16
  }
@@ -32,39 +22,36 @@ extractJsrJson;
32
22
  *
33
23
  * @since 0.6.0
34
24
  */
35
- export function extractJsrJsonc(source, content, ctx) {
36
- extractJsrJson(source, Buffer.from(stripJsonComments(content.toString())), ctx);
25
+ export function extractJsrJsonc(source, content) {
26
+ return extractJsrJson(source, Buffer.from(stripJsonComments(content.toString())));
37
27
  }
38
28
  extractJsrJsonc;
39
- function extract(source, content, ctx) {
40
- const dist = parse(content.toString());
41
- const include = { compiled: null, excludes: false, pattern: [] };
42
- const exclude = { compiled: null, excludes: true, pattern: [] };
43
- if (dist instanceof type.errors) {
44
- source.error = new Error("Invalid '" + source.path + "': " + dist.summary, { cause: dist });
45
- ctx.failed.push(source);
46
- return;
29
+ function extract(source, content) {
30
+ let dist;
31
+ try {
32
+ dist = JSON.parse(content.toString());
47
33
  }
48
- if (!dist.publish) {
49
- if (dist.exclude) {
50
- exclude.pattern.push(...dist.exclude);
51
- }
34
+ catch (e) {
35
+ return new Error("Invalid JSON in " + source.path, { cause: e });
52
36
  }
53
- else if (dist.publish.exclude) {
54
- exclude.pattern.push(...dist.publish.exclude);
37
+ // Basic runtime check to ensure dist is an object
38
+ if (!dist || typeof dist !== "object" || Array.isArray(dist)) {
39
+ return new Error("Invalid " + source.path + ": Root must be an object");
55
40
  }
56
- if (!dist.publish) {
57
- if (dist.include) {
58
- include.pattern.push(...dist.include);
59
- }
41
+ const include = { compiled: null, excludes: false, pattern: [] };
42
+ const exclude = { compiled: null, excludes: true, pattern: [] };
43
+ // Resolve patterns based on the manifest hierarchy
44
+ const target = dist.publish ?? dist;
45
+ if (target.exclude && Array.isArray(target.exclude)) {
46
+ exclude.pattern.push(...target.exclude);
60
47
  }
61
- else if (dist.publish.include) {
62
- include.pattern.push(...dist.publish.include);
48
+ if (target.include && Array.isArray(target.include)) {
49
+ include.pattern.push(...target.include);
63
50
  }
64
51
  for (const si of [include, exclude]) {
65
52
  for (const pattern of si.pattern) {
66
53
  resolveNegatable(pattern, true, include, exclude);
67
54
  }
68
55
  }
69
- source.pattern.push(include, exclude);
56
+ source.rules.push(include, exclude);
70
57
  }
@@ -1,5 +1,5 @@
1
+ import type { Resource, InvalidSource } from "./resource.js";
1
2
  import type { RuleMatch } from "./rule.js";
2
- import type { Source } from "./source.js";
3
3
  /**
4
4
  * Post-scan results.
5
5
  *
@@ -22,16 +22,16 @@ export interface MatcherContext {
22
22
  *
23
23
  * @since 0.6.0
24
24
  */
25
- external: Map<string, Source | "none">;
25
+ external: Map<string, Resource>;
26
26
  /**
27
27
  * If any fatal errors were encountered during source extractions,
28
28
  * this property will contain an array of failed sources.
29
29
  *
30
30
  * @since 0.6.0
31
31
  */
32
- failed: Source[];
32
+ failed: InvalidSource[];
33
33
  /**
34
- * Maps directory paths to the quantity of files they contain.
34
+ * Total number of files and directories scanned.
35
35
  *
36
36
  * @example
37
37
  * // for
@@ -41,15 +41,18 @@ export interface MatcherContext {
41
41
  * "src/views/index.html"
42
42
  *
43
43
  * // depth: 0
44
- * "src" => 1
44
+ * "." => { totalFiles: 1, totalMatchedFiles: 1, totalDirs: 3 }
45
+ * "src" => { totalFiles: 1, totalMatchedFiles: 1, totalDirs: 2 }
45
46
  *
46
47
  * // depth: 1
47
- * "src/components" => 0
48
- * "src/views" => 1
48
+ * "src/components" => { totalFiles: 0, totalMatchedFiles: 0, totalDirs: 0 }
49
+ * "src/views" => { totalFiles: 1, totalMatchedFiles: 1, totalDirs: 0 }
49
50
  *
50
- * @since 0.6.0
51
+ * @since 0.11.0
51
52
  */
52
- depthPaths: Map<string, number>;
53
+ total: Map<string, Total>;
54
+ }
55
+ export interface Total {
53
56
  /**
54
57
  * Total number of files scanned.
55
58
  *
@@ -1,8 +1,16 @@
1
- import { dirname } from "node:path";
2
- import { getDepth } from "../getDepth.js";
3
- import { opendir } from "../opendir.js";
4
- import { unixify, join } from "../unixify.js";
5
- import { walkIncludes } from "../walk.js";
1
+ import { scanParallel } from "../scanParallel.js";
2
+ import { dirname } from "../unixify.js";
3
+ import { walkPatchResult, walkPatchTotal, propagateTotals, } from "../walk.js";
4
+ import { resolveSources } from "./resolveSources.js";
5
+ function promiseCb(resolve, reject) {
6
+ return (err, res) => {
7
+ if (err) {
8
+ reject(err);
9
+ return;
10
+ }
11
+ resolve(res);
12
+ };
13
+ }
6
14
  /**
7
15
  * Provides patching abilities for the given {@link MatcherContext}.
8
16
  * Directories should have the slash suffix.
@@ -13,45 +21,110 @@ export async function matcherContextAddPath(ctx, options, entry) {
13
21
  if (ctx.paths.has(entry)) {
14
22
  return false;
15
23
  }
16
- const { target, fs, cwd, signal } = options;
17
24
  const isDir = entry.endsWith("/");
25
+ const direntPath = isDir ? entry.slice(0, -1) : entry;
26
+ if (isDir && direntPath === ".") {
27
+ return true;
28
+ }
29
+ const parentPath = dirname(direntPath);
30
+ const { target, fs, cwd, signal, depth: maxDepth } = options;
18
31
  if (isDir) {
19
32
  // recursive parent population
20
- const direntPath = entry.replace(/\/$/, "");
21
- if (direntPath === ".") {
22
- return true;
33
+ const resource = await new Promise((resolve, reject) => {
34
+ resolveSources({
35
+ cwd,
36
+ dir: direntPath,
37
+ external: ctx.external,
38
+ fs,
39
+ signal,
40
+ target,
41
+ }, promiseCb(resolve, reject));
42
+ });
43
+ ctx.paths.set(entry, await new Promise((resolve, reject) => {
44
+ target.ignores({
45
+ cwd,
46
+ entry: direntPath,
47
+ fs,
48
+ parentPath,
49
+ resource,
50
+ signal,
51
+ target,
52
+ }, promiseCb(resolve, reject));
53
+ }));
54
+ const total = ctx.total.get(parentPath);
55
+ if (!total) {
56
+ ctx.total.set(parentPath, { totalDirs: 1, totalFiles: 0, totalMatchedFiles: 0 });
23
57
  }
24
- ctx.paths.set(entry, await target.ignores({ fs, cwd, entry: direntPath, ctx, signal, target }));
25
- if (ctx.totalFiles >= 0) {
26
- ctx.totalDirs++;
58
+ else if (total.totalFiles >= 0) {
59
+ total.totalDirs++;
27
60
  }
28
- const parent = dirname(direntPath);
29
- if (parent !== ".") {
30
- void (await matcherContextAddPath(ctx, options, parent + "/"));
61
+ if (parentPath !== ".") {
62
+ void (await matcherContextAddPath(ctx, options, parentPath + "/"));
31
63
  }
32
64
  return true;
33
65
  }
34
- const parent = dirname(entry);
35
66
  const isSource = target.extractors.some((e) => e.path === entry);
36
67
  if (isSource) {
37
68
  // add pattern sources
38
- await matcherContextRemovePath(ctx, options, parent + "/");
39
- await rescan(ctx, { ...options, within: parent });
69
+ const resultPromise = new Promise((resolve, reject) => {
70
+ scanParallel({
71
+ external: ctx.external,
72
+ failed: ctx.failed,
73
+ onResult: (result) => {
74
+ if ("dir" in result) {
75
+ walkPatchTotal(ctx, maxDepth, result);
76
+ }
77
+ else {
78
+ walkPatchResult(ctx, result);
79
+ }
80
+ },
81
+ scanOptions: options,
82
+ stream: undefined,
83
+ within: parentPath,
84
+ }, promiseCb(resolve, reject));
85
+ });
86
+ await matcherContextRemovePath(ctx, options, parentPath + "/");
87
+ await resultPromise;
88
+ propagateTotals(ctx.total);
40
89
  }
41
90
  // add paths
42
91
  // 1. recursively populate parents
43
- await matcherContextAddPath(ctx, options, parent + "/");
92
+ await matcherContextAddPath(ctx, options, parentPath + "/");
44
93
  // 2. if ignored, remove, otherwise add
45
- const match = await target.ignores({ fs, cwd, entry, ctx, signal, target });
94
+ const resource = (await new Promise((resolve, reject) => {
95
+ resolveSources({
96
+ cwd,
97
+ dir: parentPath,
98
+ external: ctx.external,
99
+ fs,
100
+ signal,
101
+ target,
102
+ }, promiseCb(resolve, reject));
103
+ }));
104
+ const match = await new Promise((resolve, reject) => {
105
+ target.ignores({
106
+ cwd,
107
+ entry,
108
+ fs,
109
+ parentPath,
110
+ resource,
111
+ signal,
112
+ target,
113
+ }, promiseCb(resolve, reject));
114
+ });
46
115
  if (match.ignored) {
47
116
  // 2.1. remove
48
117
  await matcherContextRemovePath(ctx, options, entry);
49
118
  return false;
50
119
  }
51
120
  // 2.2. add
52
- if (ctx.totalFiles >= 0) {
53
- ctx.totalFiles++;
54
- ctx.totalMatchedFiles++;
121
+ const total = ctx.total.get(parentPath);
122
+ if (!total) {
123
+ ctx.total.set(parentPath, { totalDirs: 0, totalFiles: 1, totalMatchedFiles: 1 });
124
+ }
125
+ else {
126
+ total.totalFiles++;
127
+ total.totalMatchedFiles++;
55
128
  }
56
129
  ctx.paths.set(entry, match);
57
130
  return true;
@@ -64,93 +137,103 @@ export async function matcherContextAddPath(ctx, options, entry) {
64
137
  */
65
138
  export async function matcherContextRemovePath(ctx, options, entry) {
66
139
  const isDir = entry.endsWith("/");
140
+ const direntPath = isDir ? entry.slice(0, -1) : entry;
141
+ if (isDir && direntPath === ".") {
142
+ ctx.paths.clear();
143
+ ctx.external.clear();
144
+ ctx.failed.length = 0;
145
+ ctx.total.set(direntPath, { totalDirs: 0, totalFiles: 0, totalMatchedFiles: 0 });
146
+ return true;
147
+ }
148
+ const parentPath = dirname(direntPath);
149
+ const parentPathDir = parentPath + "/";
67
150
  if (isDir) {
68
151
  // remove directories
69
- const direntPath = entry.replace(/\/$/, "");
152
+ let deletedDirs = 0, deletedFiles = 0;
153
+ const total = ctx.total.get(direntPath);
70
154
  for (const [element] of ctx.paths) {
71
- if (entry !== "./" && !element.startsWith(entry)) {
155
+ if (!element.startsWith(entry)) {
72
156
  continue;
73
157
  }
74
- if (ctx.totalFiles >= 0) {
158
+ if (total && total.totalFiles >= 0) {
75
159
  const isDir = element.endsWith("/");
76
160
  if (isDir) {
77
- ctx.totalDirs--;
161
+ deletedDirs++;
78
162
  }
79
163
  else {
80
- ctx.totalFiles--;
81
- ctx.totalMatchedFiles--;
164
+ deletedFiles++;
82
165
  }
83
166
  }
84
167
  ctx.paths.delete(element);
85
168
  }
86
- for (const [element] of ctx.depthPaths) {
87
- if (entry !== "./" && !element.startsWith(direntPath)) {
169
+ deleteTotals(ctx, entry, deletedDirs, deletedFiles);
170
+ for (const [element] of ctx.external) {
171
+ if (!element.startsWith(direntPath)) {
88
172
  continue;
89
173
  }
90
- ctx.depthPaths.delete(element);
91
- }
92
- for (const [element] of ctx.external) {
93
- if (entry !== "./" && !element.startsWith(direntPath)) {
174
+ if (!ctx.external.delete(element) || !ctx.failed.length) {
94
175
  continue;
95
176
  }
96
- if (ctx.external.delete(element) && ctx.failed.length) {
97
- // 3.1. remove failed sources
98
- const failedEntryIndex = ctx.failed.findIndex((fail) => dirname(fail.path) === element);
99
- if (failedEntryIndex >= 0) {
100
- ctx.failed.splice(failedEntryIndex, 1);
101
- }
177
+ // 3.1. remove failed sources
178
+ const failedEntryIndex = ctx.failed.findIndex((fail) => dirname(fail.source.path) === element);
179
+ if (failedEntryIndex >= 0) {
180
+ ctx.failed.splice(failedEntryIndex, 1);
102
181
  }
103
182
  }
104
183
  return true;
105
184
  }
106
- const parent = dirname(entry);
107
185
  const isSource = options.target.extractors.some((e) => e.path === entry);
108
186
  if (isSource) {
187
+ const maxDepth = options.depth;
109
188
  // remove pattern sources
110
189
  // rescan directory and repopulate stats
111
- await matcherContextRemovePath(ctx, options, parent + "/");
112
- await rescan(ctx, { ...options, within: parent });
190
+ const resultPromise = new Promise((resolve, reject) => {
191
+ scanParallel({
192
+ external: ctx.external,
193
+ failed: ctx.failed,
194
+ onResult: (result) => {
195
+ if ("dir" in result) {
196
+ walkPatchTotal(ctx, maxDepth, result);
197
+ }
198
+ else {
199
+ walkPatchResult(ctx, result);
200
+ }
201
+ },
202
+ scanOptions: options,
203
+ stream: undefined,
204
+ within: parentPath,
205
+ }, promiseCb(resolve, reject));
206
+ });
207
+ await matcherContextRemovePath(ctx, options, parentPathDir);
208
+ await resultPromise;
209
+ propagateTotals(ctx.total);
113
210
  return true;
114
211
  }
115
212
  // remove path
116
213
  // 1. change stats
117
214
  {
118
- if (ctx.totalFiles >= 0) {
119
- ctx.totalFiles--;
120
- ctx.totalMatchedFiles--;
215
+ const total = ctx.total.get(parentPath);
216
+ if (!total) {
217
+ ctx.total.set(parentPath, { totalDirs: 0, totalFiles: 0, totalMatchedFiles: 0 });
121
218
  }
122
- // 1.1 remove depthPaths
123
- const { depthSlash } = getDepth(entry, options.depth);
124
- if (depthSlash >= 0) {
125
- const dir = entry.substring(0, depthSlash);
126
- let num = ctx.depthPaths.get(dir);
127
- if (num) {
128
- num--;
129
- if (num <= 0) {
130
- ctx.depthPaths.delete(dir);
131
- }
132
- else {
133
- ctx.depthPaths.set(dir, num);
134
- }
135
- }
219
+ else if (total.totalFiles >= 0) {
220
+ total.totalFiles--;
221
+ total.totalMatchedFiles--;
136
222
  }
137
223
  }
138
224
  // 2. remove from paths
139
225
  ctx.paths.delete(entry);
140
226
  return true;
141
227
  }
142
- async function rescan(ctx, options) {
143
- const normalCwd = unixify(options.cwd);
144
- let from = join(normalCwd, options.within);
145
- await opendir(options.fs, from, (entry, from) => {
146
- const path = from.substring(normalCwd.length + 1);
147
- return walkIncludes({
148
- path,
149
- entry,
150
- ctx,
151
- stream: undefined,
152
- scanOptions: { ...options, cwd: normalCwd },
153
- });
154
- });
155
- ctx.totalDirs = ctx.totalFiles = ctx.totalMatchedFiles = -1;
228
+ function deleteTotals(ctx, entry, deletedDirs = 0, deletedFiles = 0) {
229
+ if (entry.endsWith("/"))
230
+ ctx.total.delete(entry);
231
+ for (let parent = dirname(entry); parent !== "./"; parent = dirname(parent) + "/") {
232
+ const total = ctx.total.get(parent);
233
+ if (!total)
234
+ continue;
235
+ total.totalDirs -= deletedDirs;
236
+ total.totalFiles -= deletedFiles;
237
+ total.totalMatchedFiles -= deletedFiles;
238
+ }
156
239
  }
@@ -1,5 +1,4 @@
1
1
  import type { Dirent } from "node:fs";
2
- import { EventEmitter } from "node:events";
3
2
  import type { MatcherContext } from "../patterns/matcherContext.js";
4
3
  import type { ScanOptions, FsAdapter } from "../types.js";
5
4
  import type { RuleMatch } from "./rule.js";
@@ -27,12 +26,6 @@ export type EntryInfo = {
27
26
  * @since 0.6.0
28
27
  */
29
28
  match: RuleMatch;
30
- /**
31
- * The matcher context.
32
- *
33
- * @since 0.6.0
34
- */
35
- ctx: MatcherContext;
36
29
  };
37
30
  /**
38
31
  * @see {@link MatcherStream} uses it for the "dirent" event.
@@ -52,16 +45,32 @@ export type EndListener = (ctx: MatcherContext) => void;
52
45
  * @since 0.6.0
53
46
  */
54
47
  export type EventMap = {
55
- dirent: [EntryInfo];
56
- end: [MatcherContext];
48
+ dirent: CustomEvent<EntryInfo>;
49
+ end: CustomEvent<MatcherContext>;
57
50
  };
51
+ /**
52
+ * @see {@link MatcherStream} uses it for its event map.
53
+ *
54
+ * @since 0.11.0
55
+ */
56
+ interface EventListener<K extends keyof EventMap> {
57
+ (evt: EventMap[K]): void;
58
+ }
59
+ /**
60
+ * @see {@link MatcherStream} uses it for its event map.
61
+ *
62
+ * @since 0.11.0
63
+ */
64
+ interface EventListenerObject<K extends keyof EventMap> {
65
+ handleEvent(object: EventMap[K]): void;
66
+ }
58
67
  /**
59
68
  * Event emitter.
60
- * @extends EventEmitter
69
+ * @augments EventTarget
61
70
  *
62
71
  * @since 0.6.0
63
72
  */
64
- export declare class MatcherStream extends EventEmitter<EventMap> {
73
+ export declare class MatcherStream extends EventTarget {
65
74
  #private;
66
75
  constructor(options: ScanOptions & {
67
76
  fs: FsAdapter;
@@ -70,10 +79,20 @@ export declare class MatcherStream extends EventEmitter<EventMap> {
70
79
  } & {
71
80
  captureRejections?: boolean;
72
81
  });
82
+ addEventListener<K extends keyof EventMap>(type: K, callback: EventListenerObject<K> | EventListener<K>, options?: boolean | AddEventListenerOptions): void;
83
+ removeEventListener<K extends keyof EventMap>(type: K, callback: EventListenerObject<K> | EventListener<K>, options?: boolean | EventListenerOptions): void;
84
+ dispatchEvent(event: EventMap[keyof EventMap]): boolean;
73
85
  /**
74
86
  * Resolves when everything is scanned.
75
87
  *
76
88
  * @since 0.8.0
77
89
  */
78
90
  start(): Promise<void>;
91
+ /**
92
+ * Resolves when everything is scanned. (Callback version)
93
+ *
94
+ * @since 0.11.0
95
+ */
96
+ startCb(cb: (err: Error | null, ctx: MatcherContext) => void): void;
79
97
  }
98
+ export {};
@@ -1,18 +1,17 @@
1
- import { EventEmitter } from "node:events";
2
- import { opendir } from "../opendir.js";
3
- import { join, unixify } from "../unixify.js";
4
- import { walkIncludes } from "../walk.js";
1
+ import { scanParallel } from "../scanParallel.js";
2
+ import { unixify } from "../unixify.js";
3
+ import { walkPatchResult, walkPatchTotal, propagateTotals } from "../walk.js";
5
4
  /**
6
5
  * Event emitter.
7
- * @extends EventEmitter
6
+ * @augments EventTarget
8
7
  *
9
8
  * @since 0.6.0
10
9
  */
11
- export class MatcherStream extends EventEmitter {
10
+ export class MatcherStream extends EventTarget {
12
11
  #timeout;
13
12
  #options;
14
13
  constructor(options) {
15
- super({ captureRejections: options.captureRejections });
14
+ super();
16
15
  this.#options = options;
17
16
  if (!options.noTimeout) {
18
17
  this.#timeout = setTimeout(() => {
@@ -20,27 +19,48 @@ export class MatcherStream extends EventEmitter {
20
19
  }, 5e3);
21
20
  }
22
21
  }
22
+ addEventListener(type, callback, options) {
23
+ super.addEventListener(type, callback, options);
24
+ }
25
+ removeEventListener(type, callback, options) {
26
+ super.removeEventListener(type, callback, options);
27
+ }
28
+ dispatchEvent(event) {
29
+ return super.dispatchEvent(event);
30
+ }
23
31
  /**
24
32
  * Resolves when everything is scanned.
25
33
  *
26
34
  * @since 0.8.0
27
35
  */
28
- async start() {
36
+ start() {
37
+ const { promise, resolve, reject } = Promise.withResolvers();
38
+ this.startCb((err) => {
39
+ if (err) {
40
+ reject(err);
41
+ return;
42
+ }
43
+ resolve();
44
+ });
45
+ return promise;
46
+ }
47
+ /**
48
+ * Resolves when everything is scanned. (Callback version)
49
+ *
50
+ * @since 0.11.0
51
+ */
52
+ startCb(cb) {
29
53
  clearTimeout(this.#timeout);
30
54
  const { target, cwd, within = ".", invert = false, depth: maxDepth = Infinity, signal = null, fastDepth = false, fastInternal = false, fs, } = this.#options;
31
55
  const ctx = {
32
- paths: new Map(),
33
56
  external: new Map(),
34
57
  failed: [],
35
- depthPaths: new Map(),
36
- totalFiles: 0,
37
- totalMatchedFiles: 0,
38
- totalDirs: 0,
58
+ paths: new Map(),
59
+ total: new Map([[".", { totalDirs: 0, totalFiles: 0, totalMatchedFiles: 0 }]]),
39
60
  };
40
61
  const normalCwd = unixify(cwd);
41
62
  const scanOptions = {
42
63
  cwd: normalCwd,
43
- within,
44
64
  depth: maxDepth,
45
65
  fastDepth,
46
66
  fastInternal,
@@ -48,19 +68,44 @@ export class MatcherStream extends EventEmitter {
48
68
  invert,
49
69
  signal,
50
70
  target,
71
+ within,
51
72
  };
52
- await target.init?.({ ctx, cwd, fs, signal, target });
53
- let from = join(normalCwd, within);
54
- await opendir(fs, from, (entry, from) => {
55
- const path = from.substring(normalCwd.length + 1);
56
- return walkIncludes({
57
- path,
58
- entry,
59
- ctx,
60
- stream: this,
73
+ const startScan = () => {
74
+ scanParallel({
75
+ external: ctx.external,
76
+ failed: ctx.failed,
77
+ onResult: (result) => {
78
+ if ("dir" in result) {
79
+ walkPatchTotal(ctx, scanOptions.depth, result);
80
+ }
81
+ else {
82
+ walkPatchResult(ctx, result);
83
+ }
84
+ },
61
85
  scanOptions,
86
+ stream: this,
87
+ within,
88
+ }, (err) => {
89
+ if (err) {
90
+ cb(err, null);
91
+ return;
92
+ }
93
+ propagateTotals(ctx.total);
94
+ cb(null, ctx);
95
+ this.dispatchEvent(new CustomEvent("end", { detail: ctx }));
62
96
  });
63
- });
64
- this.emit("end", ctx);
97
+ };
98
+ if (target.init) {
99
+ target.init({ cwd: normalCwd, fs, signal, target }, (err) => {
100
+ if (err) {
101
+ cb(err, null);
102
+ return;
103
+ }
104
+ startScan();
105
+ });
106
+ }
107
+ else {
108
+ startScan();
109
+ }
65
110
  }
66
111
  }
@@ -6,7 +6,7 @@ import { type Source } from "./source.js";
6
6
  *
7
7
  * @since 0.6.0
8
8
  */
9
- export declare function extractPackageJson(source: Source, content: Buffer): void | "none";
9
+ export declare function extractPackageJson(source: Source, content: Buffer): void | null | Error;
10
10
  /**
11
11
  * Extracts and compiles patterns from the file.
12
12
  *
@@ -14,4 +14,4 @@ export declare function extractPackageJson(source: Source, content: Buffer): voi
14
14
  *
15
15
  * @since 0.8.0
16
16
  */
17
- export declare function extractPackageJsonNocase(source: Source, content: Buffer): void | "none";
17
+ export declare function extractPackageJsonNocase(source: Source, content: Buffer): void | null | Error;