@gjsify/fs 0.4.0 → 0.4.4

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 (50) hide show
  1. package/package.json +51 -48
  2. package/src/callback.spec.ts +0 -296
  3. package/src/callback.ts +0 -684
  4. package/src/cp.spec.ts +0 -181
  5. package/src/cp.ts +0 -328
  6. package/src/dir.spec.ts +0 -204
  7. package/src/dir.ts +0 -199
  8. package/src/dirent.ts +0 -165
  9. package/src/encoding.ts +0 -45
  10. package/src/errors.spec.ts +0 -389
  11. package/src/errors.ts +0 -19
  12. package/src/extended.spec.ts +0 -706
  13. package/src/fd-ops.spec.ts +0 -234
  14. package/src/fd-ops.ts +0 -251
  15. package/src/file-handle.spec.ts +0 -115
  16. package/src/file-handle.ts +0 -856
  17. package/src/fs-watcher.ts +0 -198
  18. package/src/glob.spec.ts +0 -201
  19. package/src/glob.ts +0 -205
  20. package/src/index.ts +0 -313
  21. package/src/new-apis.spec.ts +0 -505
  22. package/src/promises.spec.ts +0 -812
  23. package/src/promises.ts +0 -686
  24. package/src/read-stream.ts +0 -128
  25. package/src/stat-watcher.ts +0 -116
  26. package/src/stat.spec.ts +0 -87
  27. package/src/statfs.spec.ts +0 -67
  28. package/src/statfs.ts +0 -92
  29. package/src/stats.ts +0 -207
  30. package/src/streams.spec.ts +0 -513
  31. package/src/symlink.spec.ts +0 -188
  32. package/src/sync.spec.ts +0 -377
  33. package/src/sync.ts +0 -568
  34. package/src/test.mts +0 -27
  35. package/src/types/encoding-option.ts +0 -3
  36. package/src/types/file-read-options.ts +0 -15
  37. package/src/types/file-read-result.ts +0 -4
  38. package/src/types/flag-and-open-mode.ts +0 -6
  39. package/src/types/index.ts +0 -6
  40. package/src/types/open-flags.ts +0 -14
  41. package/src/types/read-options.ts +0 -9
  42. package/src/utils.ts +0 -31
  43. package/src/utimes.spec.ts +0 -113
  44. package/src/utimes.ts +0 -97
  45. package/src/watch.spec.ts +0 -171
  46. package/src/watchfile.spec.ts +0 -185
  47. package/src/write-stream.ts +0 -142
  48. package/test/file.txt +0 -1
  49. package/tsconfig.json +0 -29
  50. package/tsconfig.tsbuildinfo +0 -1
