Skip to content

Commit 26820ce

Browse files
Copilotstreamich
andcommitted
Remove watchFile from promises API as requested
Co-authored-by: streamich <9773803+streamich@users.noreply.github.com>
1 parent f5400b5 commit 26820ce

4 files changed

Lines changed: 300 additions & 627 deletions

File tree

src/__tests__/promises.test.ts

Lines changed: 17 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -771,13 +771,13 @@ describe('Promises API', () => {
771771
vol.fromJSON({
772772
'/foo': 'bar',
773773
});
774-
774+
775775
const watcher = promises.watch('/foo');
776776
expect(typeof watcher[Symbol.asyncIterator]).toBe('function');
777777
expect(typeof watcher.next).toBe('function');
778778
expect(typeof watcher.return).toBe('function');
779779
expect(typeof watcher.throw).toBe('function');
780-
780+
781781
// Clean up
782782
if (watcher.return) {
783783
await watcher.return();
@@ -790,10 +790,10 @@ describe('Promises API', () => {
790790
vol.fromJSON({
791791
'/foo': 'bar',
792792
});
793-
793+
794794
const watcher = promises.watch('/foo');
795795
const events: Array<{ eventType: string; filename: string | Buffer }> = [];
796-
796+
797797
// Start watching
798798
const watchPromise = (async () => {
799799
const iterator = watcher[Symbol.asyncIterator]();
@@ -805,15 +805,15 @@ describe('Promises API', () => {
805805
await iterator.return();
806806
}
807807
})();
808-
808+
809809
// Give watcher time to start
810810
await new Promise(resolve => setTimeout(resolve, 10));
811-
811+
812812
// Modify the file
813813
vol.writeFileSync('/foo', 'baz');
814-
814+
815815
await watchPromise;
816-
816+
817817
expect(events).toHaveLength(1);
818818
expect(events[0].eventType).toBe('change');
819819
expect(events[0].filename).toBe('foo');
@@ -825,13 +825,13 @@ describe('Promises API', () => {
825825
vol.fromJSON({
826826
'/foo': 'bar',
827827
});
828-
828+
829829
const abortController = new AbortController();
830830
const watcher = promises.watch('/foo', { signal: abortController.signal });
831-
831+
832832
// Abort immediately
833833
abortController.abort();
834-
834+
835835
const iterator = watcher[Symbol.asyncIterator]();
836836
const result = await iterator.next();
837837
expect(result.done).toBe(true);
@@ -843,18 +843,18 @@ describe('Promises API', () => {
843843
vol.fromJSON({
844844
'/foo': 'bar',
845845
});
846-
846+
847847
const watcher = promises.watch('/foo', { maxQueue: 1, overflow: 'ignore' });
848-
848+
849849
// Generate multiple events quickly
850850
vol.writeFileSync('/foo', 'change1');
851851
vol.writeFileSync('/foo', 'change2');
852852
vol.writeFileSync('/foo', 'change3');
853-
853+
854854
const iterator = watcher[Symbol.asyncIterator]();
855855
const result1 = await iterator.next();
856856
expect(result1.done).toBe(false);
857-
857+
858858
if (iterator.return) {
859859
await iterator.return();
860860
}
@@ -866,103 +866,14 @@ describe('Promises API', () => {
866866
vol.fromJSON({
867867
'/foo': 'bar',
868868
});
869-
869+
870870
const watcher = promises.watch('/foo', { maxQueue: 1, overflow: 'throw' });
871-
871+
872872
// This test is tricky because we need to ensure the overflow happens
873873
// We can't easily test this synchronously, so we'll skip for now
874874
if (watcher.return) {
875875
await watcher.return();
876876
}
877877
});
878878
});
879-
880-
describe('watchFile(filename[, options])', () => {
881-
it('Returns an AsyncIterableIterator', async () => {
882-
const vol = new Volume();
883-
const { promises } = vol;
884-
vol.fromJSON({
885-
'/foo': 'bar',
886-
});
887-
888-
const watcher = promises.watchFile('/foo');
889-
expect(typeof watcher[Symbol.asyncIterator]).toBe('function');
890-
expect(typeof watcher.next).toBe('function');
891-
expect(typeof watcher.return).toBe('function');
892-
expect(typeof watcher.throw).toBe('function');
893-
894-
// Clean up
895-
if (watcher.return) {
896-
await watcher.return();
897-
}
898-
});
899-
900-
it('Emits stat change events when file is modified', async () => {
901-
const vol = new Volume();
902-
const { promises } = vol;
903-
vol.fromJSON({
904-
'/foo': 'bar',
905-
});
906-
907-
const watcher = promises.watchFile('/foo', { interval: 1 });
908-
const events: Array<{ curr: any; prev: any }> = [];
909-
910-
// Start watching
911-
const watchPromise = (async () => {
912-
const iterator = watcher[Symbol.asyncIterator]();
913-
const result = await iterator.next();
914-
if (!result.done) {
915-
events.push(result.value);
916-
}
917-
if (iterator.return) {
918-
await iterator.return();
919-
}
920-
})();
921-
922-
// Give watcher time to start
923-
await new Promise(resolve => setTimeout(resolve, 10));
924-
925-
// Modify the file
926-
vol.writeFileSync('/foo', 'modified content');
927-
928-
await watchPromise;
929-
930-
expect(events).toHaveLength(1);
931-
expect(events[0].curr).toBeDefined();
932-
expect(events[0].prev).toBeDefined();
933-
expect(events[0].curr.isFile()).toBe(true);
934-
expect(events[0].prev.isFile()).toBe(true);
935-
});
936-
937-
it('Works with for-await loops', async () => {
938-
const vol = new Volume();
939-
const { promises } = vol;
940-
vol.fromJSON({
941-
'/test.txt': 'initial',
942-
});
943-
944-
const watcher = promises.watch('/test.txt');
945-
const events: Array<{ eventType: string; filename: string | Buffer }> = [];
946-
947-
// Start the for-await loop
948-
const watchPromise = (async () => {
949-
for await (const event of watcher) {
950-
events.push(event);
951-
if (events.length >= 2) {
952-
break;
953-
}
954-
}
955-
})();
956-
957-
// Generate events
958-
setTimeout(() => vol.writeFileSync('/test.txt', 'change1'), 10);
959-
setTimeout(() => vol.writeFileSync('/test.txt', 'change2'), 15);
960-
961-
await watchPromise;
962-
963-
expect(events).toHaveLength(2);
964-
expect(events[0].eventType).toBe('change');
965-
expect(events[1].eventType).toBe('change');
966-
});
967-
});
968879
});

src/node/FsPromises.ts

Lines changed: 3 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class FSWatchAsyncIterator implements AsyncIterableIterator<{ eventType: string;
1717
constructor(
1818
private fs: any,
1919
private path: misc.PathLike,
20-
private options: opts.IWatchOptions = {}
20+
private options: opts.IWatchOptions = {},
2121
) {
2222
this.maxQueue = options.maxQueue || 2048;
2323
this.overflow = options.overflow || 'ignore';
@@ -64,7 +64,7 @@ class FSWatchAsyncIterator implements AsyncIterableIterator<{ eventType: string;
6464
}
6565

6666
this.eventQueue.push(event);
67-
67+
6868
// If there's a waiting promise, resolve it
6969
if (this.resolveQueue.length > 0) {
7070
const { resolve } = this.resolveQueue.shift()!;
@@ -125,97 +125,6 @@ class FSWatchAsyncIterator implements AsyncIterableIterator<{ eventType: string;
125125
}
126126
}
127127

128-
// AsyncIterator implementation for promises.watchFile
129-
class StatWatchAsyncIterator implements AsyncIterableIterator<{ curr: misc.IStats; prev: misc.IStats }> {
130-
private watcher: any;
131-
private eventQueue: Array<{ curr: misc.IStats; prev: misc.IStats }> = [];
132-
private resolveQueue: Array<{ resolve: Function; reject: Function }> = [];
133-
private finished = false;
134-
135-
constructor(
136-
private fs: any,
137-
private path: misc.PathLike,
138-
private options: opts.IWatchFileOptions = {}
139-
) {
140-
this.startWatching();
141-
}
142-
143-
private startWatching() {
144-
try {
145-
this.watcher = this.fs.watchFile(this.path, this.options, (curr: misc.IStats, prev: misc.IStats) => {
146-
this.enqueueEvent({ curr, prev });
147-
});
148-
} catch (error) {
149-
this.finish();
150-
throw error;
151-
}
152-
}
153-
154-
private enqueueEvent(event: { curr: misc.IStats; prev: misc.IStats }) {
155-
if (this.finished) return;
156-
157-
this.eventQueue.push(event);
158-
159-
// If there's a waiting promise, resolve it
160-
if (this.resolveQueue.length > 0) {
161-
const { resolve } = this.resolveQueue.shift()!;
162-
const nextEvent = this.eventQueue.shift()!;
163-
resolve({ value: nextEvent, done: false });
164-
}
165-
}
166-
167-
private finish(error?: Error) {
168-
if (this.finished) return;
169-
this.finished = true;
170-
171-
if (this.watcher) {
172-
this.fs.unwatchFile(this.path);
173-
this.watcher = null;
174-
}
175-
176-
// Resolve or reject all pending promises
177-
while (this.resolveQueue.length > 0) {
178-
const { resolve, reject } = this.resolveQueue.shift()!;
179-
if (error) {
180-
reject(error);
181-
} else {
182-
resolve({ value: undefined, done: true });
183-
}
184-
}
185-
}
186-
187-
async next(): Promise<IteratorResult<{ curr: misc.IStats; prev: misc.IStats }>> {
188-
if (this.finished) {
189-
return { value: undefined, done: true };
190-
}
191-
192-
// If we have queued events, return one
193-
if (this.eventQueue.length > 0) {
194-
const event = this.eventQueue.shift()!;
195-
return { value: event, done: false };
196-
}
197-
198-
// Otherwise, wait for the next event
199-
return new Promise((resolve, reject) => {
200-
this.resolveQueue.push({ resolve, reject });
201-
});
202-
}
203-
204-
async return(): Promise<IteratorResult<{ curr: misc.IStats; prev: misc.IStats }>> {
205-
this.finish();
206-
return { value: undefined, done: true };
207-
}
208-
209-
async throw(error: any): Promise<IteratorResult<{ curr: misc.IStats; prev: misc.IStats }>> {
210-
this.finish(error);
211-
throw error;
212-
}
213-
214-
[Symbol.asyncIterator](): AsyncIterableIterator<{ curr: misc.IStats; prev: misc.IStats }> {
215-
return this;
216-
}
217-
}
218-
219128
export class FsPromises implements FsPromisesApi {
220129
public readonly constants = constants;
221130

@@ -288,16 +197,7 @@ export class FsPromises implements FsPromisesApi {
288197
filename: misc.PathLike,
289198
options?: opts.IWatchOptions | string,
290199
): AsyncIterableIterator<{ eventType: string; filename: string | Buffer }> => {
291-
const watchOptions: opts.IWatchOptions = typeof options === 'string'
292-
? { encoding: options as any }
293-
: (options || {});
200+
const watchOptions: opts.IWatchOptions = typeof options === 'string' ? { encoding: options as any } : options || {};
294201
return new FSWatchAsyncIterator(this.fs, filename, watchOptions);
295202
};
296-
297-
public readonly watchFile = (
298-
filename: misc.PathLike,
299-
options?: opts.IWatchFileOptions,
300-
): AsyncIterableIterator<{ curr: misc.IStats; prev: misc.IStats }> => {
301-
return new StatWatchAsyncIterator(this.fs, filename, options || {});
302-
};
303203
}

src/node/types/FsPromisesApi.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,5 @@ export interface FsPromisesApi {
3737
filename: misc.PathLike,
3838
options?: opts.IWatchOptions,
3939
): AsyncIterableIterator<{ eventType: string; filename: string | Buffer }>;
40-
watchFile(
41-
filename: misc.PathLike,
42-
options?: opts.IWatchFileOptions,
43-
): AsyncIterableIterator<{ curr: misc.IStats; prev: misc.IStats }>;
4440
writeFile(id: misc.TFileHandle, data: misc.TPromisesData, options?: opts.IWriteFileOptions): Promise<void>;
4541
}

0 commit comments

Comments
 (0)