@ms-cloudpack/file-watcher 0.2.0 → 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
@@ -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 to `*.json` and `src/**/*`, excluding `node_modules`.
37
+ - `watchedPaths` (`string[]`, optional): Relative paths/globs from the root path to watch. Defaults are under "@ms-cloudpack/path-utilities" in `sourceFilesGlobs`.
@@ -2,14 +2,12 @@
2
2
  * Base class for watcher implementations.
3
3
  */
4
4
  export class BaseWatcher {
5
- constructor() {
6
- /** Watcher is disposed or disposing */
7
- this._disposeCalled = false;
8
- /** Mapping from ID to change callback */
9
- this._onChangeCallbacks = {};
10
- /** Mapping from ID to unwatch callback */
11
- this._unwatchCallbacks = {};
12
- }
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 = {};
13
11
  /** Number of IDs currently being watched (just used for testing) */
14
12
  get activeWatchCount() {
15
13
  return Object.keys(this._unwatchCallbacks).length;
@@ -1 +1 @@
1
- {"version":3,"file":"BaseWatcher.js","sourceRoot":"","sources":["../src/BaseWatcher.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,MAAM,OAAgB,WAAW;IAAjC;QACE,uCAAuC;QAC/B,mBAAc,GAAG,KAAK,CAAC;QAC/B,yCAAyC;QAC/B,uBAAkB,GAAwC,EAAE,CAAC;QACvE,0CAA0C;QAClC,sBAAiB,GAAoC,EAAE,CAAC;IA2DlE,CAAC;IAzDC,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,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 +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;AAI/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;AAG/C;;GAEG;AACH,qBAAa,cAAe,SAAQ,WAAW;IAI7C,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,kBAAkB,EAAE,QAAQ,EAAE,mBAAmB,GAAG,eAAe;CAkB9F"}
@@ -1,7 +1,7 @@
1
1
  import chokidar from 'chokidar';
2
2
  import path from 'path';
3
3
  import { BaseWatcher } from './BaseWatcher.js';
4
- const defaultWatchPaths = ['src/**/*', '*.json', '!**/node_modules/**/*'];
4
+ import { sourceFilesGlobs } from '@ms-cloudpack/path-utilities';
5
5
  /**
6
6
  * Watch for changes using chokidar in the current process.
7
7
  */
@@ -10,7 +10,7 @@ export class DefaultWatcher extends BaseWatcher {
10
10
  // same watch paths, a single chokidar watcher should be created and shared. But this involves a
11
11
  // lot of extra tracking logic and isn't essential until SSR is actually being used in an app.
12
12
  _watch(options, onChange) {
13
- const { path: rootPath, watchPaths = defaultWatchPaths } = options;
13
+ const { path: rootPath, watchPaths = sourceFilesGlobs } = options;
14
14
  const { positiveGlobs, negativeGlobs } = separateGlobs(watchPaths);
15
15
  const paths = positiveGlobs.map((relPath) => path.resolve(rootPath, relPath));
16
16
  const ignored = negativeGlobs.map((relPath) => path.resolve(rootPath, relPath));
@@ -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;AAE/C,MAAM,iBAAiB,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC;AAE1E;;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,iBAAiB,EAAE,GAAG,OAAO,CAAC;QACnE,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';\n\nconst defaultWatchPaths = ['src/**/*', '*.json', '!**/node_modules/**/*'];\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 = defaultWatchPaths } = 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;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"]}
@@ -8,6 +8,7 @@ const watcherForkWrapper = path.resolve(currentDir, 'watcherForkWrapper.js');
8
8
  * Creates a watcher in a fork which can watch a package and notify the client when it changes.
9
9
  */
10
10
  export class ForkWatcher extends BaseWatcher {
11
+ _child;
11
12
  constructor() {
12
13
  super();
13
14
  this._child = fork(watcherForkWrapper);
@@ -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;IAG1C;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;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"]}
@@ -10,7 +10,7 @@ export interface WatchOptions {
10
10
  id?: string;
11
11
  /**
12
12
  * Path globs to watch, relative to `path`.
13
- * Defaults to `['src/**', '*.json', '!**\/node_modules/**']`
13
+ * Defaults are under "@ms-cloudpack/path-utilities" in `sourceFilesGlobs`.
14
14
  */
15
15
  watchPaths?: string[];
16
16
  }
@@ -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 to `['src/**', '*.json', '!**\\/node_modules/**']`\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 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ms-cloudpack/file-watcher",
3
- "version": "0.2.0",
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,6 +15,7 @@
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": {