package/src/fs-watcher.ts DELETED
@@ -1,198 +0,0 @@
1
- // Reference: Node.js lib/internal/fs/watchers.js
2
- // Reimplemented for GJS using Gio.FileMonitor
3
-
4
- import GLib from '@girs/glib-2.0';
5
- import Gio from '@girs/gio-2.0';
6
- import { EventEmitter } from 'node:events';
7
- import { normalizePath } from './utils.js';
8
- const privates = new WeakMap;
9
-
10
- import type { FSWatcher as IFSWatcher, PathLike, WatchOptions } from 'node:fs';
11
-
12
- export class FSWatcher extends EventEmitter implements IFSWatcher {
13
-
14
- constructor(filename: PathLike, options, listener) {
15
- super();
16
- if (!options || typeof options !== 'object')
17
- options = {persistent: true};
18
-
19
- const persistent = options.persistent !== false;
20
- const cancellable = Gio.Cancellable.new();
21
- const pathStr = normalizePath(filename);
22
- const file = Gio.File.new_for_path(pathStr);
23
- const watcher = file.monitor(Gio.FileMonitorFlags.NONE, cancellable);
24
- watcher.connect('changed', changed.bind(this));
25
-
26
- // When persistent is true, acquire a reference on the default GLib main context
27
- // so the main loop stays alive while this watcher is active.
28
- // This mirrors Node.js behavior where persistent watchers keep the event loop alive.
29
- let sourceId: number | null = null;
30
- if (persistent) {
31
- // Add a never-firing timeout source to keep the mainloop alive.
32
- // This is a lightweight way to hold a ref on the main context.
33
- sourceId = (GLib.timeout_add as unknown as (priority: number, interval: number, fn: () => boolean) => number)(
34
- GLib.PRIORITY_LOW,
35
- 2147483647,
36
- () => GLib.SOURCE_CONTINUE,
37
- );
38
- }
39
-
40
- privates.set(this, {
41
- persistent,
42
- cancellable,
43
- sourceId,
44
- // even if never used later on, the monitor needs to be
45
- // attached to this instance or GJS reference counter
46
- // will ignore it and no watch will ever happen
47
- watcher
48
- });
49
- if (listener) this.on('change', listener);
50
- }
51
-
52
- close() {
53
- const priv = privates.get(this);
54
- if (!priv.cancellable.is_cancelled()) {
55
- priv.cancellable.cancel();
56
- if (priv.sourceId !== null) {
57
- GLib.source_remove(priv.sourceId);
58
- priv.sourceId = null;
59
- }
60
- }
61
- }
62
-
63
- /**
64
- * When called, requests that the Node.js event loop not exit so long as the
65
- * FSWatcher is active. Calling ref() multiple times has no effect.
66
- */
67
- ref(): this {
68
- const priv = privates.get(this);
69
- if (!priv.persistent && !priv.cancellable.is_cancelled()) {
70
- priv.persistent = true;
71
- priv.sourceId = (GLib.timeout_add as unknown as (priority: number, interval: number, fn: () => boolean) => number)(
72
- GLib.PRIORITY_LOW,
73
- 2147483647,
74
- () => GLib.SOURCE_CONTINUE,
75
- );
76
- }
77
- return this;
78
- }
79
-
80
- /**
81
- * When called, the active FSWatcher will not require the Node.js event loop
82
- * to remain active. Calling unref() multiple times has no effect.
83
- */
84
- unref(): this {
85
- const priv = privates.get(this);
86
- if (priv.persistent) {
87
- priv.persistent = false;
88
- if (priv.sourceId !== null) {
89
- GLib.source_remove(priv.sourceId);
90
- priv.sourceId = null;
91
- }
92
- }
93
- return this;
94
- }
95
-
96
- };
97
-
98
- function changed(watcher, file, otherFile, eventType) {
99
- switch (eventType) {
100
- case Gio.FileMonitorEvent.CHANGES_DONE_HINT:
101
- this.emit('change', 'change', file.get_basename());
102
- break;
103
- case Gio.FileMonitorEvent.DELETED:
104
- case Gio.FileMonitorEvent.CREATED:
105
- case Gio.FileMonitorEvent.RENAMED:
106
- case Gio.FileMonitorEvent.MOVED_IN:
107
- case Gio.FileMonitorEvent.MOVED_OUT:
108
- this.emit('rename', 'rename', file.get_basename());
109
- break;
110
- }
111
- }
112
-
113
- export default FSWatcher;
114
-
115
- // ─── fs.promises.watch ────────────────────────────────────────────────────────
116
-
117
- type WatchEvent = { eventType: string; filename: string | null };
118
-
119
- function gioEventToNodeType(eventType: Gio.FileMonitorEvent): string | null {
120
- switch (eventType) {
121
- case Gio.FileMonitorEvent.CHANGES_DONE_HINT: return 'change';
122
- case Gio.FileMonitorEvent.DELETED:
123
- case Gio.FileMonitorEvent.CREATED:
124
- case Gio.FileMonitorEvent.RENAMED:
125
- case Gio.FileMonitorEvent.MOVED_IN:
126
- case Gio.FileMonitorEvent.MOVED_OUT: return 'rename';
127
- default: return null;
128
- }
129
- }
130
-
131
- export async function* watchAsync(
132
- filename: PathLike,
133
- options?: WatchOptions & { signal?: AbortSignal },
134
- ): AsyncIterableIterator<WatchEvent> {
135
- const signal = (options as any)?.signal as AbortSignal | undefined;
136
-
137
- if (signal?.aborted) return;
138
-
139
- const pathStr = normalizePath(filename);
140
- const file = Gio.File.new_for_path(pathStr);
141
- const cancellable = Gio.Cancellable.new();
142
-
143
- let watcher: Gio.FileMonitor;
144
- try {
145
- watcher = file.monitor(Gio.FileMonitorFlags.NONE, cancellable);
146
- } catch {
147
- return;
148
- }
149
-
150
- const eventQueue: WatchEvent[] = [];
151
- const waiterQueue: Array<{ resolve: (r: IteratorResult<WatchEvent>) => void }> = [];
152
- let finished = false;
153
-
154
- function enqueue(event: WatchEvent): void {
155
- if (finished) return;
156
- if (waiterQueue.length > 0) {
157
- waiterQueue.shift()!.resolve({ value: event, done: false });
158
- } else {
159
- eventQueue.push(event);
160
- }
161
- }
162
-
163
- function terminate(): void {
164
- if (finished) return;
165
- finished = true;
166
- if (!cancellable.is_cancelled()) cancellable.cancel();
167
- while (waiterQueue.length > 0) {
168
- waiterQueue.shift()!.resolve({ value: undefined as any, done: true });
169
- }
170
- }
171
-
172
- const signalId = watcher.connect('changed', (_mon: Gio.FileMonitor, changedFile: Gio.File, _otherFile: Gio.File | null, eventType: Gio.FileMonitorEvent) => {
173
- const type = gioEventToNodeType(eventType);
174
- if (type === null) return;
175
- enqueue({ eventType: type, filename: changedFile?.get_basename() ?? null });
176
- });
177
-
178
- const abortHandler = () => terminate();
179
- signal?.addEventListener('abort', abortHandler);
180
-
181
- try {
182
- while (!finished) {
183
- if (eventQueue.length > 0) {
184
- yield eventQueue.shift()!;
185
- continue;
186
- }
187
- const result = await new Promise<IteratorResult<WatchEvent>>(resolve => {
188
- waiterQueue.push({ resolve });
189
- });
190
- if (result.done) break;
191
- yield result.value;
192
- }
193
- } finally {
194
- signal?.removeEventListener('abort', abortHandler);
195
- try { watcher.disconnect(signalId); } catch {}
196
- if (!cancellable.is_cancelled()) cancellable.cancel();
197
- }
198
- }
package/src/glob.spec.ts DELETED
@@ -1,201 +0,0 @@
1
- // Ported from refs/bun/test/js/node/fs/glob.test.ts
2
- // Original: MIT, Oven & contributors.
3
- // Rewritten for @gjsify/unit — behavior preserved, assertion dialect adapted.
4
-
5
- import { describe, it, expect } from '@gjsify/unit';
6
- import {
7
- globSync,
8
- glob,
9
- promises,
10
- mkdirSync,
11
- writeFileSync,
12
- mkdtempSync,
13
- rmSync,
14
- } from 'node:fs';
15
- import { join } from 'node:path';
16
- import { tmpdir } from 'node:os';
17
-
18
- function makeTmp(): string {
19
- return mkdtempSync(join(tmpdir(), 'gjsify-glob-'));
20
- }
21
-
22
- export default async () => {
23
- await describe('fs.globSync', async () => {
24
- await it('matches *.ts files in flat directory', async () => {
25
- const tmp = makeTmp();
26
- writeFileSync(join(tmp, 'a.ts'), '');
27
- writeFileSync(join(tmp, 'b.ts'), '');
28
- writeFileSync(join(tmp, 'c.txt'), '');
29
-
30
- const results = globSync('*.ts', { cwd: tmp });
31
- expect(results.sort()).toStrictEqual(['a.ts', 'b.ts']);
32
- rmSync(tmp, { recursive: true, force: true });
33
- });
34
-
35
- await it('matches **/*.ts recursively', async () => {
36
- const tmp = makeTmp();
37
- mkdirSync(join(tmp, 'sub'));
38
- writeFileSync(join(tmp, 'root.ts'), '');
39
- writeFileSync(join(tmp, 'sub', 'nested.ts'), '');
40
- writeFileSync(join(tmp, 'sub', 'other.txt'), '');
41
-
42
- const results = globSync('**/*.ts', { cwd: tmp });
43
- expect(results.sort()).toStrictEqual(['root.ts', 'sub/nested.ts']);
44
- rmSync(tmp, { recursive: true, force: true });
45
- });
46
-
47
- await it('matches files in a subdirectory pattern', async () => {
48
- const tmp = makeTmp();
49
- mkdirSync(join(tmp, 'src'));
50
- writeFileSync(join(tmp, 'src', 'index.ts'), '');
51
- writeFileSync(join(tmp, 'src', 'util.ts'), '');
52
- writeFileSync(join(tmp, 'index.ts'), '');
53
-
54
- const results = globSync('src/*.ts', { cwd: tmp });
55
- expect(results.sort()).toStrictEqual(['src/index.ts', 'src/util.ts']);
56
- rmSync(tmp, { recursive: true, force: true });
57
- });
58
-
59
- await it('supports {a,b} alternation', async () => {
60
- const tmp = makeTmp();
61
- writeFileSync(join(tmp, 'a.ts'), '');
62
- writeFileSync(join(tmp, 'b.ts'), '');
63
- writeFileSync(join(tmp, 'c.ts'), '');
64
-
65
- const results = globSync('*.{ts,js}', { cwd: tmp });
66
- expect(results.sort()).toStrictEqual(['a.ts', 'b.ts', 'c.ts']);
67
- rmSync(tmp, { recursive: true, force: true });
68
- });
69
-
70
- await it('** matches all files and directories', async () => {
71
- const tmp = makeTmp();
72
- mkdirSync(join(tmp, 'sub'));
73
- writeFileSync(join(tmp, 'a.ts'), '');
74
- writeFileSync(join(tmp, 'sub', 'b.ts'), '');
75
-
76
- const results = globSync('**', { cwd: tmp });
77
- // Should include '.', 'a.ts', 'sub', 'sub/b.ts'
78
- expect(results.includes('a.ts')).toBe(true);
79
- expect(results.includes('sub/b.ts')).toBe(true);
80
- rmSync(tmp, { recursive: true, force: true });
81
- });
82
-
83
- await it('a/** matches the directory itself and its contents', async () => {
84
- const tmp = makeTmp();
85
- mkdirSync(join(tmp, 'sub'));
86
- writeFileSync(join(tmp, 'sub', 'x.ts'), '');
87
- writeFileSync(join(tmp, 'root.ts'), '');
88
-
89
- const results = globSync('sub/**', { cwd: tmp });
90
- expect(results.includes('sub')).toBe(true);
91
- expect(results.includes('sub/x.ts')).toBe(true);
92
- expect(results.includes('root.ts')).toBe(false);
93
- rmSync(tmp, { recursive: true, force: true });
94
- });
95
-
96
- await it('accepts array of patterns', async () => {
97
- const tmp = makeTmp();
98
- writeFileSync(join(tmp, 'a.ts'), '');
99
- writeFileSync(join(tmp, 'b.js'), '');
100
- writeFileSync(join(tmp, 'c.txt'), '');
101
-
102
- const results = globSync(['*.ts', '*.js'], { cwd: tmp });
103
- expect(results.sort()).toStrictEqual(['a.ts', 'b.js']);
104
- rmSync(tmp, { recursive: true, force: true });
105
- });
106
-
107
- await it('exclude function filters out matching paths', async () => {
108
- const tmp = makeTmp();
109
- writeFileSync(join(tmp, 'a.ts'), '');
110
- writeFileSync(join(tmp, 'b.ts'), '');
111
-
112
- // Use **/*.ts so Node.js native also invokes the exclude function
113
- // (flat patterns like *.ts skip directory traversal and exclude is not called)
114
- const results = globSync('**/*.ts', {
115
- cwd: tmp,
116
- exclude: (p) => p === 'a.ts',
117
- });
118
- expect(results).toStrictEqual(['b.ts']);
119
- rmSync(tmp, { recursive: true, force: true });
120
- });
121
-
122
- await it('returns empty array when no files match', async () => {
123
- const tmp = makeTmp();
124
- writeFileSync(join(tmp, 'a.txt'), '');
125
-
126
- const results = globSync('*.ts', { cwd: tmp });
127
- expect(results).toStrictEqual([]);
128
- rmSync(tmp, { recursive: true, force: true });
129
- });
130
-
131
- await it('returns empty array for non-existent cwd', async () => {
132
- const results = globSync('*.ts', { cwd: '/nonexistent-dir-gjsify-test' });
133
- expect(results).toStrictEqual([]);
134
- });
135
- });
136
-
137
- await describe('fs.glob (callback)', async () => {
138
- await it('calls back with matched files', async () => {
139
- const tmp = makeTmp();
140
- writeFileSync(join(tmp, 'hello.ts'), '');
141
- writeFileSync(join(tmp, 'world.ts'), '');
142
-
143
- const results = await new Promise<string[]>((resolve, reject) => {
144
- glob('*.ts', { cwd: tmp }, (err, matches) => {
145
- if (err) return reject(err);
146
- resolve(matches);
147
- });
148
- });
149
-
150
- expect(results.sort()).toStrictEqual(['hello.ts', 'world.ts']);
151
- rmSync(tmp, { recursive: true, force: true });
152
- });
153
-
154
- await it('accepts callback as second argument (no options)', async () => {
155
- const tmp = makeTmp();
156
- writeFileSync(join(tmp, 'file.ts'), '');
157
-
158
- const results = await new Promise<string[]>((resolve, reject) => {
159
- (glob as any)('*.ts', (err: any, matches: string[]) => {
160
- if (err) return reject(err);
161
- resolve(matches);
162
- });
163
- });
164
-
165
- // No cwd given — just verify it completes without error
166
- expect(Array.isArray(results)).toBe(true);
167
- rmSync(tmp, { recursive: true, force: true });
168
- });
169
- });
170
-
171
- await describe('fs.promises.glob', async () => {
172
- await it('async iterates matched files', async () => {
173
- const tmp = makeTmp();
174
- writeFileSync(join(tmp, 'alpha.ts'), '');
175
- writeFileSync(join(tmp, 'beta.ts'), '');
176
-
177
- const results: string[] = [];
178
- for await (const match of promises.glob('*.ts', { cwd: tmp })) {
179
- results.push(match);
180
- }
181
-
182
- expect(results.sort()).toStrictEqual(['alpha.ts', 'beta.ts']);
183
- rmSync(tmp, { recursive: true, force: true });
184
- });
185
-
186
- await it('async iterates recursively with **/*.ts', async () => {
187
- const tmp = makeTmp();
188
- mkdirSync(join(tmp, 'lib'));
189
- writeFileSync(join(tmp, 'index.ts'), '');
190
- writeFileSync(join(tmp, 'lib', 'helper.ts'), '');
191
-
192
- const results: string[] = [];
193
- for await (const match of promises.glob('**/*.ts', { cwd: tmp })) {
194
- results.push(match);
195
- }
196
-
197
- expect(results.sort()).toStrictEqual(['index.ts', 'lib/helper.ts']);
198
- rmSync(tmp, { recursive: true, force: true });
199
- });
200
- });
201
- };
package/src/glob.ts DELETED
@@ -1,205 +0,0 @@
1
- // Reference: Node.js lib/internal/fs/glob.js
2
- // Reimplemented for GJS using readdirSync (recursive) + pattern matching
3
-
4
- import { readdirSync } from './sync.js';
5
- import { normalizePath } from './utils.js';
6
- import type { PathLike } from 'node:fs';
7
-
8
- export interface GlobOptions {
9
- cwd?: string | URL;
10
- exclude?: string | string[] | ((path: string) => boolean);
11
- withFileTypes?: boolean;
12
- }
13
-
14
- // ─── Pattern → RegExp conversion ─────────────────────────────────────────────
15
-
16
- /** Convert a single glob segment (no `/`) to a regex source string */
17
- function segmentToRegexSrc(seg: string): string {
18
- // Handle extglob: !(pattern), *(pattern), +(pattern), ?(pattern), @(pattern)
19
- const extglob = /^([!*+?@])\((.+)\)$/.exec(seg);
20
- if (extglob) {
21
- const [, type, inner] = extglob;
22
- const parts = inner.split('|').map(p => segmentToRegexSrc(p));
23
- const group = '(?:' + parts.join('|') + ')';
24
- switch (type) {
25
- case '!': return '(?!(?:' + parts.join('|') + '))[^/]*';
26
- case '*': return group + '*';
27
- case '+': return group + '+';
28
- case '?': return group + '?';
29
- case '@': return group;
30
- }
31
- }
32
-
33
- let result = '';
34
- let i = 0;
35
- while (i < seg.length) {
36
- const c = seg[i];
37
- if (c === '*') { result += '[^/]*'; i++; continue; }
38
- if (c === '?') { result += '[^/]'; i++; continue; }
39
- if (c === '[') {
40
- // Character class — pass through to regex
41
- const end = seg.indexOf(']', i + 1);
42
- if (end === -1) { result += '\\['; i++; continue; }
43
- result += seg.slice(i, end + 1);
44
- i = end + 1;
45
- continue;
46
- }
47
- if (c === '{') {
48
- // Alternation
49
- const end = seg.indexOf('}', i + 1);
50
- if (end === -1) { result += '\\{'; i++; continue; }
51
- const alts = seg.slice(i + 1, end).split(',').map(a => segmentToRegexSrc(a.trim()));
52
- result += '(?:' + alts.join('|') + ')';
53
- i = end + 1;
54
- continue;
55
- }
56
- // Escape regex special chars
57
- if ('.+^$|()[]{}'.includes(c)) {
58
- result += '\\' + c;
59
- } else {
60
- result += c;
61
- }
62
- i++;
63
- }
64
- return result;
65
- }
66
-
67
- /**
68
- * Convert a glob pattern to a RegExp that matches relative POSIX paths.
69
- */
70
- function globToRegex(pattern: string): RegExp {
71
- // Normalize: collapse multiple slashes, remove leading './'
72
- pattern = pattern.replace(/\/+/g, '/').replace(/^\.\//, '');
73
-
74
- const segments = pattern.split('/');
75
- const parts: string[] = [];
76
-
77
- for (let si = 0; si < segments.length; si++) {
78
- const seg = segments[si];
79
- const isLast = si === segments.length - 1;
80
-
81
- if (seg === '**') {
82
- if (isLast) {
83
- if (parts.length > 0 && parts[parts.length - 1] === '/') {
84
- // Remove the trailing '/' so "a/" becomes optional: "a(?:/.*)?
85
- parts.pop();
86
- parts.push('(?:/.+)?');
87
- } else {
88
- // '**' at root level: match '.' or anything
89
- parts.push('(?:.+)?');
90
- }
91
- } else {
92
- // Not last: match zero or more path segments with trailing slash
93
- parts.push('(?:[^/]+/)*');
94
- }
95
- } else {
96
- parts.push(segmentToRegexSrc(seg));
97
- if (!isLast) parts.push('/');
98
- }
99
- }
100
-
101
- return new RegExp('^(?:' + parts.join('') + ')$');
102
- }
103
-
104
- // ─── Exclude logic ────────────────────────────────────────────────────────────
105
-
106
- function buildExcludePredicate(exclude: GlobOptions['exclude']): ((path: string) => boolean) | null {
107
- if (!exclude) return null;
108
- if (typeof exclude === 'function') return exclude;
109
- const patterns = Array.isArray(exclude) ? exclude : [exclude];
110
- const regexes = patterns.map(p => globToRegex(p));
111
- return (path: string) => regexes.some(rx => rx.test(path));
112
- }
113
-
114
- // ─── Walk + match ─────────────────────────────────────────────────────────────
115
-
116
- function matchAll(pattern: string, cwd: string, exclude: GlobOptions['exclude']): string[] {
117
- const regex = globToRegex(pattern);
118
- const isExcluded = buildExcludePredicate(exclude);
119
-
120
- // Get all entries recursively (files + directories)
121
- let allEntries: string[];
122
- try {
123
- allEntries = readdirSync(cwd, { recursive: true }) as string[];
124
- } catch {
125
- return [];
126
- }
127
-
128
- // Include '.' itself for patterns like '**' that match the root
129
- const candidates = ['.', ...allEntries];
130
-
131
- const results: string[] = [];
132
- for (const entry of candidates) {
133
- // Normalize separators for matching
134
- const normalized = entry.replace(/\\/g, '/');
135
- if (!regex.test(normalized)) continue;
136
- if (isExcluded && isExcluded(normalized)) continue;
137
- results.push(entry);
138
- }
139
-
140
- return results;
141
- }
142
-
143
- // ─── Public API ───────────────────────────────────────────────────────────────
144
-
145
- export function globSync(
146
- pattern: string | string[],
147
- options?: GlobOptions,
148
- ): string[] {
149
- const patterns = Array.isArray(pattern) ? pattern : [pattern];
150
- const cwd = options?.cwd
151
- ? normalizePath(options.cwd as PathLike)
152
- : (globalThis as any).process?.cwd?.() ?? '/';
153
- const exclude = options?.exclude;
154
-
155
- const seen = new Set<string>();
156
- const results: string[] = [];
157
-
158
- for (const p of patterns) {
159
- for (const match of matchAll(p, cwd, exclude)) {
160
- if (!seen.has(match)) {
161
- seen.add(match);
162
- results.push(match);
163
- }
164
- }
165
- }
166
-
167
- return results;
168
- }
169
-
170
- export function glob(
171
- pattern: string | string[],
172
- options: GlobOptions | ((err: NodeJS.ErrnoException | null, matches: string[]) => void),
173
- callback?: (err: NodeJS.ErrnoException | null, matches: string[]) => void,
174
- ): void {
175
- let opts: GlobOptions;
176
- let cb: (err: NodeJS.ErrnoException | null, matches: string[]) => void;
177
-
178
- if (typeof options === 'function') {
179
- cb = options;
180
- opts = {};
181
- } else {
182
- cb = callback!;
183
- opts = options || {};
184
- }
185
-
186
- Promise.resolve().then(() => {
187
- try {
188
- const matches = globSync(pattern, opts);
189
- cb(null, matches);
190
- } catch (err: unknown) {
191
- cb(err as NodeJS.ErrnoException, []);
192
- }
193
- });
194
- }
195
-
196
- // promises.glob returns an async iterator
197
- export async function* globAsync(
198
- pattern: string | string[],
199
- options?: GlobOptions,
200
- ): AsyncIterableIterator<string> {
201
- const matches = globSync(pattern, options);
202
- for (const m of matches) {
203
- yield m;
204
- }
205
- }