@opensumi/ide-file-service 3.7.1-next-1736826301.0 → 3.7.1-next-1736846464.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.
Files changed (44) hide show
  1. package/lib/browser/file-service-client.js +4 -4
  2. package/lib/browser/file-service-client.js.map +1 -1
  3. package/lib/browser/file-service-provider-client.d.ts +2 -2
  4. package/lib/browser/file-service-provider-client.d.ts.map +1 -1
  5. package/lib/browser/file-service-provider-client.js +2 -2
  6. package/lib/browser/file-service-provider-client.js.map +1 -1
  7. package/lib/common/files.d.ts +2 -2
  8. package/lib/common/files.d.ts.map +1 -1
  9. package/lib/common/watcher.d.ts +3 -2
  10. package/lib/common/watcher.d.ts.map +1 -1
  11. package/lib/common/watcher.js.map +1 -1
  12. package/lib/node/disk-file-system.provider.d.ts +3 -2
  13. package/lib/node/disk-file-system.provider.d.ts.map +1 -1
  14. package/lib/node/disk-file-system.provider.js +3 -2
  15. package/lib/node/disk-file-system.provider.js.map +1 -1
  16. package/lib/node/hosted/recursive/file-service-watcher.d.ts +13 -13
  17. package/lib/node/hosted/recursive/file-service-watcher.d.ts.map +1 -1
  18. package/lib/node/hosted/recursive/file-service-watcher.js +146 -95
  19. package/lib/node/hosted/recursive/file-service-watcher.js.map +1 -1
  20. package/lib/node/hosted/un-recursive/file-service-watcher.d.ts +5 -11
  21. package/lib/node/hosted/un-recursive/file-service-watcher.d.ts.map +1 -1
  22. package/lib/node/hosted/un-recursive/file-service-watcher.js +28 -40
  23. package/lib/node/hosted/un-recursive/file-service-watcher.js.map +1 -1
  24. package/lib/node/hosted/watcher.host.service.d.ts +7 -2
  25. package/lib/node/hosted/watcher.host.service.d.ts.map +1 -1
  26. package/lib/node/hosted/watcher.host.service.js +55 -28
  27. package/lib/node/hosted/watcher.host.service.js.map +1 -1
  28. package/lib/node/hosted/watcher.process.js +1 -1
  29. package/lib/node/hosted/watcher.process.js.map +1 -1
  30. package/lib/node/watcher-process-manager.d.ts +3 -2
  31. package/lib/node/watcher-process-manager.d.ts.map +1 -1
  32. package/lib/node/watcher-process-manager.js +4 -3
  33. package/lib/node/watcher-process-manager.js.map +1 -1
  34. package/package.json +9 -9
  35. package/src/browser/file-service-client.ts +4 -4
  36. package/src/browser/file-service-provider-client.ts +3 -2
  37. package/src/common/files.ts +2 -2
  38. package/src/common/watcher.ts +10 -3
  39. package/src/node/disk-file-system.provider.ts +8 -3
  40. package/src/node/hosted/recursive/file-service-watcher.ts +179 -106
  41. package/src/node/hosted/un-recursive/file-service-watcher.ts +36 -55
  42. package/src/node/hosted/watcher.host.service.ts +95 -33
  43. package/src/node/hosted/watcher.process.ts +1 -1
  44. package/src/node/watcher-process-manager.ts +13 -5
@@ -2,24 +2,15 @@ import fs, { watch } from 'fs-extra';
2
2
  import debounce from 'lodash/debounce';
3
3
 
4
4
  import { ILogService } from '@opensumi/ide-core-common/lib/log';
5
- import { Disposable, DisposableCollection, FileUri, IDisposable, isMacintosh, path } from '@opensumi/ide-utils/lib';
5
+ import { Disposable, DisposableCollection, FileUri, isMacintosh, path } from '@opensumi/ide-utils/lib';
6
6
 
7
- import { FileChangeType, FileSystemWatcherClient, IFileSystemWatcherServer } from '../../../common/index';
7
+ import { FileChangeType, FileSystemWatcherClient, IWatcher } from '../../../common/index';
8
8
  import { FileChangeCollection } from '../../file-change-collection';
