@ms-cloudpack/file-watcher 0.1.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,19 +16,22 @@ const watcher = createWatcher();
16
16
  const unwatch = watcher.watch({ path: 'path/to/package' }, () => console.log(`package changed`));
17
17
  ```
18
18
 
19
- 3. To dispose, call the returned `unwatch`, or call `watcher.unwatchAll` to unsubscribe from all watchers.
19
+ 3. To dispose, call the returned `unwatch`, or call `watcher.dispose` to unsubscribe from all watchers.
20
20
 
21
21
  ```ts
22
22
  // Dispose an individual watcher.
23
23
  await unwatch();
24
24
 
25
25
  // Dispose all watchers.
26
- await watcher.unwatchAll();
26
+ await watcher.dispose();
27
27
  ```
28
28
 
29
+ ### `createWatcher` options
30
+
31
+ - `type` (`'default' | 'fork'`, optional): By default, this will create a watcher in the same thread. Use `type: 'fork'` to create the watcher in a forked process.
32
+
29
33
  ### `watcher.watch` options
30
34
 
31
- | Name | Type | Description |
32
- | -------------- | -------- | ----------------------------------------------------------------------------------------- |
33
- | `path` | string | The absolute base path to be watched. |
34
- | `watchedPaths` | string[] | Relative paths from the base path to watch. Defaults to `package.json` and `src/` folder. |
35
+ - `path` (`string`): The absolute root path to be watched.
36
+ - `id` (`string`, optional): ID for the watch job (defaults to `path`). If you call `watch` twice using the same `id`, subsequent calls will be ignored.
37
+ - `watchedPaths` (`string[]`, optional): Relative paths/globs from the root path to watch. Defaults are under "@ms-cloudpack/path-utilities" in `sourceFilesGlobs`.
@@ -0,0 +1,22 @@
1
+ import type { UnwatchCallback, WatchChangeCallback, Watcher, WatchOptions, WatchOptionsWithId } from './types/Watcher.js';
2
+ /**
3
+ * Base class for watcher implementations.
4
+ */
5
+ export declare abstract class BaseWatcher implements Watcher {
6
+ /** Watcher is disposed or disposing */
7
+ private _disposeCalled;
8
+ /** Mapping from ID to change callback */
9
+ protected _onChangeCallbacks: Record<string, WatchChangeCallback>;
10
+ /** Mapping from ID to unwatch callback */
11
+ private _unwatchCallbacks;
12
+ /** Number of IDs currently being watched (just used for testing) */
13
+ get activeWatchCount(): number;
14
+ watch(baseOptions: WatchOptions, onChange: WatchChangeCallback): UnwatchCallback;
15
+ unwatch(id: string): Promise<void>;
16
+ dispose(): Promise<void>;
17
+ protected abstract _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): UnwatchCallback;
18
+ /** By default, calls all the unwatch callbacks. Override for custom dispose logic. */
19
+ protected _dispose(): Promise<void>;
20
+ protected _isDisposed(): boolean;
21
+ }
22
+ //# sourceMappingURL=BaseWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseWatcher.d.ts","sourceRoot":"","sources":["../src/BaseWatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,OAAO,EACP,YAAY,EACZ,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAE5B;;GAEG;AACH,8BAAsB,WAAY,YAAW,OAAO;IAClD,uCAAuC;IACvC,OAAO,CAAC,cAAc,CAAS;IAC/B,yCAAyC;IACzC,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAM;IACvE,0CAA0C;IAC1C,OAAO,CAAC,iBAAiB,CAAuC;IAEhE,oEAAoE;IACpE,IAAW,gBAAgB,IAAI,MAAM,CAEpC;IAEM,KAAK,CAAC,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,mBAAmB,GAAG,eAAe;IA8B1E,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,eAAe;IAEtG,sFAAsF;cACtE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzC,SAAS,CAAC,WAAW,IAAI,OAAO;CAGjC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Base class for watcher implementations.
3
+ */
4
+ export class BaseWatcher {
5
+ /** Watcher is disposed or disposing */
6
+ _disposeCalled = false;
7
+ /** Mapping from ID to change callback */
8
+ _onChangeCallbacks = {};
9
+ /** Mapping from ID to unwatch callback */
10
+ _unwatchCallbacks = {};
11
+ /** Number of IDs currently being watched (just used for testing) */
12
+ get activeWatchCount() {
13
+ return Object.keys(this._unwatchCallbacks).length;
14
+ }
15
+ watch(baseOptions, onChange) {
16
+ if (this._isDisposed()) {
17
+ throw new Error('Watcher has been disposed.');
18
+ }
19
+ const { id = baseOptions.path } = baseOptions;
20
+ if (!this._unwatchCallbacks[id]) {
21
+ this._onChangeCallbacks[id] = onChange;
22
+ // Watch the path using the child class's implementation
23
+ const unwatch = this._watch({ ...baseOptions, id }, onChange);
24
+ // Make an unwatch wrapper to clean up parent class state
25
+ let calledUnwatch = false;
26
+ this._unwatchCallbacks[id] = async () => {
27
+ // The same unwatch callback might be returned multiple times in case of duplicate calls
28
+ // for the same ID. Only do the cleanup once.
29
+ if (!calledUnwatch) {
30
+ calledUnwatch = true;
31
+ await unwatch();
32
+ delete this._unwatchCallbacks[id];
33
+ delete this._onChangeCallbacks[id];
34
+ }
35
+ };
36
+ }
37
+ return this._unwatchCallbacks[id];
38
+ }
39
+ async unwatch(id) {
40
+ // The wrapper code at the callback definition in watch() handles cleanup of callback records.
41
+ await this._unwatchCallbacks[id]?.();
42
+ }
43
+ async dispose() {
44
+ this._disposeCalled = true;
45
+ await this._dispose();
46
+ this._unwatchCallbacks = {};
47
+ this._onChangeCallbacks = {};
48
+ }
49
+ /** By default, calls all the unwatch callbacks. Override for custom dispose logic. */
50
+ async _dispose() {
51
+ await Promise.all(Object.values(this._unwatchCallbacks).map((unwatch) => unwatch()));
52
+ }
53
+ _isDisposed() {
54
+ return this._disposeCalled;
55
+ }
56
+ }
57
+ //# sourceMappingURL=BaseWatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseWatcher.js","sourceRoot":"","sources":["../src/BaseWatcher.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,OAAgB,WAAW;IAC/B,uCAAuC;IAC/B,cAAc,GAAG,KAAK,CAAC;IAC/B,yCAAyC;IAC/B,kBAAkB,GAAwC,EAAE,CAAC;IACvE,0CAA0C;IAClC,iBAAiB,GAAoC,EAAE,CAAC;IAEhE,oEAAoE;IACpE,IAAW,gBAAgB;QACzB,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC;IACpD,CAAC;IAEM,KAAK,CAAC,WAAyB,EAAE,QAA6B;QACnE,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,GAAG,WAAW,CAAC;QAE9C,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,CAAC;YAChC,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC;YAEvC,wDAAwD;YACxD,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,WAAW,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;YAE9D,yDAAyD;YACzD,IAAI,aAAa,GAAG,KAAK,CAAC;YAC1B,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,GAAG,KAAK,IAAI,EAAE;gBACtC,wFAAwF;gBACxF,6CAA6C;gBAC7C,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,aAAa,GAAG,IAAI,CAAC;oBACrB,MAAM,OAAO,EAAE,CAAC;oBAChB,OAAO,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;oBAClC,OAAO,IAAI,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC;gBACrC,CAAC;YACH,CAAC,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,EAAU;QAC7B,8FAA8F;QAC9F,MAAM,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,OAAO;QAClB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC3B,MAAM,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtB,IAAI,CAAC,iBAAiB,GAAG,EAAE,CAAC;QAC5B,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IAC/B,CAAC;IAID,sFAAsF;IAC5E,KAAK,CAAC,QAAQ;QACtB,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACvF,CAAC;IAES,WAAW;QACnB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;CACF","sourcesContent":["import type {\n UnwatchCallback,\n WatchChangeCallback,\n Watcher,\n WatchOptions,\n WatchOptionsWithId,\n} from './types/Watcher.js';\n\n/**\n * Base class for watcher implementations.\n */\nexport abstract class BaseWatcher implements Watcher {\n /** Watcher is disposed or disposing */\n private _disposeCalled = false;\n /** Mapping from ID to change callback */\n protected _onChangeCallbacks: Record<string, WatchChangeCallback> = {};\n /** Mapping from ID to unwatch callback */\n private _unwatchCallbacks: Record<string, UnwatchCallback> = {};\n\n /** Number of IDs currently being watched (just used for testing) */\n public get activeWatchCount(): number {\n return Object.keys(this._unwatchCallbacks).length;\n }\n\n public watch(baseOptions: WatchOptions, onChange: WatchChangeCallback): UnwatchCallback {\n if (this._isDisposed()) {\n throw new Error('Watcher has been disposed.');\n }\n\n const { id = baseOptions.path } = baseOptions;\n\n if (!this._unwatchCallbacks[id]) {\n this._onChangeCallbacks[id] = onChange;\n\n // Watch the path using the child class's implementation\n const unwatch = this._watch({ ...baseOptions, id }, onChange);\n\n // Make an unwatch wrapper to clean up parent class state\n let calledUnwatch = false;\n this._unwatchCallbacks[id] = async () => {\n // The same unwatch callback might be returned multiple times in case of duplicate calls\n // for the same ID. Only do the cleanup once.\n if (!calledUnwatch) {\n calledUnwatch = true;\n await unwatch();\n delete this._unwatchCallbacks[id];\n delete this._onChangeCallbacks[id];\n }\n };\n }\n\n return this._unwatchCallbacks[id];\n }\n\n public async unwatch(id: string): Promise<void> {\n // The wrapper code at the callback definition in watch() handles cleanup of callback records.\n await this._unwatchCallbacks[id]?.();\n }\n\n public async dispose(): Promise<void> {\n this._disposeCalled = true;\n await this._dispose();\n this._unwatchCallbacks = {};\n this._onChangeCallbacks = {};\n }\n\n protected abstract _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): UnwatchCallback;\n\n /** By default, calls all the unwatch callbacks. Override for custom dispose logic. */\n protected async _dispose(): Promise<void> {\n await Promise.all(Object.values(this._unwatchCallbacks).map((unwatch) => unwatch()));\n }\n\n protected _isDisposed(): boolean {\n return this._disposeCalled;\n }\n}\n"]}
@@ -0,0 +1,9 @@
1
+ import type { UnwatchCallback, WatchChangeCallback, WatchOptionsWithId } from './types/Watcher.js';
2
+ import { BaseWatcher } from './BaseWatcher.js';
3
+ /**
4
+ * Watch for changes using chokidar in the current process.
5
+ */
6
+ export declare class DefaultWatcher extends BaseWatcher {
7
+ protected _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): UnwatchCallback;
8
+ }
9
+ //# sourceMappingURL=DefaultWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultWatcher.d.ts","sourceRoot":"","sources":["../src/DefaultWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C;;GAEG;AACH,qBAAa,cAAe,SAAQ,WAAW;IAI7C,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,eAAe;CAkB9F"}
@@ -0,0 +1,46 @@
1
+ import chokidar from 'chokidar';
2
+ import path from 'path';
3
+ import { BaseWatcher } from './BaseWatcher.js';
4
+ import { sourceFilesGlobs } from '@ms-cloudpack/path-utilities';
5
+ /**
6
+ * Watch for changes using chokidar in the current process.
7
+ */
8
+ export class DefaultWatcher extends BaseWatcher {
9
+ // TODO: Ideally if this is called multiple times with a different ID but the same root path and
10
+ // same watch paths, a single chokidar watcher should be created and shared. But this involves a
11
+ // lot of extra tracking logic and isn't essential until SSR is actually being used in an app.
12
+ _watch(options, onChange) {
13
+ const { path: rootPath, watchPaths = sourceFilesGlobs } = options;
14
+ const { positiveGlobs, negativeGlobs } = separateGlobs(watchPaths);
15
+ const paths = positiveGlobs.map((relPath) => path.resolve(rootPath, relPath));
16
+ const ignored = negativeGlobs.map((relPath) => path.resolve(rootPath, relPath));
17
+ // For efficiency, don't follow symlinks, and don't send 'add' events after the initial scan.
18
+ let watcher = chokidar
19
+ .watch(paths, { ignored, followSymlinks: false, ignoreInitial: true })
20
+ .on('change', (filePath) => {
21
+ onChange(filePath);
22
+ });
23
+ return async () => {
24
+ await watcher?.close();
25
+ watcher = undefined;
26
+ };
27
+ }
28
+ }
29
+ /**
30
+ * This function separates positive and negative globs to later on ignore the negative ones.
31
+ * This is necessary because chokidar does not support negative globs well.
32
+ */
33
+ function separateGlobs(globs) {
34
+ const positiveGlobs = [];
35
+ const negativeGlobs = [];
36
+ for (const glob of globs) {
37
+ if (glob.startsWith('!')) {
38
+ negativeGlobs.push(glob.slice(1));
39
+ }
40
+ else {
41
+ positiveGlobs.push(glob);
42
+ }
43
+ }
44
+ return { positiveGlobs, negativeGlobs };
45
+ }
46
+ //# sourceMappingURL=DefaultWatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefaultWatcher.js","sourceRoot":"","sources":["../src/DefaultWatcher.ts"],"names":[],"mappings":"AAAA,OAAO,QAA4B,MAAM,UAAU,CAAC;AACpD,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhE;;GAEG;AACH,MAAM,OAAO,cAAe,SAAQ,WAAW;IAC7C,gGAAgG;IAChG,gGAAgG;IAChG,8FAA8F;IACpF,MAAM,CAAC,OAA2B,EAAE,QAA6B;QACzE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,GAAG,gBAAgB,EAAE,GAAG,OAAO,CAAC;QAClE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;QAEhF,6FAA6F;QAC7F,IAAI,OAAO,GAA0B,QAAQ;aAC1C,KAAK,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC;aACrE,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE;YACzB,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;QAEL,OAAO,KAAK,IAAI,EAAE;YAChB,MAAM,OAAO,EAAE,KAAK,EAAE,CAAC;YACvB,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,KAAe;IACpC,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AAC1C,CAAC","sourcesContent":["import chokidar, { type FSWatcher } from 'chokidar';\nimport path from 'path';\nimport type { UnwatchCallback, WatchChangeCallback, WatchOptionsWithId } from './types/Watcher.js';\nimport { BaseWatcher } from './BaseWatcher.js';\nimport { sourceFilesGlobs } from '@ms-cloudpack/path-utilities';\n\n/**\n * Watch for changes using chokidar in the current process.\n */\nexport class DefaultWatcher extends BaseWatcher {\n // TODO: Ideally if this is called multiple times with a different ID but the same root path and\n // same watch paths, a single chokidar watcher should be created and shared. But this involves a\n // lot of extra tracking logic and isn't essential until SSR is actually being used in an app.\n protected _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): UnwatchCallback {\n const { path: rootPath, watchPaths = sourceFilesGlobs } = options;\n const { positiveGlobs, negativeGlobs } = separateGlobs(watchPaths);\n const paths = positiveGlobs.map((relPath) => path.resolve(rootPath, relPath));\n const ignored = negativeGlobs.map((relPath) => path.resolve(rootPath, relPath));\n\n // For efficiency, don't follow symlinks, and don't send 'add' events after the initial scan.\n let watcher: FSWatcher | undefined = chokidar\n .watch(paths, { ignored, followSymlinks: false, ignoreInitial: true })\n .on('change', (filePath) => {\n onChange(filePath);\n });\n\n return async () => {\n await watcher?.close();\n watcher = undefined;\n };\n }\n}\n\n/**\n * This function separates positive and negative globs to later on ignore the negative ones.\n * This is necessary because chokidar does not support negative globs well.\n */\nfunction separateGlobs(globs: string[]): { positiveGlobs: string[]; negativeGlobs: string[] } {\n const positiveGlobs: string[] = [];\n const negativeGlobs: string[] = [];\n\n for (const glob of globs) {\n if (glob.startsWith('!')) {\n negativeGlobs.push(glob.slice(1));\n } else {\n positiveGlobs.push(glob);\n }\n }\n\n return { positiveGlobs, negativeGlobs };\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import type { UnwatchCallback, WatchOptions } from './types/Watcher.js';
2
+ import { BaseWatcher } from './BaseWatcher.js';
3
+ /**
4
+ * Creates a watcher in a fork which can watch a package and notify the client when it changes.
5
+ */
6
+ export declare class ForkWatcher extends BaseWatcher {
7
+ private _child;
8
+ constructor();
9
+ protected _watch(options: Required<WatchOptions>): UnwatchCallback;
10
+ protected _dispose(): Promise<void>;
11
+ protected _isDisposed(): boolean;
12
+ /** Typed wrapper for `this._child.send()`. No-op if `_child` is disposed. */
13
+ private _sendMessage;
14
+ }
15
+ //# sourceMappingURL=ForkWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ForkWatcher.d.ts","sourceRoot":"","sources":["../src/ForkWatcher.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAK/C;;GAEG;AACH,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,MAAM,CAA2B;;IAyBzC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,GAAG,eAAe;cAQlD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAYzC,SAAS,CAAC,WAAW,IAAI,OAAO;IAIhC,6EAA6E;IAC7E,OAAO,CAAC,YAAY;CAGrB"}
@@ -0,0 +1,55 @@
1
+ import path from 'path';
2
+ import { fork } from 'child_process';
3
+ import { fileURLToPath } from 'url';
4
+ import { BaseWatcher } from './BaseWatcher.js';
5
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
6
+ const watcherForkWrapper = path.resolve(currentDir, 'watcherForkWrapper.js');
7
+ /**
8
+ * Creates a watcher in a fork which can watch a package and notify the client when it changes.
9
+ */
10
+ export class ForkWatcher extends BaseWatcher {
11
+ _child;
12
+ constructor() {
13
+ super();
14
+ this._child = fork(watcherForkWrapper);
15
+ this._child.on('message', (message) => {
16
+ switch (message.type) {
17
+ case 'change':
18
+ if (this._isDisposed()) {
19
+ throw new Error('Watcher has been disposed.');
20
+ }
21
+ this._onChangeCallbacks[message.id]?.(message.changedPath);
22
+ break;
23
+ case 'error':
24
+ console.error(`An error occurred while watching files: ${message.error}`);
25
+ break;
26
+ default:
27
+ console.warn(`Unknown message type: ${JSON.stringify(message)}`);
28
+ }
29
+ });
30
+ }
31
+ _watch(options) {
32
+ this._sendMessage({ type: 'watch', options });
33
+ return () => {
34
+ this._sendMessage({ type: 'unwatch', id: options.id });
35
+ };
36
+ }
37
+ async _dispose() {
38
+ await new Promise((r) => {
39
+ if (!this._child) {
40
+ return r();
41
+ }
42
+ this._child.on('close', r);
43
+ this._sendMessage({ type: 'shutdown' });
44
+ this._child = undefined;
45
+ });
46
+ }
47
+ _isDisposed() {
48
+ return !this._child || super._isDisposed();
49
+ }
50
+ /** Typed wrapper for `this._child.send()`. No-op if `_child` is disposed. */
51
+ _sendMessage(message) {
52
+ this._child?.send(message);
53
+ }
54
+ }
55
+ //# sourceMappingURL=ForkWatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ForkWatcher.js","sourceRoot":"","sources":["../src/ForkWatcher.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,IAAI,EAAqB,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAGpC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChE,MAAM,kBAAkB,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,uBAAuB,CAAC,CAAC;AAE7E;;GAEG;AACH,MAAM,OAAO,WAAY,SAAQ,WAAW;IAClC,MAAM,CAA2B;IAEzC;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAEvC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAqB,EAAE,EAAE;YAClD,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,KAAK,QAAQ;oBACX,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;wBACvB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;oBAChD,CAAC;oBACD,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;oBAC3D,MAAM;gBAER,KAAK,OAAO;oBACV,OAAO,CAAC,KAAK,CAAC,2CAA2C,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;oBAC1E,MAAM;gBAER;oBACE,OAAO,CAAC,IAAI,CAAC,yBAAyB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACrE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAES,MAAM,CAAC,OAA+B;QAC9C,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAE9C,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC;QACzD,CAAC,CAAC;IACJ,CAAC;IAES,KAAK,CAAC,QAAQ;QACtB,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE;YAC5B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACjB,OAAO,CAAC,EAAE,CAAC;YACb,CAAC;YAED,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC3B,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;YACxC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;QAC1B,CAAC,CAAC,CAAC;IACL,CAAC;IAES,WAAW;QACnB,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;IAC7C,CAAC;IAED,6EAA6E;IACrE,YAAY,CAAC,OAAsB;QACzC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF","sourcesContent":["import path from 'path';\nimport { fork, type ChildProcess } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport type { ChildMessage, ParentMessage } from './types/WatchMessages.js';\nimport type { UnwatchCallback, WatchOptions } from './types/Watcher.js';\nimport { BaseWatcher } from './BaseWatcher.js';\n\nconst currentDir = path.dirname(fileURLToPath(import.meta.url));\nconst watcherForkWrapper = path.resolve(currentDir, 'watcherForkWrapper.js');\n\n/**\n * Creates a watcher in a fork which can watch a package and notify the client when it changes.\n */\nexport class ForkWatcher extends BaseWatcher {\n private _child: ChildProcess | undefined;\n\n constructor() {\n super();\n this._child = fork(watcherForkWrapper);\n\n this._child.on('message', (message: ChildMessage) => {\n switch (message.type) {\n case 'change':\n if (this._isDisposed()) {\n throw new Error('Watcher has been disposed.');\n }\n this._onChangeCallbacks[message.id]?.(message.changedPath);\n break;\n\n case 'error':\n console.error(`An error occurred while watching files: ${message.error}`);\n break;\n\n default:\n console.warn(`Unknown message type: ${JSON.stringify(message)}`);\n }\n });\n }\n\n protected _watch(options: Required<WatchOptions>): UnwatchCallback {\n this._sendMessage({ type: 'watch', options });\n\n return () => {\n this._sendMessage({ type: 'unwatch', id: options.id });\n };\n }\n\n protected async _dispose(): Promise<void> {\n await new Promise<void>((r) => {\n if (!this._child) {\n return r();\n }\n\n this._child.on('close', r);\n this._sendMessage({ type: 'shutdown' });\n this._child = undefined;\n });\n }\n\n protected _isDisposed(): boolean {\n return !this._child || super._isDisposed();\n }\n\n /** Typed wrapper for `this._child.send()`. No-op if `_child` is disposed. */\n private _sendMessage(message: ParentMessage): void {\n this._child?.send(message);\n }\n}\n"]}
@@ -1,6 +1,14 @@
1
1
  import type { Watcher } from './types/Watcher.js';
2
2
  /**
3
- * Creates a watcher which can watch a package and notify the client when it changes.
3
+ * Create a file watcher. By default, the watcher will run in the main thread.
4
+ * Use `{ type: 'fork' }` to run the watcher in a forked process.
4
5
  */
5
- export declare function createWatcher(): Watcher;
6
+ export declare function createWatcher(options?: {
7
+ /**
8
+ * By default, the watcher will run in the main thread.
9
+ * Specify `'fork'` to run the watcher in a forked process.
10
+ * @default 'default'
11
+ */
12
+ type?: 'default' | 'fork';
13
+ }): Watcher;
6
14
  //# sourceMappingURL=createWatcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createWatcher.d.ts","sourceRoot":"","sources":["../src/createWatcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,oBAAoB,CAAC;AAKhE;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,CA4CvC"}
1
+ {"version":3,"file":"createWatcher.d.ts","sourceRoot":"","sources":["../src/createWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE;IACtC;;;;OAIG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;CAC3B,GAAG,OAAO,CAKV"}
@@ -1,44 +1,13 @@
1
- import chokidar from 'chokidar';
2
- import { resolve } from 'path';
3
- // TODO: This list of watched paths is hardcoded, but really it shouldn't be.
4
- const defaultWatchPaths = ['src/**', '*.json', '!**/node_modules/**'];
1
+ import { DefaultWatcher } from './DefaultWatcher.js';
2
+ import { ForkWatcher } from './ForkWatcher.js';
5
3
  /**
6
- * Creates a watcher which can watch a package and notify the client when it changes.
4
+ * Create a file watcher. By default, the watcher will run in the main thread.
5
+ * Use `{ type: 'fork' }` to run the watcher in a forked process.
7
6
  */
8
- export function createWatcher() {
9
- const watchedPaths = new Set();
10
- const watchers = new Set();
11
- return {
12
- watch(options, onPackageChanged) {
13
- const { path, watchPaths = defaultWatchPaths } = options;
14
- const paths = watchPaths.map((relPath) => resolve(path, relPath)).filter((p) => !watchedPaths.has(p));
15
- let watcher;
16
- if (paths.length) {
17
- // Add paths to watchedPaths to avoid watching them again.
18
- paths.forEach((p) => watchedPaths.add(p));
19
- // Watch source and register a handler which will rebundle the changed package and notify the client on
20
- // success. Note that if failures happen, the client will receive the error notification automatically.
21
- watcher = chokidar.watch(paths).on('change', onPackageChanged);
22
- watchers.add(watcher);
23
- }
24
- // Return a dispose function to individually clean up.
25
- return async () => {
26
- if (watcher) {
27
- const w = watcher;
28
- watcher = undefined;
29
- watchers.delete(w);
30
- for (const watchedPath of paths) {
31
- watchedPaths.delete(watchedPath);
32
- }
33
- await w.close();
34
- }
35
- };
36
- },
37
- async unwatchAll() {
38
- await Promise.all(Array.from(watchers).map((w) => w.close()));
39
- watchers.clear();
40
- watchedPaths.clear();
41
- },
42
- };
7
+ export function createWatcher(options) {
8
+ if (options?.type === 'fork') {
9
+ return new ForkWatcher();
10
+ }
11
+ return new DefaultWatcher();
43
12
  }
44
13
  //# sourceMappingURL=createWatcher.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createWatcher.js","sourceRoot":"","sources":["../src/createWatcher.ts"],"names":[],"mappings":"AACA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAG/B,6EAA6E;AAC7E,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,qBAAqB,CAAC,CAAC;AAEtE;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAa,CAAC;IAEtC,OAAO;QACL,KAAK,CAAC,OAAqB,EAAE,gBAA4B;YACvD,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,iBAAiB,EAAE,GAAG,OAAO,CAAC;YACzD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YACtG,IAAI,OAA8B,CAAC;YAEnC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjB,0DAA0D;gBAC1D,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;gBAE1C,uGAAuG;gBACvG,uGAAuG;gBACvG,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC;gBAE/D,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC;YAED,sDAAsD;YACtD,OAAO,KAAK,IAAI,EAAE;gBAChB,IAAI,OAAO,EAAE,CAAC;oBACZ,MAAM,CAAC,GAAG,OAAO,CAAC;oBAElB,OAAO,GAAG,SAAS,CAAC;oBACpB,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;oBAEnB,KAAK,MAAM,WAAW,IAAI,KAAK,EAAE,CAAC;wBAChC,YAAY,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;oBACnC,CAAC;oBAED,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC;gBAClB,CAAC;YACH,CAAC,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,UAAU;YACd,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;YAC9D,QAAQ,CAAC,KAAK,EAAE,CAAC;YACjB,YAAY,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { FSWatcher } from 'chokidar';\nimport chokidar from 'chokidar';\nimport { resolve } from 'path';\nimport type { Watcher, WatchOptions } from './types/Watcher.js';\n\n// TODO: This list of watched paths is hardcoded, but really it shouldn't be.\nconst defaultWatchPaths = ['src/**', '*.json', '!**/node_modules/**'];\n\n/**\n * Creates a watcher which can watch a package and notify the client when it changes.\n */\nexport function createWatcher(): Watcher {\n const watchedPaths = new Set<string>();\n const watchers = new Set<FSWatcher>();\n\n return {\n watch(options: WatchOptions, onPackageChanged: () => void) {\n const { path, watchPaths = defaultWatchPaths } = options;\n const paths = watchPaths.map((relPath) => resolve(path, relPath)).filter((p) => !watchedPaths.has(p));\n let watcher: FSWatcher | undefined;\n\n if (paths.length) {\n // Add paths to watchedPaths to avoid watching them again.\n paths.forEach((p) => watchedPaths.add(p));\n\n // Watch source and register a handler which will rebundle the changed package and notify the client on\n // success. Note that if failures happen, the client will receive the error notification automatically.\n watcher = chokidar.watch(paths).on('change', onPackageChanged);\n\n watchers.add(watcher);\n }\n\n // Return a dispose function to individually clean up.\n return async () => {\n if (watcher) {\n const w = watcher;\n\n watcher = undefined;\n watchers.delete(w);\n\n for (const watchedPath of paths) {\n watchedPaths.delete(watchedPath);\n }\n\n await w.close();\n }\n };\n },\n\n async unwatchAll() {\n await Promise.all(Array.from(watchers).map((w) => w.close()));\n watchers.clear();\n watchedPaths.clear();\n },\n };\n}\n"]}
1
+ {"version":3,"file":"createWatcher.js","sourceRoot":"","sources":["../src/createWatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAO7B;IACC,IAAI,OAAO,EAAE,IAAI,KAAK,MAAM,EAAE,CAAC;QAC7B,OAAO,IAAI,WAAW,EAAE,CAAC;IAC3B,CAAC;IACD,OAAO,IAAI,cAAc,EAAE,CAAC;AAC9B,CAAC","sourcesContent":["import { DefaultWatcher } from './DefaultWatcher.js';\nimport { ForkWatcher } from './ForkWatcher.js';\nimport type { Watcher } from './types/Watcher.js';\n\n/**\n * Create a file watcher. By default, the watcher will run in the main thread.\n * Use `{ type: 'fork' }` to run the watcher in a forked process.\n */\nexport function createWatcher(options?: {\n /**\n * By default, the watcher will run in the main thread.\n * Specify `'fork'` to run the watcher in a forked process.\n * @default 'default'\n */\n type?: 'default' | 'fork';\n}): Watcher {\n if (options?.type === 'fork') {\n return new ForkWatcher();\n }\n return new DefaultWatcher();\n}\n"]}
package/lib/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- export { createWatcher } from './createWatcher.js';
2
- export { createWatcherInFork } from './createWatcherInFork.js';
3
1
  export type { Watcher, WatchOptions } from './types/Watcher.js';
2
+ export { createWatcher } from './createWatcher.js';
4
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
package/lib/index.js CHANGED
@@ -1,3 +1,2 @@
1
1
  export { createWatcher } from './createWatcher.js';
2
- export { createWatcherInFork } from './createWatcherInFork.js';
3
2
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["export { createWatcher } from './createWatcher.js';\nexport { createWatcherInFork } from './createWatcherInFork.js';\nexport type { Watcher, WatchOptions } from './types/Watcher.js';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export type { Watcher, WatchOptions } from './types/Watcher.js';\nexport { createWatcher } from './createWatcher.js';\n"]}
@@ -1,23 +1,25 @@
1
- import type { WatchOptions } from './Watcher.js';
2
- export interface WatchMessage {
1
+ import type { WatchOptions, WatchOptionsWithId } from './Watcher.js';
2
+ type WatchMessage = {
3
3
  type: 'watch';
4
- options: WatchOptions;
5
- }
6
- export interface UnwatchMessage {
4
+ options: Required<WatchOptions>;
5
+ };
6
+ type UnwatchMessage = Pick<WatchOptionsWithId, 'id'> & {
7
7
  type: 'unwatch';
8
- path: string;
9
- }
10
- export interface ShutdownMessage {
8
+ };
9
+ type ShutdownMessage = {
11
10
  type: 'shutdown';
12
- }
11
+ };
12
+ /** Message from the parent to the child in `ForkWatcher` */
13
13
  export type ParentMessage = WatchMessage | UnwatchMessage | ShutdownMessage;
14
- export type ChangeMessage = {
14
+ type ChangeMessage = Pick<WatchOptionsWithId, 'id'> & {
15
15
  type: 'change';
16
- path: string;
16
+ changedPath: string;
17
17
  };
18
- export type ErrorMessage = {
18
+ type ErrorMessage = {
19
19
  type: 'error';
20
20
  error: Error;
21
21
  };
22
+ /** Message from the child to the parent in `ForkWatcher` */
22
23
  export type ChildMessage = ChangeMessage | ErrorMessage;
24
+ export {};
23
25
  //# sourceMappingURL=WatchMessages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WatchMessages.d.ts","sourceRoot":"","sources":["../../src/types/WatchMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,YAAY,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,eAAe,CAAC;AAE5E,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC"}
1
+ {"version":3,"file":"WatchMessages.d.ts","sourceRoot":"","sources":["../../src/types/WatchMessages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAErE,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,QAAQ,CAAC,YAAY,CAAC,CAAC;CACjC,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,GAAG;IACrD,IAAI,EAAE,SAAS,CAAC;CACjB,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,YAAY,GAAG,cAAc,GAAG,eAAe,CAAC;AAE5E,KAAK,aAAa,GAAG,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,GAAG;IACpD,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,KAAK,YAAY,GAAG;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;CACd,CAAC;AAEF,4DAA4D;AAC5D,MAAM,MAAM,YAAY,GAAG,aAAa,GAAG,YAAY,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"WatchMessages.js","sourceRoot":"","sources":["../../src/types/WatchMessages.ts"],"names":[],"mappings":"","sourcesContent":["import type { WatchOptions } from './Watcher.js';\n\nexport interface WatchMessage {\n type: 'watch';\n options: WatchOptions;\n}\n\nexport interface UnwatchMessage {\n type: 'unwatch';\n path: string;\n}\n\nexport interface ShutdownMessage {\n type: 'shutdown';\n}\n\nexport type ParentMessage = WatchMessage | UnwatchMessage | ShutdownMessage;\n\nexport type ChangeMessage = {\n type: 'change';\n path: string;\n};\n\nexport type ErrorMessage = {\n type: 'error';\n error: Error;\n};\n\nexport type ChildMessage = ChangeMessage | ErrorMessage;\n"]}
1
+ {"version":3,"file":"WatchMessages.js","sourceRoot":"","sources":["../../src/types/WatchMessages.ts"],"names":[],"mappings":"","sourcesContent":["import type { WatchOptions, WatchOptionsWithId } from './Watcher.js';\n\ntype WatchMessage = {\n type: 'watch';\n options: Required<WatchOptions>;\n};\n\ntype UnwatchMessage = Pick<WatchOptionsWithId, 'id'> & {\n type: 'unwatch';\n};\n\ntype ShutdownMessage = {\n type: 'shutdown';\n};\n\n/** Message from the parent to the child in `ForkWatcher` */\nexport type ParentMessage = WatchMessage | UnwatchMessage | ShutdownMessage;\n\ntype ChangeMessage = Pick<WatchOptionsWithId, 'id'> & {\n type: 'change';\n changedPath: string;\n};\n\ntype ErrorMessage = {\n type: 'error';\n error: Error;\n};\n\n/** Message from the child to the parent in `ForkWatcher` */\nexport type ChildMessage = ChangeMessage | ErrorMessage;\n"]}
@@ -1,9 +1,36 @@
1
1
  export interface WatchOptions {
2
+ /**
3
+ * Absolute path to the root directory where watching should happen.
4
+ */
2
5
  path: string;
6
+ /**
7
+ * ID for the watch job (defaults to `path`).
8
+ * If you call `watch` twice using the same `id`, subsequent calls will be ignored.
9
+ */
10
+ id?: string;
11
+ /**
12
+ * Path globs to watch, relative to `path`.
13
+ * Defaults are under "@ms-cloudpack/path-utilities" in `sourceFilesGlobs`.
14
+ */
3
15
  watchPaths?: string[];
4
16
  }
17
+ export type WatchOptionsWithId = WatchOptions & Required<Pick<WatchOptions, 'id'>>;
18
+ /**
19
+ * Callback when a watched path changes.
20
+ * @param changedPath Absolute path to the file that changed.
21
+ */
22
+ export type WatchChangeCallback = (changedPath: string) => void;
23
+ /** Callback to unwatch a path */
24
+ export type UnwatchCallback = () => void | Promise<void>;
5
25
  export interface Watcher {
6
- watch(options: WatchOptions, onPackageChanged: () => void): () => Promise<void>;
7
- unwatchAll(): Promise<void>;
26
+ /**
27
+ * Watch a path for changes.
28
+ * @returns Callback to unwatch the path.
29
+ */
30
+ watch(options: WatchOptions, onChange: WatchChangeCallback): UnwatchCallback;
31
+ /** Remove watchers for a specific ID (path) */
32
+ unwatch(id: string): Promise<void>;
33
+ /** Dispose all watchers. The Watcher object can't be reused afer this. */
34
+ dispose(): Promise<void>;
8
35
  }
9
36
  //# sourceMappingURL=Watcher.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Watcher.d.ts","sourceRoot":"","sources":["../../src/types/Watcher.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChF,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B"}
1
+ {"version":3,"file":"Watcher.d.ts","sourceRoot":"","sources":["../../src/types/Watcher.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC;AAEnF;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;AAEhE,iCAAiC;AACjC,MAAM,MAAM,eAAe,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEzD,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,mBAAmB,GAAG,eAAe,CAAC;IAE7E,+CAA+C;IAC/C,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnC,0EAA0E;IAC1E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"Watcher.js","sourceRoot":"","sources":["../../src/types/Watcher.ts"],"names":[],"mappings":"","sourcesContent":["export interface WatchOptions {\n path: string;\n watchPaths?: string[];\n}\n\nexport interface Watcher {\n watch(options: WatchOptions, onPackageChanged: () => void): () => Promise<void>;\n unwatchAll(): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"Watcher.js","sourceRoot":"","sources":["../../src/types/Watcher.ts"],"names":[],"mappings":"","sourcesContent":["export interface WatchOptions {\n /**\n * Absolute path to the root directory where watching should happen.\n */\n path: string;\n\n /**\n * ID for the watch job (defaults to `path`).\n * If you call `watch` twice using the same `id`, subsequent calls will be ignored.\n */\n id?: string;\n\n /**\n * Path globs to watch, relative to `path`.\n * Defaults are under \"@ms-cloudpack/path-utilities\" in `sourceFilesGlobs`.\n */\n watchPaths?: string[];\n}\n\nexport type WatchOptionsWithId = WatchOptions & Required<Pick<WatchOptions, 'id'>>;\n\n/**\n * Callback when a watched path changes.\n * @param changedPath Absolute path to the file that changed.\n */\nexport type WatchChangeCallback = (changedPath: string) => void;\n\n/** Callback to unwatch a path */\nexport type UnwatchCallback = () => void | Promise<void>;\n\nexport interface Watcher {\n /**\n * Watch a path for changes.\n * @returns Callback to unwatch the path.\n */\n watch(options: WatchOptions, onChange: WatchChangeCallback): UnwatchCallback;\n\n /** Remove watchers for a specific ID (path) */\n unwatch(id: string): Promise<void>;\n\n /** Dispose all watchers. The Watcher object can't be reused afer this. */\n dispose(): Promise<void>;\n}\n"]}
@@ -1,37 +1,36 @@
1
- import { createWatcher } from './createWatcher.js';
1
+ import { DefaultWatcher } from './DefaultWatcher.js';
2
2
  /**
3
- * Initializes the watcher in a child process.
3
+ * `ForkWatcher` runs this file to initialize the watcher in a child process.
4
4
  */
5
5
  function initializeWatcher() {
6
- const watcher = createWatcher();
7
- const unwatches = new Map();
6
+ const watcher = new DefaultWatcher();
8
7
  process.on('message', (message) => {
9
8
  (async () => {
10
9
  switch (message.type) {
11
10
  case 'watch':
12
11
  {
13
- const { path } = message.options;
14
- const unwatch = watcher.watch(message.options, () => {
15
- process.send?.({ type: 'change', path });
12
+ watcher.watch(message.options, (changedPath) => {
13
+ sendMessage({ type: 'change', id: message.options.id, changedPath });
16
14
  });
17
- unwatches.set(path, unwatch);
18
15
  }
19
16
  break;
20
17
  case 'unwatch':
21
- await unwatches.get(message.path)?.();
22
- unwatches.delete(message.path);
18
+ await watcher.unwatch(message.id);
23
19
  break;
24
20
  case 'shutdown':
25
- await watcher.unwatchAll();
26
- // eslint-disable-next-line no-restricted-properties
21
+ await watcher.dispose();
22
+ // eslint-disable-next-line no-restricted-properties -- rule isn't relevant for exiting a child process
27
23
  process.exit(0);
28
- break;
29
24
  }
30
25
  })().catch((error) => {
31
- process.send?.({ type: 'error', error: error });
26
+ sendMessage({ type: 'error', error: error });
32
27
  });
33
28
  });
34
29
  }
30
+ /** Typed wrapper for `process.send()` */
31
+ function sendMessage(message) {
32
+ process.send?.(message);
33
+ }
35
34
  // Prevent the process from exiting immediately.
36
35
  // This is handled by the parent process instead with the shutdown message.
37
36
  process.on('SIGINT', () => { });
@@ -1 +1 @@
1
- {"version":3,"file":"watcherForkWrapper.js","sourceRoot":"","sources":["../src/watcherForkWrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAInD;;GAEG;AACH,SAAS,iBAAiB;IACxB,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAwC,CAAC;IAElE,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAsB,EAAE,EAAE;QAC/C,CAAC,KAAK,IAAI,EAAE;YACV,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,KAAK,OAAO;oBACV,CAAC;wBACC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;wBACjC,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,EAAE;4BAClD,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;wBAC3C,CAAC,CAAC,CAAC;wBACH,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;oBAC/B,CAAC;oBACD,MAAM;gBAER,KAAK,SAAS;oBACZ,MAAM,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;oBACtC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;oBAC/B,MAAM;gBAER,KAAK,UAAU;oBACb,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;oBAC3B,oDAAoD;oBACpD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAChB,MAAM;YACV,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,CAAC,CAAC;QAC3D,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,gDAAgD;AAChD,2EAA2E;AAC3E,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAE/B,iBAAiB,EAAE,CAAC","sourcesContent":["import { createWatcher } from './createWatcher.js';\nimport type { Watcher } from './types/Watcher.js';\nimport type { ParentMessage } from './types/WatchMessages.js';\n\n/**\n * Initializes the watcher in a child process.\n */\nfunction initializeWatcher() {\n const watcher = createWatcher();\n const unwatches = new Map<string, ReturnType<Watcher['watch']>>();\n\n process.on('message', (message: ParentMessage) => {\n (async () => {\n switch (message.type) {\n case 'watch':\n {\n const { path } = message.options;\n const unwatch = watcher.watch(message.options, () => {\n process.send?.({ type: 'change', path });\n });\n unwatches.set(path, unwatch);\n }\n break;\n\n case 'unwatch':\n await unwatches.get(message.path)?.();\n unwatches.delete(message.path);\n break;\n\n case 'shutdown':\n await watcher.unwatchAll();\n // eslint-disable-next-line no-restricted-properties\n process.exit(0);\n break;\n }\n })().catch((error) => {\n process.send?.({ type: 'error', error: error as Error });\n });\n });\n}\n\n// Prevent the process from exiting immediately.\n// This is handled by the parent process instead with the shutdown message.\nprocess.on('SIGINT', () => {});\n\ninitializeWatcher();\n"]}
1
+ {"version":3,"file":"watcherForkWrapper.js","sourceRoot":"","sources":["../src/watcherForkWrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD;;GAEG;AACH,SAAS,iBAAiB;IACxB,MAAM,OAAO,GAAG,IAAI,cAAc,EAAE,CAAC;IAErC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAsB,EAAE,EAAE;QAC/C,CAAC,KAAK,IAAI,EAAE;YACV,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;gBACrB,KAAK,OAAO;oBACV,CAAC;wBACC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,WAAW,EAAE,EAAE;4BAC7C,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;wBACvE,CAAC,CAAC,CAAC;oBACL,CAAC;oBACD,MAAM;gBAER,KAAK,SAAS;oBACZ,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;oBAClC,MAAM;gBAER,KAAK,UAAU;oBACb,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC;oBACxB,uGAAuG;oBACvG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACnB,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAED,yCAAyC;AACzC,SAAS,WAAW,CAAC,OAAqB;IACxC,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,CAAC;AAC1B,CAAC;AAED,gDAAgD;AAChD,2EAA2E;AAC3E,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AAE/B,iBAAiB,EAAE,CAAC","sourcesContent":["import { DefaultWatcher } from './DefaultWatcher.js';\nimport type { ChildMessage, ParentMessage } from './types/WatchMessages.js';\n\n/**\n * `ForkWatcher` runs this file to initialize the watcher in a child process.\n */\nfunction initializeWatcher() {\n const watcher = new DefaultWatcher();\n\n process.on('message', (message: ParentMessage) => {\n (async () => {\n switch (message.type) {\n case 'watch':\n {\n watcher.watch(message.options, (changedPath) => {\n sendMessage({ type: 'change', id: message.options.id, changedPath });\n });\n }\n break;\n\n case 'unwatch':\n await watcher.unwatch(message.id);\n break;\n\n case 'shutdown':\n await watcher.dispose();\n // eslint-disable-next-line no-restricted-properties -- rule isn't relevant for exiting a child process\n process.exit(0);\n }\n })().catch((error) => {\n sendMessage({ type: 'error', error: error as Error });\n });\n });\n}\n\n/** Typed wrapper for `process.send()` */\nfunction sendMessage(message: ChildMessage) {\n process.send?.(message);\n}\n\n// Prevent the process from exiting immediately.\n// This is handled by the parent process instead with the shutdown message.\nprocess.on('SIGINT', () => {});\n\ninitializeWatcher();\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ms-cloudpack/file-watcher",
3
- "version": "0.1.4",
3
+ "version": "0.3.0",
4
4
  "description": "A file watcher abstraction for use with Cloudpack.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,9 +15,11 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
+ "@ms-cloudpack/path-utilities": "^3.1.0",
18
19
  "chokidar": "^3.5.3"
19
20
  },
20
21
  "devDependencies": {
22
+ "@ms-cloudpack/environment": "*",
21
23
  "@ms-cloudpack/eslint-plugin-internal": "^0.0.1",
22
24
  "@ms-cloudpack/scripts": "^0.0.1",
23
25
  "@ms-cloudpack/test-utilities": "^0.5.0"
@@ -1,6 +0,0 @@
1
- import type { Watcher } from './types/Watcher.js';
2
- /**
3
- * Creates a watcher in a fork which can watch a package and notify the client when it changes.
4
- */
5
- export declare function createWatcherInFork(): Watcher;
6
- //# sourceMappingURL=createWatcherInFork.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createWatcherInFork.d.ts","sourceRoot":"","sources":["../src/createWatcherInFork.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAgB,MAAM,oBAAoB,CAAC;AAKhE;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CA0C7C"}
@@ -1,47 +0,0 @@
1
- import { resolve, dirname } from 'path';
2
- import { fork } from 'child_process';
3
- import { fileURLToPath } from 'url';
4
- const currentDir = dirname(fileURLToPath(import.meta.url));
5
- const fileWatcher = resolve(currentDir, 'watcherForkWrapper.js');
6
- /**
7
- * Creates a watcher in a fork which can watch a package and notify the client when it changes.
8
- */
9
- export function createWatcherInFork() {
10
- const callbackMap = new Map();
11
- const child = fork(fileWatcher);
12
- child.on('message', (message) => {
13
- switch (message.type) {
14
- case 'change':
15
- callbackMap.get(message.path)?.();
16
- break;
17
- case 'error':
18
- console.error(`An error occurred while watching files: ${message.error}`);
19
- break;
20
- default:
21
- console.warn(`Unknown message type: ${JSON.stringify(message)}`);
22
- }
23
- });
24
- return {
25
- watch(options, onPackageChanged) {
26
- const { path } = options;
27
- const watchMessage = { type: 'watch', options };
28
- child.send(watchMessage);
29
- callbackMap.set(path, onPackageChanged);
30
- // Marking this function as async to match the return type of the function in createWatcher.ts
31
- // This function is only ever used for testing purposes, so it's fine to mark it as async.
32
- // eslint-disable-next-line @typescript-eslint/require-await
33
- return async () => {
34
- const unwatchMessage = { type: 'unwatch', path };
35
- child.send(unwatchMessage);
36
- };
37
- },
38
- async unwatchAll() {
39
- await new Promise((r) => {
40
- child.on('close', r);
41
- const shutDownMessage = { type: 'shutdown' };
42
- child.send(shutDownMessage);
43
- });
44
- },
45
- };
46
- }
47
- //# sourceMappingURL=createWatcherInFork.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"createWatcherInFork.js","sourceRoot":"","sources":["../src/createWatcherInFork.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAIpC,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3D,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,EAAE,uBAAuB,CAAC,CAAC;AAEjE;;GAEG;AACH,MAAM,UAAU,mBAAmB;IACjC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,OAAqB,EAAE,EAAE;QAC5C,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBAClC,MAAM;YAER,KAAK,OAAO;gBACV,OAAO,CAAC,KAAK,CAAC,2CAA2C,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;gBAC1E,MAAM;YAER;gBACE,OAAO,CAAC,IAAI,CAAC,yBAAyB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK,CAAC,OAAqB,EAAE,gBAA4B;YACvD,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;YACzB,MAAM,YAAY,GAAiB,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YAC9D,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACzB,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAExC,8FAA8F;YAC9F,0FAA0F;YAC1F,4DAA4D;YAC5D,OAAO,KAAK,IAAI,EAAE;gBAChB,MAAM,cAAc,GAAmB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;gBACjE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YAC7B,CAAC,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,UAAU;YACd,MAAM,IAAI,OAAO,CAAO,CAAC,CAAC,EAAE,EAAE;gBAC5B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;gBACrB,MAAM,eAAe,GAAoB,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;gBAC9D,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import { resolve, dirname } from 'path';\nimport { fork } from 'child_process';\nimport { fileURLToPath } from 'url';\nimport type { ChildMessage, ShutdownMessage, UnwatchMessage, WatchMessage } from './types/WatchMessages.js';\nimport type { Watcher, WatchOptions } from './types/Watcher.js';\n\nconst currentDir = dirname(fileURLToPath(import.meta.url));\nconst fileWatcher = resolve(currentDir, 'watcherForkWrapper.js');\n\n/**\n * Creates a watcher in a fork which can watch a package and notify the client when it changes.\n */\nexport function createWatcherInFork(): Watcher {\n const callbackMap = new Map<string, () => void>();\n const child = fork(fileWatcher);\n child.on('message', (message: ChildMessage) => {\n switch (message.type) {\n case 'change':\n callbackMap.get(message.path)?.();\n break;\n\n case 'error':\n console.error(`An error occurred while watching files: ${message.error}`);\n break;\n\n default:\n console.warn(`Unknown message type: ${JSON.stringify(message)}`);\n }\n });\n\n return {\n watch(options: WatchOptions, onPackageChanged: () => void) {\n const { path } = options;\n const watchMessage: WatchMessage = { type: 'watch', options };\n child.send(watchMessage);\n callbackMap.set(path, onPackageChanged);\n\n // Marking this function as async to match the return type of the function in createWatcher.ts\n // This function is only ever used for testing purposes, so it's fine to mark it as async.\n // eslint-disable-next-line @typescript-eslint/require-await\n return async () => {\n const unwatchMessage: UnwatchMessage = { type: 'unwatch', path };\n child.send(unwatchMessage);\n };\n },\n\n async unwatchAll() {\n await new Promise<void>((r) => {\n child.on('close', r);\n const shutDownMessage: ShutdownMessage = { type: 'shutdown' };\n child.send(shutDownMessage);\n });\n },\n };\n}\n"]}