@ms-cloudpack/file-watcher 0.3.12 → 0.4.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
@@ -13,7 +13,7 @@ const watcher = createWatcher();
13
13
  2. Subscribe to a particular package path using `watch`. (Returns an `unwatch` function.)
14
14
 
15
15
  ```ts
16
- const unwatch = watcher.watch({ path: 'path/to/package' }, () => console.log(`package changed`));
16
+ const unwatch = await watcher.watch({ path: 'path/to/package' }, () => console.log(`package changed`));
17
17
  ```
18
18
 
19
19
  3. To dispose, call the returned `unwatch`, or call `watcher.dispose` to unsubscribe from all watchers.
@@ -34,4 +34,4 @@ await watcher.dispose();
34
34
 
35
35
  - `path` (`string`): The absolute root path to be watched.
36
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`.
37
+ - `watchPaths` (`string[]`, optional): Relative paths/globs from the root path. Currently, only negative globs (starting with `!`) are supported and will be used to ignore files. Positive globs are ignored - the watcher will watch all files in the root path by default. Defaults are under "@ms-cloudpack/path-utilities" in `sourceFilesGlobs`.
@@ -11,10 +11,10 @@ export declare abstract class BaseWatcher implements Watcher {
11
11
  private _unwatchCallbacks;
12
12
  /** Number of IDs currently being watched (just used for testing) */
13
13
  get activeWatchCount(): number;
14
- watch(baseOptions: WatchOptions, onChange: WatchChangeCallback): UnwatchCallback;
14
+ watch(baseOptions: WatchOptions, onChange: WatchChangeCallback): Promise<UnwatchCallback>;
15
15
  unwatch(id: string): Promise<void>;
16
16
  dispose(): Promise<void>;
17
- protected abstract _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): UnwatchCallback;
17
+ protected abstract _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): Promise<UnwatchCallback> | UnwatchCallback;
18
18
  /** By default, calls all the unwatch callbacks. Override for custom dispose logic. */
19
19
  protected _dispose(): Promise<void>;
20
20
  protected _isDisposed(): boolean;
@@ -1 +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"}
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;IAEY,KAAK,CAAC,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;IA8BzF,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKlC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAOrC,SAAS,CAAC,QAAQ,CAAC,MAAM,CACvB,OAAO,EAAE,kBAAkB,EAC3B,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe;IAE7C,sFAAsF;cACtE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAIzC,SAAS,CAAC,WAAW,IAAI,OAAO;CAGjC"}
@@ -12,7 +12,7 @@ export class BaseWatcher {
12
12
  get activeWatchCount() {
13
13
  return Object.keys(this._unwatchCallbacks).length;
14
14
  }
15
- watch(baseOptions, onChange) {
15
+ async watch(baseOptions, onChange) {
16
16
  if (this._isDisposed()) {
17
17
  throw new Error('Watcher has been disposed.');
18
18
  }
@@ -20,7 +20,7 @@ export class BaseWatcher {
20
20
  if (!this._unwatchCallbacks[id]) {
21
21
  this._onChangeCallbacks[id] = onChange;
22
22
  // Watch the path using the child class's implementation
23
- const unwatch = this._watch({ ...baseOptions, id }, onChange);
23
+ const unwatch = await this._watch({ ...baseOptions, id }, onChange);
24
24
  // Make an unwatch wrapper to clean up parent class state
25
25
  let calledUnwatch = false;
26
26
  this._unwatchCallbacks[id] = async () => {
@@ -1 +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"]}
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,KAAK,CAAC,WAAyB,EAAE,QAA6B;QACzE,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,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,WAAW,EAAE,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;YAEpE,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;IAOD,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 async watch(baseOptions: WatchOptions, onChange: WatchChangeCallback): Promise<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 = await 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(\n options: WatchOptionsWithId,\n onChange: WatchChangeCallback,\n ): Promise<UnwatchCallback> | 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"]}
@@ -1 +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"}
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;AAI/C;;GAEG;AACH,qBAAa,cAAe,SAAQ,WAAW;IAC7C,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,eAAe;CAwB9F"}
@@ -2,13 +2,11 @@ import chokidar from 'chokidar';
2
2
  import path from 'path';
3
3
  import { BaseWatcher } from './BaseWatcher.js';
4
4
  import { sourceFilesGlobs } from '@ms-cloudpack/path-utilities';
5
+ import { separateGlobs } from './separateGlobs.js';
5
6
  /**
6
7
  * Watch for changes using chokidar in the current process.
7
8
  */
8
9
  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
10
  _watch(options, onChange) {
13
11
  const { path: rootPath, watchPaths = sourceFilesGlobs } = options;
14
12
  const { positiveGlobs, negativeGlobs } = separateGlobs(watchPaths);
@@ -18,7 +16,13 @@ export class DefaultWatcher extends BaseWatcher {
18
16
  let watcher = chokidar
19
17
  .watch(paths, { ignored, followSymlinks: false, ignoreInitial: true })
20
18
  .on('change', (filePath) => {
21
- onChange(filePath);
19
+ onChange([filePath]);
20
+ })
21
+ .on('add', (filePath) => {
22
+ onChange([filePath]);
23
+ })
24
+ .on('unlink', (filePath) => {
25
+ onChange([filePath]);
22
26
  });
23
27
  return async () => {
24
28
  await watcher?.close();
@@ -26,21 +30,4 @@ export class DefaultWatcher extends BaseWatcher {
26
30
  };
27
31
  }
28
32
  }
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
33
  //# sourceMappingURL=DefaultWatcher.js.map
@@ -1 +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"]}
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;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;GAEG;AACH,MAAM,OAAO,cAAe,SAAQ,WAAW;IACnC,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,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvB,CAAC,CAAC;aACD,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;YACtB,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvB,CAAC,CAAC;aACD,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE;YACzB,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;QACvB,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","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';\nimport { separateGlobs } from './separateGlobs.js';\n\n/**\n * Watch for changes using chokidar in the current process.\n */\nexport class DefaultWatcher extends BaseWatcher {\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 .on('add', (filePath) => {\n onChange([filePath]);\n })\n .on('unlink', (filePath) => {\n onChange([filePath]);\n });\n\n return async () => {\n await watcher?.close();\n watcher = undefined;\n };\n }\n}\n"]}
@@ -1,11 +1,11 @@
1
- import type { UnwatchCallback, WatchOptions } from './types/Watcher.js';
1
+ import type { Backend, UnwatchCallback, WatchOptions } from './types/Watcher.js';
2
2
  import { BaseWatcher } from './BaseWatcher.js';
3
3
  /**
4
4
  * Creates a watcher in a fork which can watch a package and notify the client when it changes.
5
5
  */
6
6
  export declare class ForkWatcher extends BaseWatcher {
7
7
  private _child;
8
- constructor();
8
+ constructor(backend?: Backend);
9
9
  protected _watch(options: Required<WatchOptions>): UnwatchCallback;
10
10
  protected _dispose(): Promise<void>;
11
11
  protected _isDisposed(): boolean;
@@ -1 +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"}
1
+ {"version":3,"file":"ForkWatcher.d.ts","sourceRoot":"","sources":["../src/ForkWatcher.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAK/C;;GAEG;AACH,qBAAa,WAAY,SAAQ,WAAW;IAC1C,OAAO,CAAC,MAAM,CAA2B;gBAE7B,OAAO,GAAE,OAAmB;IAuBxC,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;CAOrB"}
@@ -9,16 +9,16 @@ const watcherForkWrapper = path.resolve(currentDir, 'watcherForkWrapper.js');
9
9
  */
10
10
  export class ForkWatcher extends BaseWatcher {
11
11
  _child;
12
- constructor() {
12
+ constructor(backend = 'default') {
13
13
  super();
14
- this._child = fork(watcherForkWrapper);
14
+ this._child = fork(watcherForkWrapper, [backend]);
15
15
  this._child.on('message', (message) => {
16
16
  switch (message.type) {
17
17
  case 'change':
18
18
  if (this._isDisposed()) {
19
19
  throw new Error('Watcher has been disposed.');
20
20
  }
21
- this._onChangeCallbacks[message.id]?.(message.changedPath);
21
+ this._onChangeCallbacks[message.id]?.(message.changedPaths);
22
22
  break;
23
23
  case 'error':
24
24
  console.error(`An error occurred while watching files: ${message.error}`);
@@ -49,7 +49,11 @@ export class ForkWatcher extends BaseWatcher {
49
49
  }
50
50
  /** Typed wrapper for `this._child.send()`. No-op if `_child` is disposed. */
51
51
  _sendMessage(message) {
52
- this._child?.send(message);
52
+ this._child?.send(message, (error) => {
53
+ if (error) {
54
+ console.error(`Failed to send message to file watcher process. Error: ${error.message}`);
55
+ }
56
+ });
53
57
  }
54
58
  }
55
59
  //# sourceMappingURL=ForkWatcher.js.map
@@ -1 +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
+ {"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,YAAY,UAAmB,SAAS;QACtC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAElD,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,YAAY,CAAC,CAAC;oBAC5D,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,EAAE,CAAC,KAAK,EAAE,EAAE;YACnC,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,KAAK,CAAC,0DAA0D,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC3F,CAAC;QACH,CAAC,CAAC,CAAC;IACL,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 { Backend, 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(backend: Backend = 'default') {\n super();\n this._child = fork(watcherForkWrapper, [backend]);\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.changedPaths);\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, (error) => {\n if (error) {\n console.error(`Failed to send message to file watcher process. Error: ${error.message}`);\n }\n });\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 ParcelWatcher extends BaseWatcher {
7
+ protected _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): Promise<UnwatchCallback>;
8
+ }
9
+ //# sourceMappingURL=ParcelWatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParcelWatcher.d.ts","sourceRoot":"","sources":["../src/ParcelWatcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAI/C;;GAEG;AACH,qBAAa,aAAc,SAAQ,WAAW;cAK5B,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,eAAe,CAAC;CAsB7G"}
@@ -0,0 +1,31 @@
1
+ import parcel from '@parcel/watcher';
2
+ import { BaseWatcher } from './BaseWatcher.js';
3
+ import { sourceFilesGlobs } from '@ms-cloudpack/path-utilities';
4
+ import { separateGlobs } from './separateGlobs.js';
5
+ /**
6
+ * Watch for changes using chokidar in the current process.
7
+ */
8
+ export class ParcelWatcher 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 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
+ // Tracking: https://github.com/microsoft/cloudpack/issues/3046
13
+ async _watch(options, onChange) {
14
+ const { path: rootPath, watchPaths = sourceFilesGlobs } = options;
15
+ const { negativeGlobs } = separateGlobs(watchPaths);
16
+ let watcher = await parcel.subscribe(rootPath, (err, events) => {
17
+ if (err) {
18
+ console.error(`Error watching files: ${err}`);
19
+ }
20
+ else if (events.length) {
21
+ const uniquePaths = new Set(events.map((event) => event.path));
22
+ onChange([...uniquePaths]);
23
+ }
24
+ }, { ignore: negativeGlobs });
25
+ return async () => {
26
+ await watcher?.unsubscribe();
27
+ watcher = undefined;
28
+ };
29
+ }
30
+ }
31
+ //# sourceMappingURL=ParcelWatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ParcelWatcher.js","sourceRoot":"","sources":["../src/ParcelWatcher.ts"],"names":[],"mappings":"AAAA,OAAO,MAAkC,MAAM,iBAAiB,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;GAEG;AACH,MAAM,OAAO,aAAc,SAAQ,WAAW;IAC5C,gGAAgG;IAChG,uFAAuF;IACvF,8FAA8F;IAC9F,+DAA+D;IACrD,KAAK,CAAC,MAAM,CAAC,OAA2B,EAAE,QAA6B;QAC/E,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,GAAG,gBAAgB,EAAE,GAAG,OAAO,CAAC;QAClE,MAAM,EAAE,aAAa,EAAE,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QAEpD,IAAI,OAAO,GAAkC,MAAM,MAAM,CAAC,SAAS,CACjE,QAAQ,EACR,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE;YACd,IAAI,GAAG,EAAE,CAAC;gBACR,OAAO,CAAC,KAAK,CAAC,yBAAyB,GAAG,EAAE,CAAC,CAAC;YAChD,CAAC;iBAAM,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBACzB,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/D,QAAQ,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC,EACD,EAAE,MAAM,EAAE,aAAa,EAAE,CAC1B,CAAC;QAEF,OAAO,KAAK,IAAI,EAAE;YAChB,MAAM,OAAO,EAAE,WAAW,EAAE,CAAC;YAC7B,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC,CAAC;IACJ,CAAC;CACF","sourcesContent":["import parcel, { type AsyncSubscription } from '@parcel/watcher';\nimport type { UnwatchCallback, WatchChangeCallback, WatchOptionsWithId } from './types/Watcher.js';\nimport { BaseWatcher } from './BaseWatcher.js';\nimport { sourceFilesGlobs } from '@ms-cloudpack/path-utilities';\nimport { separateGlobs } from './separateGlobs.js';\n\n/**\n * Watch for changes using chokidar in the current process.\n */\nexport class ParcelWatcher 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 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 // Tracking: https://github.com/microsoft/cloudpack/issues/3046\n protected async _watch(options: WatchOptionsWithId, onChange: WatchChangeCallback): Promise<UnwatchCallback> {\n const { path: rootPath, watchPaths = sourceFilesGlobs } = options;\n const { negativeGlobs } = separateGlobs(watchPaths);\n\n let watcher: AsyncSubscription | undefined = await parcel.subscribe(\n rootPath,\n (err, events) => {\n if (err) {\n console.error(`Error watching files: ${err}`);\n } else if (events.length) {\n const uniquePaths = new Set(events.map((event) => event.path));\n onChange([...uniquePaths]);\n }\n },\n { ignore: negativeGlobs },\n );\n\n return async () => {\n await watcher?.unsubscribe();\n watcher = undefined;\n };\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { Watcher } from './types/Watcher.js';
1
+ import type { Backend, Watcher } from './types/Watcher.js';
2
2
  /**
3
3
  * Create a file watcher. By default, the watcher will run in the main thread.
4
4
  * Use `{ type: 'fork' }` to run the watcher in a forked process.
@@ -10,5 +10,11 @@ export declare function createWatcher(options?: {
10
10
  * @default 'default'
11
11
  */
12
12
  type?: 'default' | 'fork';
13
+ /**
14
+ * By default, the watcher will use chokidar.
15
+ * Specify `'parcel'` to use parcel as the file watching backend.
16
+ * @default 'default'
17
+ */
18
+ backend?: Backend;
13
19
  }): Watcher;
14
20
  //# sourceMappingURL=createWatcher.d.ts.map
@@ -1 +1 @@
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
+ {"version":3,"file":"createWatcher.d.ts","sourceRoot":"","sources":["../src/createWatcher.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE;IACtC;;;;OAIG;IACH,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,OAAO,CAYV"}
@@ -1,12 +1,17 @@
1
1
  import { DefaultWatcher } from './DefaultWatcher.js';
2
2
  import { ForkWatcher } from './ForkWatcher.js';
3
+ import { ParcelWatcher } from './ParcelWatcher.js';
3
4
  /**
4
5
  * Create a file watcher. By default, the watcher will run in the main thread.
5
6
  * Use `{ type: 'fork' }` to run the watcher in a forked process.
6
7
  */
7
8
  export function createWatcher(options) {
8
- if (options?.type === 'fork') {
9
- return new ForkWatcher();
9
+ const { type = 'default', backend = 'default' } = options ?? {};
10
+ if (type === 'fork') {
11
+ return new ForkWatcher(backend);
12
+ }
13
+ if (backend === 'parcel') {
14
+ return new ParcelWatcher();
10
15
  }
11
16
  return new DefaultWatcher();
12
17
  }
@@ -1 +1 @@
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"]}
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;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAa7B;IACC,MAAM,EAAE,IAAI,GAAG,SAAS,EAAE,OAAO,GAAG,SAAS,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;IAEhE,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAED,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,IAAI,aAAa,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,IAAI,cAAc,EAAE,CAAC;AAC9B,CAAC","sourcesContent":["import { DefaultWatcher } from './DefaultWatcher.js';\nimport { ForkWatcher } from './ForkWatcher.js';\nimport { ParcelWatcher } from './ParcelWatcher.js';\nimport type { Backend, 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 /**\n * By default, the watcher will use chokidar.\n * Specify `'parcel'` to use parcel as the file watching backend.\n * @default 'default'\n */\n backend?: Backend;\n}): Watcher {\n const { type = 'default', backend = 'default' } = options ?? {};\n\n if (type === 'fork') {\n return new ForkWatcher(backend);\n }\n\n if (backend === 'parcel') {\n return new ParcelWatcher();\n }\n\n return new DefaultWatcher();\n}\n"]}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * This function separates positive and negative globs to later on ignore the negative ones.
3
+ * This is necessary because it is more reliable to pass negative globs to ignore.
4
+ */
5
+ export declare function separateGlobs(globs: string[]): {
6
+ positiveGlobs: string[];
7
+ negativeGlobs: string[];
8
+ };
9
+ //# sourceMappingURL=separateGlobs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"separateGlobs.d.ts","sourceRoot":"","sources":["../src/separateGlobs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG;IAAE,aAAa,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE,CAanG"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * This function separates positive and negative globs to later on ignore the negative ones.
3
+ * This is necessary because it is more reliable to pass negative globs to ignore.
4
+ */
5
+ export function separateGlobs(globs) {
6
+ const positiveGlobs = [];
7
+ const negativeGlobs = [];
8
+ for (const glob of globs) {
9
+ if (glob.startsWith('!')) {
10
+ negativeGlobs.push(glob.slice(1));
11
+ }
12
+ else {
13
+ positiveGlobs.push(glob);
14
+ }
15
+ }
16
+ return { positiveGlobs, negativeGlobs };
17
+ }
18
+ //# sourceMappingURL=separateGlobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"separateGlobs.js","sourceRoot":"","sources":["../src/separateGlobs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAe;IAC3C,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":["/**\n * This function separates positive and negative globs to later on ignore the negative ones.\n * This is necessary because it is more reliable to pass negative globs to ignore.\n */\nexport function 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"]}
@@ -13,7 +13,7 @@ type ShutdownMessage = {
13
13
  export type ParentMessage = WatchMessage | UnwatchMessage | ShutdownMessage;
14
14
  type ChangeMessage = Pick<WatchOptionsWithId, 'id'> & {
15
15
  type: 'change';
16
- changedPath: string;
16
+ changedPaths: string[];
17
17
  };
18
18
  type ErrorMessage = {
19
19
  type: 'error';
@@ -1 +1 @@
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
+ {"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,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,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, 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
+ {"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 changedPaths: 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"]}
@@ -10,16 +10,21 @@ export interface WatchOptions {
10
10
  id?: string;
11
11
  /**
12
12
  * Path globs to watch, relative to `path`.
13
- * Defaults are under "@ms-cloudpack/path-utilities" in `sourceFilesGlobs`.
13
+ * Defaults are under `@ms-cloudpack/path-utilities` in `sourceFilesGlobs`.
14
+ *
15
+ * The parcel watcher only supports negative globs (starting with `!`) and will watch all files
16
+ * except those matched by the glob.
17
+ *
18
+ * For the default watcher, both positive and negative globs are supported.
14
19
  */
15
20
  watchPaths?: string[];
16
21
  }
17
22
  export type WatchOptionsWithId = WatchOptions & Required<Pick<WatchOptions, 'id'>>;
18
23
  /**
19
24
  * Callback when a watched path changes.
20
- * @param changedPath Absolute path to the file that changed.
25
+ * @param changedPaths Absolute paths to the files that changed.
21
26
  */
22
- export type WatchChangeCallback = (changedPath: string) => void;
27
+ export type WatchChangeCallback = (changedPaths: string[]) => void;
23
28
  /** Callback to unwatch a path */
24
29
  export type UnwatchCallback = () => void | Promise<void>;
25
30
  export interface Watcher {
@@ -27,10 +32,11 @@ export interface Watcher {
27
32
  * Watch a path for changes.
28
33
  * @returns Callback to unwatch the path.
29
34
  */
30
- watch(options: WatchOptions, onChange: WatchChangeCallback): UnwatchCallback;
35
+ watch(options: WatchOptions, onChange: WatchChangeCallback): Promise<UnwatchCallback>;
31
36
  /** Remove watchers for a specific ID (path) */
32
37
  unwatch(id: string): Promise<void>;
33
38
  /** Dispose all watchers. The Watcher object can't be reused afer this. */
34
39
  dispose(): Promise<void>;
35
40
  }
41
+ export type Backend = 'default' | 'parcel';
36
42
  //# 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;;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
+ {"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;;;;;;;;OAQG;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,YAAY,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;AAEnE,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,OAAO,CAAC,eAAe,CAAC,CAAC;IAEtF,+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;AAED,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC"}
@@ -1 +1 @@
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
+ {"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 * The parcel watcher only supports negative globs (starting with `!`) and will watch all files\n * except those matched by the glob.\n *\n * For the default watcher, both positive and negative globs are supported.\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 changedPaths Absolute paths to the files that changed.\n */\nexport type WatchChangeCallback = (changedPaths: 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): Promise<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\nexport type Backend = 'default' | 'parcel';\n"]}
@@ -1,18 +1,19 @@
1
1
  import { DefaultWatcher } from './DefaultWatcher.js';
2
+ import { ParcelWatcher } from './ParcelWatcher.js';
2
3
  /**
3
4
  * `ForkWatcher` runs this file to initialize the watcher in a child process.
4
5
  */
5
6
  function initializeWatcher() {
6
- const watcher = new DefaultWatcher();
7
+ // Get backend from command line arguments
8
+ const backend = process.argv[2];
9
+ const watcher = backend === 'parcel' ? new ParcelWatcher() : new DefaultWatcher();
7
10
  process.on('message', (message) => {
8
11
  (async () => {
9
12
  switch (message.type) {
10
13
  case 'watch':
11
- {
12
- watcher.watch(message.options, (changedPath) => {
13
- sendMessage({ type: 'change', id: message.options.id, changedPath });
14
- });
15
- }
14
+ await watcher.watch(message.options, (changedPaths) => {
15
+ sendMessage({ type: 'change', id: message.options.id, changedPaths });
16
+ });
16
17
  break;
17
18
  case 'unwatch':
18
19
  await watcher.unwatch(message.id);
@@ -23,7 +24,13 @@ function initializeWatcher() {
23
24
  process.exit(0);
24
25
  }
25
26
  })().catch((error) => {
26
- sendMessage({ type: 'error', error: error });
27
+ try {
28
+ sendMessage({ type: 'error', error: error });
29
+ }
30
+ catch (sendError) {
31
+ console.error('Failed to send error message:', sendError);
32
+ console.error('Original error:', error);
33
+ }
27
34
  });
28
35
  });
29
36
  }
@@ -1 +1 @@
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"]}
1
+ {"version":3,"file":"watcherForkWrapper.js","sourceRoot":"","sources":["../src/watcherForkWrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAInD;;GAEG;AACH,SAAS,iBAAiB;IACxB,0CAA0C;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAwB,CAAC;IAEvD,MAAM,OAAO,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,cAAc,EAAE,CAAC;IAElF,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,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC,YAAY,EAAE,EAAE;wBACpD,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC;oBACxE,CAAC,CAAC,CAAC;oBACH,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,IAAI,CAAC;gBACH,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,CAAC,CAAC;YACxD,CAAC;YAAC,OAAO,SAAS,EAAE,CAAC;gBACnB,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,SAAS,CAAC,CAAC;gBAC1D,OAAO,CAAC,KAAK,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC;YAC1C,CAAC;QACH,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 { ParcelWatcher } from './ParcelWatcher.js';\nimport type { Backend } from './types/Watcher.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 // Get backend from command line arguments\n const backend = process.argv[2] as Backend | undefined;\n\n const watcher = backend === 'parcel' ? new ParcelWatcher() : new DefaultWatcher();\n\n process.on('message', (message: ParentMessage) => {\n (async () => {\n switch (message.type) {\n case 'watch':\n await watcher.watch(message.options, (changedPaths) => {\n sendMessage({ type: 'change', id: message.options.id, changedPaths });\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 try {\n sendMessage({ type: 'error', error: error as Error });\n } catch (sendError) {\n console.error('Failed to send error message:', sendError);\n console.error('Original error:', error);\n }\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.3.12",
3
+ "version": "0.4.0",
4
4
  "description": "A file watcher abstraction for use with Cloudpack.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,7 +15,8 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@ms-cloudpack/path-utilities": "^3.1.12",
18
+ "@ms-cloudpack/path-utilities": "^3.1.14",
19
+ "@parcel/watcher": "^2.5.1",
19
20
  "chokidar": "^3.5.3"
20
21
  },
21
22
  "devDependencies": {