9
9
  import { shouldIgnorePath } from '../shared';
10
10
  const { join, basename, normalize } = path;
11
11
 
12
- export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
13
- private WATCHER_HANDLERS = new Map<
14
- number,
15
- {
16
- path: string;
17
- handlers: any;
18
- disposable: IDisposable;
19
- }
20
- >();
21
-
22
- private static WATCHER_SEQUENCE = 1;
12
+ export class UnRecursiveFileSystemWatcher implements IWatcher {
13
+ private watcherCollections: Map<string, fs.FSWatcher> = new Map();
23
14
 
24
15
  private static readonly FILE_DELETE_HANDLER_DELAY = 500;
25
16
 
@@ -34,34 +25,25 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
34
25
 
35
26
  dispose(): void {
36
27
  this.toDispose.dispose();
37
- this.WATCHER_HANDLERS.clear();
38
- }
39
-
40
- /**
41
- * 查找某个路径是否已被监听
42
- * @param watcherPath
43
- */
44
- checkIsAlreadyWatched(watcherPath: string): number | undefined {
45
- for (const [watcherId, watcher] of this.WATCHER_HANDLERS) {
46
- if (watcherPath.indexOf(watcher.path) === 0) {
47
- return watcherId;
48
- }
49
- }
50
28
  }
51
29
 
52
30
  private async doWatch(basePath: string) {
53
31
  try {
54
32
  const watcher = watch(basePath);
55
- this.logger.log('start watching', basePath);
56
- const isDirectory = fs.lstatSync(basePath).isDirectory();
33
+ this.watcherCollections.set(basePath, watcher);
34
+
35
+ this.logger.log('[Un-Recursive] start watching', basePath);
36
+ const isDirectory = (await fs.lstat(basePath)).isDirectory();
57
37
 
58
38
  const docChildren = new Set<string>();
59
39
  let signalDoc = '';
60
40
  if (isDirectory) {
61
41
  try {
62
- for (const child of fs.readdirSync(basePath)) {
42
+ const children = await fs.readdir(basePath);
43
+ for (const child of children) {
63
44
  const base = join(basePath, String(child));
64
- if (!fs.lstatSync(base).isDirectory()) {
45
+ const childStat = await fs.lstat(base);
46
+ if (!childStat.isDirectory()) {
65
47
  docChildren.add(child);
66
48
  }
67
49
  }
@@ -74,11 +56,13 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
74
56
 
75
57
  // 开始走监听流程
76
58
  watcher.on('error', (code: number, signal: string) => {
77
- this.logger.error(`Failed to watch ${basePath} for changes using fs.watch() (${code}, ${signal})`);
59
+ this.logger.error(
60
+ `[Un-Recursive] Failed to watch ${basePath} for changes using fs.watch() (${code}, ${signal})`,
61
+ );
78
62
  watcher.close();
79
63
  });
80
64
 
81
- watcher.on('change', (type: string, filename: string | Buffer) => {
65
+ watcher.on('change', async (type: string, filename: string | Buffer) => {
82
66
  if (shouldIgnorePath(filename as string)) {
83
67
  return;
84
68
  }
@@ -96,21 +80,22 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
96
80
  }
97
81
 
98
82
  const changePath = join(basePath, changeFileName);
83
+ const pathExist = await fs.pathExists(changePath);
99
84
  if (isDirectory) {
100
85
  setTimeout(async () => {
101
86
  // 监听的目录如果是文件夹,那么只对其下面的文件改动做出响应
102
87
  if (docChildren.has(changeFileName)) {
103
88
  if ((type === 'rename' || type === 'change') && changeFileName === filename) {
104
- const fileExists = fs.existsSync(changePath);
105
- if (fileExists) {
89
+ if (pathExist) {
106
90
  this.pushUpdated(changePath);
107
91
  } else {
108
92
  docChildren.delete(changeFileName);
109
93
  this.pushDeleted(changePath);
110
94
  }
111
95
  }
112
- } else if (fs.pathExistsSync(changePath)) {
113
- if (!fs.lstatSync(changePath).isDirectory()) {
96
+ } else if (pathExist) {
97
+ const fileStat = await fs.lstat(changePath);
98
+ if (!fileStat.isDirectory()) {
114
99
  this.pushAdded(changePath);
115
100
  docChildren.add(changeFileName);
116
101
  }
@@ -119,7 +104,7 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
119
104
  } else {
120
105
  setTimeout(async () => {
121
106
  if (changeFileName === signalDoc) {
122
- if (fs.pathExistsSync(basePath)) {
107
+ if (pathExist) {
123
108
  this.pushUpdated(basePath);
124
109
  } else {
125
110
  this.pushDeleted(basePath);
@@ -130,7 +115,7 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
130
115
  }
131
116
  });
132
117
  } catch (error) {
133
- this.logger.error(`Failed to watch ${basePath} for change using fs.watch() (${error.toString()})`);
118
+ this.logger.error(`[Un-Recursive] Failed to watch ${basePath} for change using fs.watch() (${error.toString()})`);
134
119
  }
135
120
  }
136
121
 
@@ -138,29 +123,20 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
138
123
  const basePath = FileUri.fsPath(uri);
139
124
  const exist = await fs.pathExists(basePath);
140
125
 
141
- let watcherId = this.checkIsAlreadyWatched(basePath);
142
-
143
- if (watcherId) {
144
- return watcherId;
145
- }
146
-
147
- watcherId = UnRecursiveFileSystemWatcher.WATCHER_SEQUENCE++;
148
-
149
126
  const disposables = new DisposableCollection(); // 管理可释放的资源
150
127
 
151
128
  let watchPath = '';
152
129
 
153
130
  if (exist) {
154
- const stat = await fs.lstatSync(basePath);
131
+ const stat = await fs.lstat(basePath);
155
132
  if (stat) {
156
133
  watchPath = basePath;
157
134
  }
158
135
  } else {
159
- this.logger.warn('This path does not exist. Please try again');
136
+ this.logger.warn('[Un-Recursive] This path does not exist. Please try again');
160
137
  }
161
138
  disposables.push(await this.start(watchPath));
162
139
  this.toDispose.push(disposables);
163
- return watcherId;
164
140
  }
165
141
 
166
142
  protected async start(basePath: string): Promise<DisposableCollection> {
@@ -170,6 +146,10 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
170
146
  }
171
147
 
172
148
  const realPath = await fs.realpath(basePath);
149
+ if (this.watcherCollections.has(realPath)) {
150
+ return disposables;
151
+ }
152
+
173
153
  const tryWatchDir = async (retryDelay = 1000) => {
174
154
  try {
175
155
  this.doWatch(realPath);
@@ -183,13 +163,14 @@ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
183
163
  await tryWatchDir();
184
164
  return disposables;
185
165
  }
186
- unwatchFileChanges(watcherId: number): Promise<void> {
187
- const watcher = this.WATCHER_HANDLERS.get(watcherId);
188
- if (watcher) {
189
- this.WATCHER_HANDLERS.delete(watcherId);
190
- watcher.disposable.dispose();
166
+
167
+ async unwatchFileChanges(uri: string): Promise<void> {
168
+ const basePath = FileUri.fsPath(uri);
169
+ if (this.watcherCollections.has(basePath)) {
170
+ const watcher = this.watcherCollections.get(basePath);
171
+ watcher?.close();
172
+ this.watcherCollections.delete(basePath);
191
173
  }
192
- return Promise.resolve();
193
174
  }
194
175
 
195
176
  protected pushAdded(path: string): void {
@@ -1,20 +1,45 @@
1
1
  import { SumiConnectionMultiplexer } from '@opensumi/ide-connection';
2
- import { DidFilesChangedParams } from '@opensumi/ide-core-common';
2
+ import {
3
+ DidFilesChangedParams,
4
+ Disposable,
5
+ DisposableCollection,
6
+ FileUri,
7
+ IDisposable,
8
+ RecursiveWatcherBackend,
9
+ } from '@opensumi/ide-core-common';
3
10
  import { defaultFilesWatcherExcludes, flattenExcludes } from '@opensumi/ide-core-common/lib/preferences/file-watch';
4
11
  import { URI, Uri, UriComponents } from '@opensumi/ide-utils/lib/uri';
5
12
 
6
13
  import { IWatcherHostService, WatcherProcessManagerProxy, WatcherServiceProxy } from '../../common/watcher';
7
14
  import { IWatcher } from '../disk-file-system.provider';
8
15
 
9
- import { FileSystemWatcherServer } from './recursive/file-service-watcher';
16
+ import { RecursiveFileSystemWatcher } from './recursive/file-service-watcher';
10
17
  import { UnRecursiveFileSystemWatcher } from './un-recursive/file-service-watcher';
11
18
  import { WatcherProcessLogger } from './watch-process-log';
12
19
 
20
+ const watcherPlaceHolder = {
21
+ disposable: {
22
+ dispose: () => { },
23
+ },
24
+ handlers: [],
25
+ };
26
+
13
27
  export class WatcherHostServiceImpl implements IWatcherHostService {
28
+ private static WATCHER_SEQUENCE = 1;
29
+
30
+ private WATCHER_HANDLERS = new Map<
31
+ number,
32
+ {
33
+ path: string;
34
+ handlers: any;
35
+ disposable: IDisposable;
36
+ }
37
+ >();
38
+
14
39
  /**
15
40
  * recursive file system watcher
16
41
  */
17
- private recursiveFileSystemWatcher?: FileSystemWatcherServer;
42
+ private recursiveFileSystemWatcher?: RecursiveFileSystemWatcher;
18
43
 
19
44
  /**
20
45
  * unrecursive file system watcher
@@ -27,7 +52,11 @@ export class WatcherHostServiceImpl implements IWatcherHostService {
27
52
 
28
53
  private watchedDirs: Set<string> = new Set();
29
54
 
30
- constructor(private rpcProtocol: SumiConnectionMultiplexer, private logger: WatcherProcessLogger) {
55
+ constructor(
56
+ private rpcProtocol: SumiConnectionMultiplexer,
57
+ private logger: WatcherProcessLogger,
58
+ private backend: RecursiveWatcherBackend,
59
+ ) {
31
60
  this.rpcProtocol.set(WatcherServiceProxy, this);
32
61
  this.defaultExcludes = flattenExcludes(defaultFilesWatcherExcludes);
33
62
  this.initWatcherServer(this.defaultExcludes);
@@ -48,18 +77,18 @@ export class WatcherHostServiceImpl implements IWatcherHostService {
48
77
 
49
78
  // rewatch
50
79
  for (const [_uri, { options, disposable }] of this.watcherCollection) {
51
- this.doWatch(Uri.parse(_uri), options);
52
80
  this.logger.log('rewatch file changes: ', _uri, ' recursive: ', options?.recursive);
53
81
  disposable.dispose();
82
+ this.doWatch(Uri.parse(_uri), options);
54
83
  }
55
84
  }
56
85
 
57
- this.recursiveFileSystemWatcher = new FileSystemWatcherServer(excludes, this.logger);
86
+ this.recursiveFileSystemWatcher = new RecursiveFileSystemWatcher(excludes, this.logger, this.backend);
58
87
  this.unrecursiveFileSystemWatcher = new UnRecursiveFileSystemWatcher(this.logger);
59
88
 
60
89
  const watcherClient = {
61
90
  onDidFilesChanged: (events: DidFilesChangedParams) => {
62
- this.logger.log('watcher server onDidFilesChanged: ', events);
91
+ this.logger.log('onDidFilesChanged: ', events);
63
92
  const proxy = this.rpcProtocol.getProxy(WatcherProcessManagerProxy);
64
93
  proxy.$onDidFilesChanged(events);
65
94
  },
@@ -69,47 +98,80 @@ export class WatcherHostServiceImpl implements IWatcherHostService {
69
98
  this.unrecursiveFileSystemWatcher.setClient(watcherClient);
70
99
  }
71
100
 
72
- private getWatcherServer(recursive?: boolean) {
73
- const useRecursiveServer = recursive;
74
- let watcherServer: FileSystemWatcherServer | UnRecursiveFileSystemWatcher;
101
+ checkIsAlreadyWatched(watcherPath: string): number | undefined {
102
+ for (const [watcherId, watcher] of this.WATCHER_HANDLERS) {
103
+ if (watcherPath === watcher.path) {
104
+ return watcherId;
105
+ }
106
+ }
107
+ }
108
+
109
+ private async doWatch(
110
+ uri: Uri,
111
+ options?: { excludes?: string[]; recursive?: boolean; pollingWatch?: boolean },
112
+ ): Promise<number> {
75
113
  this.initWatcherServer();
114
+ const basePath = FileUri.fsPath(uri.toString());
115
+ let watcherId = this.checkIsAlreadyWatched(basePath);
76
116
 
77
- if (useRecursiveServer) {
78
- watcherServer = this.recursiveFileSystemWatcher!;
79
- } else {
80
- watcherServer = this.unrecursiveFileSystemWatcher!;
117
+ if (watcherId) {
118
+ this.logger.log(uri.toString(), 'is already watched');
119
+ return watcherId;
81
120
  }
82
121
 
83
- return watcherServer;
84
- }
122
+ watcherId = WatcherHostServiceImpl.WATCHER_SEQUENCE++;
85
123
 
86
- private async doWatch(uri: Uri, options?: { excludes?: string[]; recursive?: boolean }): Promise<number> {
87
- const watcherServer = this.getWatcherServer(options?.recursive);
88
- if (!watcherServer) {
89
- return -1;
90
- }
124
+ this.WATCHER_HANDLERS.set(watcherId, {
125
+ ...watcherPlaceHolder,
126
+ path: basePath,
127
+ });
91
128
 
92
129
  this.logger.log('watch file changes: ', uri.toString(), ' recursive: ', options?.recursive);
93
130
 
94
131
  const mergedExcludes = new Set([...(options?.excludes ?? []), ...this.defaultExcludes]);
95
- const id = await watcherServer.watchFileChanges(uri.toString(), {
96
- excludes: Array.from(mergedExcludes),
97
- });
98
132
 
99
- this.watchedDirs.add(uri.toString());
133
+ const disposables = new DisposableCollection();
100
134
 
101
- const disposable = {
102
- dispose: () => {
103
- watcherServer.unwatchFileChanges(id);
135
+ await this.unrecursiveFileSystemWatcher!.watchFileChanges(uri.toString());
136
+
137
+ disposables.push(
138
+ Disposable.create(() => {
139
+ this.unrecursiveFileSystemWatcher!.unwatchFileChanges(uri.toString());
104
140
  this.watchedDirs.delete(uri.toString());
105
- },
106
- };
141
+ }),
142
+ );
143
+
144
+ if (options?.recursive) {
145
+ this.logger.log('use recursive watcher for: ', uri.toString());
146
+ try {
147
+ await this.recursiveFileSystemWatcher!.watchFileChanges(uri.toString(), {
148
+ excludes: Array.from(mergedExcludes),
149
+ pollingWatch: options?.pollingWatch,
150
+ });
151
+
152
+ disposables.push(
153
+ Disposable.create(() => {
154
+ this.recursiveFileSystemWatcher!.unwatchFileChanges(uri.toString());
155
+ this.watchedDirs.delete(uri.toString());
156
+ }),
157
+ );
158
+ } catch (error) {
159
+ // watch error or timeout
160
+ this.logger.error('watch error: ', error);
161
+ }
162
+ }
163
+
164
+ this.watcherCollection.set(uri.toString(), { id: watcherId, options, disposable: disposables });
165
+
166
+ this.watchedDirs.add(uri.toString());
107
167
 
108
- this.watcherCollection.set(uri.toString(), { id, options, disposable });
109
- return id;
168
+ return watcherId;
110
169
  }
111
170
 
112
- async $watch(uri: UriComponents, options?: { excludes?: string[]; recursive?: boolean }): Promise<number> {
171
+ async $watch(
172
+ uri: UriComponents,
173
+ options?: { excludes?: string[]; recursive?: boolean; pollingWatch?: boolean },
174
+ ): Promise<number> {
113
175
  const _uri = URI.revive(uri);
114
176
  return this.doWatch(_uri, options);
115
177
  }
@@ -37,7 +37,7 @@ async function initWatcherProcess() {
37
37
  });
38
38
 
39
39
  const logger = new WatcherProcessLogger(watcherInjector, initData.logDir, initData.logLevel);
40
- const watcherHostService = new WatcherHostServiceImpl(watcherProtocol, logger);
40
+ const watcherHostService = new WatcherHostServiceImpl(watcherProtocol, logger, initData.backend);
41
41
  watcherHostService.initWatcherServer();
42
42
  }
43
43
 
@@ -7,7 +7,11 @@ import { IRPCProtocol } from '@opensumi/ide-connection';
7
7
  import { NetSocketConnection } from '@opensumi/ide-connection/lib/common/connection/drivers/socket';
8
8
  import { SumiConnectionMultiplexer } from '@opensumi/ide-connection/lib/common/rpc/multiplexer';
9
9
  import { ILogServiceManager, SupportLogNamespace } from '@opensumi/ide-core-common/lib/log';
10
- import { DidFilesChangedParams, FileSystemWatcherClient } from '@opensumi/ide-core-common/lib/types/file-watch';
10
+ import {
11
+ DidFilesChangedParams,
12
+ FileSystemWatcherClient,
13
+ RecursiveWatcherBackend,
14
+ } from '@opensumi/ide-core-common/lib/types/file-watch';
11
15
  import { normalizedIpcHandlerPathAsync } from '@opensumi/ide-core-common/lib/utils/ipc';
12
16
  import { AppConfig, Deferred, ILogService, UriComponents } from '@opensumi/ide-core-node';
13
17
  import { process as processUtil } from '@opensumi/ide-utils';
@@ -110,7 +114,7 @@ export class WatcherProcessManagerImpl implements IWatcherProcessManager {
110
114
  );
111
115
  }
112
116
 
113
- private async createWatcherProcess(clientId: string, ipcHandlerPath: string) {
117
+ private async createWatcherProcess(clientId: string, ipcHandlerPath: string, backend?: RecursiveWatcherBackend) {
114
118
  const forkArgs = [
115
119
  `--${SUMI_WATCHER_PROCESS_SOCK_KEY}=${JSON.stringify({
116
120
  path: ipcHandlerPath,
@@ -118,6 +122,7 @@ export class WatcherProcessManagerImpl implements IWatcherProcessManager {
118
122
  `--${WATCHER_INIT_DATA_KEY}=${JSON.stringify({
119
123
  logDir: this.appConfig.logDir,
120
124
  logLevel: this.appConfig.logLevel,
125
+ backend,
121
126
  clientId,
122
127
  })}`,
123
128
  ];
@@ -136,14 +141,14 @@ export class WatcherProcessManagerImpl implements IWatcherProcessManager {
136
141
  return this.watcherProcess.pid;
137
142
  }
138
143
 
139
- async createProcess(clientId: string) {
144
+ async createProcess(clientId: string, backend?: RecursiveWatcherBackend) {
140
145
  this.logger.log('create watcher process for client: ', clientId);
141
146
  this.logger.log('appconfig watcherHost: ', this.watcherHost);
142
147
 
143
148
  const ipcHandlerPath = await this.getIPCHandlerPath('watcher_process');
144
149
  await this.createWatcherServer(clientId, ipcHandlerPath);
145
150
 
146
- const pid = await this.createWatcherProcess(clientId, ipcHandlerPath);
151
+ const pid = await this.createWatcherProcess(clientId, ipcHandlerPath, backend);
147
152
 
148
153
  return pid;
149
154
  }
@@ -158,7 +163,10 @@ export class WatcherProcessManagerImpl implements IWatcherProcessManager {
158
163
  }
159
164
  }
160
165
 
161
- async watch(uri: UriComponents, options?: { excludes?: string[]; recursive?: boolean }): Promise<number> {
166
+ async watch(
167
+ uri: UriComponents,
168
+ options?: { excludes?: string[]; recursive?: boolean; pollingWatch: boolean },
169
+ ): Promise<number> {
162
170
  this.logger.log('Wait for watcher process ready...');
163
171
  await this._whenReadyDeferred.promise;
164
172
  this.logger.log('start watch: ', uri);