Skip to content

Commit 83822a3

Browse files
committed
Add searchPaths option so we know where to look for watchFactory from server which follows same locations as other Plugins
1 parent fc57b16 commit 83822a3

File tree

28 files changed

+654
-612
lines changed

28 files changed

+654
-612
lines changed

src/compiler/sys.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ namespace ts {
846846
/*@internal*/
847847
export function resolveModule<T = {}>(
848848
pluginConfigEntry: PluginImport,
849-
searchPaths: string[],
849+
searchPaths: readonly string[],
850850
host: Pick<System, "require" | "resolvePath">,
851851
log: (message: string) => void,
852852
): ImportPluginResult<T> {
@@ -945,9 +945,12 @@ namespace ts {
945945
sysLog(`Skipped loading watchFactory ${isString(options.watchFactory) ? options.watchFactory : JSON.stringify(options.watchFactory)} because it can be named with only package name`);
946946
return setUserWatchFactory(options, /*userWatchFactory*/ undefined);
947947
}
948-
const searchPaths = [
949-
combinePaths(system.getExecutingFilePath(), "../../..")
950-
];
948+
const host = options.getHost?.();
949+
const searchPaths = host ?
950+
host.searchPaths :
951+
[
952+
combinePaths(system.getExecutingFilePath(), "../../..")
953+
];
951954
sysLog(`Enabling watchFactory ${isString(options.watchFactory) ? options.watchFactory : JSON.stringify(options.watchFactory)} from candidate paths: ${searchPaths.join(",")}`);
952955
const { resolvedModule, errorLogs, pluginConfigEntry } = resolveModule<UserWatchFactoryModule>(
953956
isString(options.watchFactory) ? { name: options.watchFactory } : options.watchFactory,

src/compiler/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6679,6 +6679,10 @@ namespace ts {
66796679
watchDirectory?(fileName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher;
66806680
onConfigurationChanged?(config: any): void;
66816681
}
6682+
/*@internal*/
6683+
export interface WatchOptionsFactoryHost {
6684+
searchPaths: readonly string[];
6685+
}
66826686
export interface WatchOptions {
66836687
watchFile?: WatchFileKind;
66846688
watchDirectory?: WatchDirectoryKind;
@@ -6690,6 +6694,7 @@ namespace ts {
66906694

66916695
// All the internal properties are set as non enumerable and non configurable so that they arenot enumerated when checking if options have changed
66926696
/* @internal */ getResolvedWatchFactory?(): UserWatchFactory | undefined;
6697+
/* @internal */ getHost?(): WatchOptionsFactoryHost;
66936698

66946699
[option: string]: CompilerOptionsValue | undefined;
66956700
}

src/server/editorServices.ts

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,7 @@ namespace ts.server {
13111311
* This is to watch whenever files are added or removed to the wildcard directories
13121312
*/
13131313
/*@internal*/
1314-
private watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, configFileName: NormalizedPath, config: ParsedConfig) {
1314+
private watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, configFileName: NormalizedPath, canonicalConfigFilePath: NormalizedPath, config: ParsedConfig) {
13151315
return this.watchFactory.watchDirectory(
13161316
directory,
13171317
fileOrDirectory => {
@@ -1370,7 +1370,7 @@ namespace ts.server {
13701370
});
13711371
},
13721372
flags,
1373-
this.getWatchOptionsFromProjectWatchOptions(config.parsedCommandLine!.watchOptions),
1373+
this.getWatchOptionsFromProjectWatchOptions(config.parsedCommandLine!.watchOptions, canonicalConfigFilePath),
13741374
WatchType.WildcardDirectory,
13751375
configFileName
13761376
);
@@ -1684,7 +1684,7 @@ namespace ts.server {
16841684
configFileName,
16851685
(_fileName, eventKind) => this.onConfigFileChanged(canonicalConfigFilePath, eventKind),
16861686
PollingInterval.High,
1687-
this.getWatchOptionsFromProjectWatchOptions(configFileExistenceInfo?.config?.parsedCommandLine?.watchOptions),
1687+
this.getWatchOptionsFromProjectWatchOptions(configFileExistenceInfo?.config?.parsedCommandLine?.watchOptions, canonicalConfigFilePath),
16881688
WatchType.ConfigFile,
16891689
forProject
16901690
);
@@ -2236,9 +2236,9 @@ namespace ts.server {
22362236
// If watch options different than older options when setting for the first time, update the config file watcher
22372237
if (!oldCommandLine && !isJsonEqual(
22382238
// Old options
2239-
this.getWatchOptionsFromProjectWatchOptions(/*projectOptions*/ undefined),
2239+
this.getWatchOptionsFromProjectWatchOptions(/*projectOptions*/ undefined, canonicalConfigFilePath),
22402240
// New options
2241-
this.getWatchOptionsFromProjectWatchOptions(parsedCommandLine.watchOptions)
2241+
this.getWatchOptionsFromProjectWatchOptions(parsedCommandLine.watchOptions, canonicalConfigFilePath)
22422242
)) {
22432243
// Reset the config file watcher
22442244
configFileExistenceInfo.watcher?.close();
@@ -2284,7 +2284,7 @@ namespace ts.server {
22842284
config!.watchedDirectories ||= new Map(),
22852285
new Map(getEntries(config!.parsedCommandLine!.wildcardDirectories!)),
22862286
// Create new directory watcher
2287-
(directory, flags) => this.watchWildcardDirectory(directory as Path, flags, configFileName, config!),
2287+
(directory, flags) => this.watchWildcardDirectory(directory as Path, flags, configFileName, forProject.canonicalConfigFilePath, config!),
22882288
);
22892289
}
22902290
else {
@@ -3044,6 +3044,9 @@ namespace ts.server {
30443044
if (args.watchOptions) {
30453045
const result = convertWatchOptions(args.watchOptions);
30463046
this.hostConfiguration.watchOptions = result?.watchOptions;
3047+
if (this.hostConfiguration.watchOptions?.watchFactory) {
3048+
this.setWatchOptionsFactoryHost(this.hostConfiguration.watchOptions, /*canonicalConfigFilePath*/ undefined);
3049+
}
30473050
this.projectWatchOptions.clear();
30483051
this.logger.info(`Host watch options changed to ${JSON.stringify(this.hostConfiguration.watchOptions)}, it will be take effect for next watches.`);
30493052
if (result?.errors?.length) {
@@ -3059,22 +3062,30 @@ namespace ts.server {
30593062
}
30603063

30613064
/*@internal*/
3062-
getWatchOptions(project: Project) {
3063-
return this.getWatchOptionsFromProjectWatchOptions(project.getWatchOptions());
3065+
getWatchOptions(project: Project): WatchOptions | undefined {
3066+
return this.getWatchOptionsFromProjectWatchOptions(project.getWatchOptions(), isConfiguredProject(project) ? project.canonicalConfigFilePath : undefined);
30643067
}
30653068

30663069
/*@internal*/
3067-
private getWatchOptionsFromProjectWatchOptions(projectOptions: WatchOptions | undefined) {
3070+
private getWatchOptionsFromProjectWatchOptions(projectOptions: WatchOptions | undefined, canonicalConfigFilePath: NormalizedPath | undefined) {
30683071
if (!projectOptions) return this.hostConfiguration.watchOptions;
30693072
let options = this.projectWatchOptions.get(projectOptions);
30703073
if (options) return options;
30713074
this.projectWatchOptions.set(projectOptions, options = this.hostConfiguration.watchOptions ?
30723075
{ ...this.hostConfiguration.watchOptions, ...projectOptions } :
30733076
projectOptions
30743077
);
3078+
this.setWatchOptionsFactoryHost(options, canonicalConfigFilePath);
30753079
return options;
30763080
}
30773081

3082+
/*@internal*/
3083+
private setWatchOptionsFactoryHost(options: WatchOptions, canonicalConfigFilePath: NormalizedPath | undefined) {
3084+
setWatchOptionInternalProperty(options, "getHost", memoize(() => ({
3085+
searchPaths: this.getProjectPluginSearchPaths(canonicalConfigFilePath)
3086+
})));
3087+
}
3088+
30783089
/*@internal*/
30793090
clearWatchOptionsFromProjectWatchOptions(projectOptions: WatchOptions | undefined) {
30803091
if (projectOptions) this.projectWatchOptions.delete(projectOptions);
@@ -4097,6 +4108,27 @@ namespace ts.server {
40974108
return false;
40984109
}
40994110

4111+
/*@internal */
4112+
getGlobalPluginSearchPaths() {
4113+
// Search any globally-specified probe paths, then our peer node_modules
4114+
return [
4115+
...this.pluginProbeLocations,
4116+
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
4117+
combinePaths(this.getExecutingFilePath(), "../../.."),
4118+
];
4119+
}
4120+
4121+
/*@internal*/
4122+
getProjectPluginSearchPaths(canonicalConfigFilePath: string | undefined) {
4123+
const searchPaths = this.getGlobalPluginSearchPaths();
4124+
if (canonicalConfigFilePath && this.allowLocalPluginLoads) {
4125+
const local = getDirectoryPath(canonicalConfigFilePath);
4126+
this.logger.info(`Local plugin loading enabled; adding ${local} to search paths`);
4127+
searchPaths.unshift(local);
4128+
}
4129+
return searchPaths;
4130+
}
4131+
41004132
/*@internal*/
41014133
requestEnablePlugin(project: Project, pluginConfigEntry: PluginImport, searchPaths: string[]) {
41024134
if (!this.host.importPlugin && !this.host.require) {

src/server/project.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,13 +1604,8 @@ namespace ts.server {
16041604
}
16051605

16061606
/*@internal*/
1607-
protected getGlobalPluginSearchPaths() {
1608-
// Search any globally-specified probe paths, then our peer node_modules
1609-
return [
1610-
...this.projectService.pluginProbeLocations,
1611-
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
1612-
combinePaths(this.projectService.getExecutingFilePath(), "../../.."),
1613-
];
1607+
protected getProjectPluginSearchPaths() {
1608+
return this.projectService.getProjectPluginSearchPaths(/*canonicalConfigFilePath*/ undefined);
16141609
}
16151610

16161611
protected enableGlobalPlugins(options: CompilerOptions): void {
@@ -1623,7 +1618,7 @@ namespace ts.server {
16231618
}
16241619

16251620
// Enable global plugins with synthetic configuration entries
1626-
const searchPaths = this.getGlobalPluginSearchPaths();
1621+
const searchPaths = this.projectService.getGlobalPluginSearchPaths();
16271622
for (const globalPluginName of this.projectService.globalPlugins) {
16281623
// Skip empty names from odd commandline parses
16291624
if (!globalPluginName) continue;
@@ -2481,6 +2476,11 @@ namespace ts.server {
24812476
return this.getCurrentProgram()?.forEachResolvedProjectReference(cb);
24822477
}
24832478

2479+
/*@internal*/
2480+
protected getProjectPluginSearchPaths() {
2481+
return this.projectService.getProjectPluginSearchPaths(this.canonicalConfigFilePath);
2482+
}
2483+
24842484
/*@internal*/
24852485
enablePluginsWithOptions(options: CompilerOptions): void {
24862486
this.plugins.length = 0;
@@ -2491,14 +2491,8 @@ namespace ts.server {
24912491
return;
24922492
}
24932493

2494-
const searchPaths = this.getGlobalPluginSearchPaths();
2495-
if (this.projectService.allowLocalPluginLoads) {
2496-
const local = getDirectoryPath(this.canonicalConfigFilePath);
2497-
this.projectService.logger.info(`Local plugin loading enabled; adding ${local} to search paths`);
2498-
searchPaths.unshift(local);
2499-
}
2500-
25012494
// Enable tsconfig-specified plugins
2495+
const searchPaths = this.getProjectPluginSearchPaths();
25022496
if (options.plugins) {
25032497
for (const pluginConfigEntry of options.plugins) {
25042498
this.enablePlugin(pluginConfigEntry, searchPaths);

tests/baselines/reference/tsserver/watchEnvironment/watchFactory-as-configuration-of-host-allowLocalPluginLoads-object.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Info 6 [00:00:29.000] Search path: /user/username/projects/myproject
9494
Info 7 [00:00:30.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
9595
Info 8 [00:00:31.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
9696
Info 9 [00:00:32.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}} Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
97-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
97+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
9898
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
9999
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
100100
Info 10 [00:00:33.000] Config: /user/username/projects/myproject/tsconfig.json : {

tests/baselines/reference/tsserver/watchEnvironment/watchFactory-as-configuration-of-host-allowLocalPluginLoads.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Info 6 [00:00:29.000] Search path: /user/username/projects/myproject
9191
Info 7 [00:00:30.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
9292
Info 8 [00:00:31.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
9393
Info 9 [00:00:32.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"} Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
94-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
94+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
9595
Require:: Module myplugin created with config: {"name":"myplugin"} and options: {"watchFactory":"myplugin"}
9696
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"}
9797
Info 10 [00:00:33.000] Config: /user/username/projects/myproject/tsconfig.json : {

tests/baselines/reference/tsserver/watchEnvironment/watchFactory-as-configuration-of-host-object.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ Info 6 [00:00:29.000] Search path: /user/username/projects/myproject
9494
Info 7 [00:00:30.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
9595
Info 8 [00:00:31.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
9696
Info 9 [00:00:32.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}} Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
97-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
97+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
9898
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
9999
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
100100
Info 10 [00:00:33.000] Config: /user/username/projects/myproject/tsconfig.json : {

tests/baselines/reference/tsserver/watchEnvironment/watchFactory-as-configuration-of-host-with-pluginOverride-allowLocalPluginLoads-object.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
138138
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
139139
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
140140
Info 12 [00:00:35.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}} Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
141-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
141+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
142142
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
143143
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
144144
Info 13 [00:00:36.000] Config: /user/username/projects/myproject/tsconfig.json : {

tests/baselines/reference/tsserver/watchEnvironment/watchFactory-as-configuration-of-host-with-pluginOverride-object.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
138138
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
139139
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
140140
Info 12 [00:00:35.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}} Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
141-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
141+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
142142
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
143143
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
144144
Info 13 [00:00:36.000] Config: /user/username/projects/myproject/tsconfig.json : {

tests/baselines/reference/tsserver/watchEnvironment/watchFactory-as-configuration-of-host-with-pluginoverride-allowLocalPluginLoads.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
135135
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
136136
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
137137
Info 12 [00:00:35.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"} Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
138-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
138+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
139139
Require:: Module myplugin created with config: {"name":"myplugin"} and options: {"watchFactory":"myplugin"}
140140
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"}
141141
Info 13 [00:00:36.000] Config: /user/username/projects/myproject/tsconfig.json : {

tests/baselines/reference/tsserver/watchEnvironment/watchFactory-as-configuration-of-host-with-pluginoverride.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
135135
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
136136
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
137137
Info 12 [00:00:35.000] FileWatcher:: Added:: WatchInfo: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"} Project: /user/username/projects/myproject/tsconfig.json WatchType: Config file
138-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
138+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
139139
Require:: Module myplugin created with config: {"name":"myplugin"} and options: {"watchFactory":"myplugin"}
140140
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"}
141141
Info 13 [00:00:36.000] Config: /user/username/projects/myproject/tsconfig.json : {

0 commit comments

Comments
 (0)