Skip to content

Commit 4836af7

Browse files
committed
feat(nxls): implement passive daemon watcher in vscode
1 parent 48a6fbe commit 4836af7

30 files changed

+1727
-672
lines changed

apps/nxls/src/main.ts

Lines changed: 55 additions & 346 deletions
Large diffs are not rendered by default.

apps/nxls/src/requests.ts

Lines changed: 410 additions & 0 deletions
Large diffs are not rendered by default.

apps/vscode/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,11 @@
215215
"command": "nxConsole.showProblems",
216216
"when": "view == nxProjects && viewItem == projectGraphError",
217217
"group": "inline@1"
218+
},
219+
{
220+
"command": "nxConsole.restartDaemonWatcher",
221+
"when": "view == nxProjects && viewItem == daemonWatcherNotRunning",
222+
"group": "inline@1"
218223
}
219224
],
220225
"editor/title": [
@@ -935,6 +940,12 @@
935940
"command": "nxConsole.showProblems",
936941
"icon": "$(eye)"
937942
},
943+
{
944+
"category": "Nx",
945+
"title": "Restart Daemon Watcher",
946+
"command": "nxConsole.restartDaemonWatcher",
947+
"icon": "$(refresh)"
948+
},
938949
{
939950
"category": "Nx Migrate",
940951
"title": "Refresh View",

libs/language-server/types/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@ export const NxWorkspaceRefreshNotification: NotificationType<void> =
3434
export const NxWorkspaceRefreshStartedNotification: NotificationType<void> =
3535
new NotificationType('nx/refreshWorkspaceStarted');
3636

37+
export const NxWatcherOperationalNotification: NotificationType<{
38+
isOperational: boolean;
39+
}> = new NotificationType('nx/fileWatcherOperational');
40+
3741
export const NxStopDaemonRequest: RequestType<undefined, undefined, unknown> =
3842
new RequestType('nx/stopDaemon');
3943

44+
export const NxStartDaemonRequest: RequestType<undefined, undefined, unknown> =
45+
new RequestType('nx/startDaemon');
46+
4047
export const NxWorkspaceRequest: RequestType<
4148
{ reset: boolean },
4249
NxWorkspace,

libs/language-server/watcher/src/lib/parcel-watcher.ts

Lines changed: 0 additions & 81 deletions
This file was deleted.

libs/language-server/watcher/src/lib/watcher.ts

Lines changed: 142 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
import { lspLogger } from '@nx-console/language-server-utils';
2-
import { getNxVersion } from '@nx-console/shared-nx-workspace-info';
2+
import { gte, NxVersion } from '@nx-console/nx-version';
3+
import {
4+
getNxDaemonClient,
5+
getNxVersion,
6+
} from '@nx-console/shared-nx-workspace-info';
37
import { debounce } from '@nx-console/shared-utils';
4-
import { DaemonWatcher, NativeWatcher } from '@nx-console/shared-watcher';
5-
import { ParcelWatcher } from './parcel-watcher';
6-
import { gte } from '@nx-console/nx-version';
8+
import {
9+
DaemonWatcher,
10+
DaemonWatcherCallback,
11+
NativeWatcher,
12+
PassiveDaemonWatcher,
13+
} from '@nx-console/shared-watcher';
714

8-
let _daemonWatcher: DaemonWatcher | undefined;
15+
let _passiveDaemonWatcher: PassiveDaemonWatcher | undefined;
916
let _nativeWatcher: NativeWatcher | undefined;
17+
let _daemonWatcher: DaemonWatcher | undefined;
1018

1119
export async function cleanupAllWatchers(): Promise<void> {
1220
const cleanupPromises: Promise<void>[] = [];
1321

14-
if (_nativeWatcher) {
22+
if (_passiveDaemonWatcher) {
1523
cleanupPromises.push(
16-
_nativeWatcher.stop().catch((e) => {
24+
Promise.resolve(_passiveDaemonWatcher.dispose()).catch((e) => {
1725
lspLogger.log(
18-
'Error stopping native watcher during global cleanup: ' + e,
26+
'Error stopping daemon watcher during global cleanup: ' + e,
1927
);
2028
}),
2129
);
22-
_nativeWatcher = undefined;
30+
_passiveDaemonWatcher = undefined;
2331
}
2432

2533
if (_daemonWatcher) {
@@ -33,82 +41,147 @@ export async function cleanupAllWatchers(): Promise<void> {
3341
_daemonWatcher = undefined;
3442
}
3543

44+
if (_nativeWatcher) {
45+
cleanupPromises.push(
46+
_nativeWatcher.stop().catch((e) => {
47+
lspLogger.log(
48+
'Error stopping native watcher during global cleanup: ' + e,
49+
);
50+
}),
51+
);
52+
_nativeWatcher = undefined;
53+
}
54+
3655
await Promise.all(cleanupPromises);
3756
}
3857

3958
export async function languageServerWatcher(
4059
workspacePath: string,
41-
callback: () => unknown,
60+
callback: DaemonWatcherCallback,
61+
watcherOperationalCallback?: (isOperational: boolean) => void,
62+
): Promise<() => Promise<void>> {
63+
const nxVersion = await getNxVersion(workspacePath);
64+
if (!nxVersion || !gte(nxVersion, '16.4.0')) {
65+
lspLogger.log(
66+
'File watching is not supported for Nx versions below 16.4.0.',
67+
);
68+
watcherOperationalCallback?.(false);
69+
return async () => {
70+
lspLogger.log('unregistering empty watcher');
71+
};
72+
}
73+
74+
if (gte(nxVersion, '22.0.0')) {
75+
return registerPassiveDaemonWatcher(
76+
workspacePath,
77+
callback,
78+
watcherOperationalCallback,
79+
);
80+
} else {
81+
// older versions don't have this granular watcher tracking so we just assume true
82+
watcherOperationalCallback?.(true);
83+
return registerOldWatcher(workspacePath, nxVersion, callback);
84+
}
85+
}
86+
87+
async function registerPassiveDaemonWatcher(
88+
workspacePath: string,
89+
callback: DaemonWatcherCallback,
90+
watcherOperationalCallback?: (isOperational: boolean) => void,
91+
): Promise<() => Promise<void>> {
92+
const daemonClient = await getNxDaemonClient(workspacePath, lspLogger);
93+
94+
if (!daemonClient.daemonClient.enabled()) {
95+
lspLogger.log('Daemon client is not enabled, file watching not available.');
96+
return async () => {
97+
lspLogger.log('unregistering empty watcher');
98+
};
99+
}
100+
try {
101+
_passiveDaemonWatcher = new PassiveDaemonWatcher(
102+
workspacePath,
103+
lspLogger,
104+
watcherOperationalCallback,
105+
);
106+
await _passiveDaemonWatcher.start();
107+
_passiveDaemonWatcher.listen((error, projectGraphAndSourceMaps) => {
108+
callback(error, projectGraphAndSourceMaps);
109+
});
110+
return async () => {
111+
if (_passiveDaemonWatcher) {
112+
return _passiveDaemonWatcher.dispose();
113+
}
114+
};
115+
} catch (e) {
116+
lspLogger.log(
117+
'Error starting passive daemon watcher: ' + (e as Error).message,
118+
);
119+
return async () => {
120+
lspLogger.log('unregistering empty watcher');
121+
};
122+
}
123+
}
124+
125+
async function registerOldWatcher(
126+
workspacePath: string,
127+
nxVersion: NxVersion,
128+
callback: () => void,
42129
): Promise<() => Promise<void>> {
43-
const version = await getNxVersion(workspacePath);
44130
const debouncedCallback = debounce(callback, 1000);
45131

46-
if (gte(version, '16.4.0')) {
47-
if (process.platform === 'win32') {
48-
if (_nativeWatcher) {
49-
try {
50-
await _nativeWatcher.stop();
51-
} catch (e) {
52-
lspLogger.log('Error stopping previous native watcher: ' + e);
53-
}
132+
if (process.platform === 'win32') {
133+
if (_nativeWatcher) {
134+
try {
135+
await _nativeWatcher.stop();
136+
} catch (e) {
137+
lspLogger.log('Error stopping previous native watcher: ' + e);
138+
}
139+
_nativeWatcher = undefined;
140+
}
141+
const nativeWatcher = new NativeWatcher(
142+
workspacePath,
143+
debouncedCallback,
144+
lspLogger,
145+
);
146+
_nativeWatcher = nativeWatcher;
147+
return async () => {
148+
lspLogger.log('Unregistering file watcher');
149+
try {
150+
await nativeWatcher.stop();
151+
} catch (e) {
152+
lspLogger.log('Error stopping native watcher during cleanup: ' + e);
153+
}
154+
if (_nativeWatcher === nativeWatcher) {
54155
_nativeWatcher = undefined;
55156
}
56-
const nativeWatcher = new NativeWatcher(
57-
workspacePath,
58-
debouncedCallback,
59-
lspLogger,
60-
);
61-
_nativeWatcher = nativeWatcher;
62-
return async () => {
63-
lspLogger.log('Unregistering file watcher');
64-
try {
65-
await nativeWatcher.stop();
66-
} catch (e) {
67-
lspLogger.log('Error stopping native watcher during cleanup: ' + e);
68-
}
69-
if (_nativeWatcher === nativeWatcher) {
70-
_nativeWatcher = undefined;
71-
}
72-
};
73-
} else {
74-
if (_daemonWatcher) {
75-
try {
76-
_daemonWatcher.stop();
77-
} catch (e) {
78-
lspLogger.log('Error stopping previous daemon watcher: ' + e);
79-
}
80-
_daemonWatcher = undefined;
157+
};
158+
} else {
159+
if (_daemonWatcher) {
160+
try {
161+
_daemonWatcher.stop();
162+
} catch (e) {
163+
lspLogger.log('Error stopping previous daemon watcher: ' + e);
81164
}
82-
const daemonWatcher = new DaemonWatcher(
83-
workspacePath,
84-
version,
85-
debouncedCallback,
86-
lspLogger,
87-
);
88-
_daemonWatcher = daemonWatcher;
89-
90-
await daemonWatcher.start();
91-
return async () => {
92-
lspLogger.log('Unregistering file watcher');
93-
try {
94-
await daemonWatcher.stop();
95-
} catch (e) {
96-
lspLogger.log('Error stopping daemon watcher during cleanup: ' + e);
97-
}
98-
if (_daemonWatcher === daemonWatcher) {
99-
_daemonWatcher = undefined;
100-
}
101-
};
165+
_daemonWatcher = undefined;
102166
}
103-
} else {
104-
lspLogger.log('Nx version <16.4.0, using @parcel/watcher');
105-
const parcelWatcher = new ParcelWatcher(workspacePath, debouncedCallback);
167+
const daemonWatcher = new DaemonWatcher(
168+
workspacePath,
169+
nxVersion,
170+
debouncedCallback,
171+
lspLogger,
172+
);
173+
_daemonWatcher = daemonWatcher;
174+
175+
await daemonWatcher.start();
106176
return async () => {
107177
lspLogger.log('Unregistering file watcher');
108178
try {
109-
parcelWatcher.stop();
179+
await daemonWatcher.stop();
110180
} catch (e) {
111-
lspLogger.log('Error stopping parcel watcher during cleanup: ' + e);
181+
lspLogger.log('Error stopping daemon watcher during cleanup: ' + e);
182+
}
183+
if (_daemonWatcher === daemonWatcher) {
184+
_daemonWatcher = undefined;
112185
}
113186
};
114187
}

libs/language-server/watcher/tsconfig.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@
33
"files": [],
44
"include": [],
55
"references": [
6-
{
7-
"path": "../../shared/nx-version"
8-
},
96
{
107
"path": "../../shared/watcher"
118
},
@@ -16,7 +13,7 @@
1613
"path": "../../shared/nx-workspace-info"
1714
},
1815
{
19-
"path": "../../shared/npm"
16+
"path": "../../shared/nx-version"
2017
},
2118
{
2219
"path": "../utils"

0 commit comments

Comments
 (0)