Skip to content

Commit 4f7694c

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 d3de4f1 commit 4f7694c

File tree

28 files changed

+663
-619
lines changed

28 files changed

+663
-619
lines changed

src/compiler/sys.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -903,7 +903,7 @@ export interface ImportPluginResult<T> {
903903
/** @internal */
904904
export function resolveModule<T = {}>(
905905
pluginConfigEntry: PluginImport,
906-
searchPaths: string[],
906+
searchPaths: readonly string[],
907907
host: Pick<System, "require" | "resolvePath">,
908908
log: (message: string) => void,
909909
): ImportPluginResult<T> {
@@ -1002,9 +1002,12 @@ export function createSystemWatchFunctions({
10021002
sysLog(`Skipped loading watchFactory ${isString(options.watchFactory) ? options.watchFactory : JSON.stringify(options.watchFactory)} because it can be named with only package name`);
10031003
return setUserWatchFactory(options, /*userWatchFactory*/ undefined);
10041004
}
1005-
const searchPaths = [
1006-
combinePaths(system.getExecutingFilePath(), "../../..")
1007-
];
1005+
const host = options.getHost?.();
1006+
const searchPaths = host ?
1007+
host.searchPaths :
1008+
[
1009+
combinePaths(system.getExecutingFilePath(), "../../..")
1010+
];
10081011
sysLog(`Enabling watchFactory ${isString(options.watchFactory) ? options.watchFactory : JSON.stringify(options.watchFactory)} from candidate paths: ${searchPaths.join(",")}`);
10091012
const { resolvedModule, errorLogs, pluginConfigEntry } = resolveModule<UserWatchFactoryModule>(
10101013
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
@@ -7176,6 +7176,10 @@ export interface UserWatchFactory {
71767176
watchDirectory?(fileName: string, callback: DirectoryWatcherCallback, recursive: boolean, options: WatchOptions | undefined): FileWatcher;
71777177
onConfigurationChanged?(config: any): void;
71787178
}
7179+
/**@internal*/
7180+
export interface WatchOptionsFactoryHost {
7181+
searchPaths: readonly string[];
7182+
}
71797183
export interface WatchOptions {
71807184
watchFile?: WatchFileKind;
71817185
watchDirectory?: WatchDirectoryKind;
@@ -7187,6 +7191,7 @@ export interface WatchOptions {
71877191

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

71917196
[option: string]: CompilerOptionsValue | Function | undefined;
71927197
}

src/server/editorServices.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {
7676
map,
7777
mapDefinedEntries,
7878
mapDefinedIterator,
79+
memoize,
7980
missingFileModifiedTime,
8081
MultiMap,
8182
noop,
@@ -105,6 +106,7 @@ import {
105106
returnNoopFileWatcher,
106107
returnTrue,
107108
ScriptKind,
109+
setWatchOptionInternalProperty,
108110
SharedExtendedConfigFileWatcher,
109111
some,
110112
SourceFile,
@@ -1494,7 +1496,7 @@ export class ProjectService {
14941496
*
14951497
* @internal
14961498
*/
1497-
private watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, configFileName: NormalizedPath, config: ParsedConfig) {
1499+
private watchWildcardDirectory(directory: Path, flags: WatchDirectoryFlags, configFileName: NormalizedPath, canonicalConfigFilePath: NormalizedPath, config: ParsedConfig) {
14981500
return this.watchFactory.watchDirectory(
14991501
directory,
15001502
fileOrDirectory => {
@@ -1553,7 +1555,7 @@ export class ProjectService {
15531555
});
15541556
},
15551557
flags,
1556-
this.getWatchOptionsFromProjectWatchOptions(config.parsedCommandLine!.watchOptions),
1558+
this.getWatchOptionsFromProjectWatchOptions(config.parsedCommandLine!.watchOptions, canonicalConfigFilePath),
15571559
WatchType.WildcardDirectory,
15581560
configFileName
15591561
);
@@ -1867,7 +1869,7 @@ export class ProjectService {
18671869
configFileName,
18681870
(_fileName, eventKind) => this.onConfigFileChanged(canonicalConfigFilePath, eventKind),
18691871
PollingInterval.High,
1870-
this.getWatchOptionsFromProjectWatchOptions(configFileExistenceInfo?.config?.parsedCommandLine?.watchOptions),
1872+
this.getWatchOptionsFromProjectWatchOptions(configFileExistenceInfo?.config?.parsedCommandLine?.watchOptions, canonicalConfigFilePath),
18711873
WatchType.ConfigFile,
18721874
forProject
18731875
);
@@ -2423,9 +2425,9 @@ export class ProjectService {
24232425
// If watch options different than older options when setting for the first time, update the config file watcher
24242426
if (!oldCommandLine && !isJsonEqual(
24252427
// Old options
2426-
this.getWatchOptionsFromProjectWatchOptions(/*projectOptions*/ undefined),
2428+
this.getWatchOptionsFromProjectWatchOptions(/*projectOptions*/ undefined, canonicalConfigFilePath),
24272429
// New options
2428-
this.getWatchOptionsFromProjectWatchOptions(parsedCommandLine.watchOptions)
2430+
this.getWatchOptionsFromProjectWatchOptions(parsedCommandLine.watchOptions, canonicalConfigFilePath)
24292431
)) {
24302432
// Reset the config file watcher
24312433
configFileExistenceInfo.watcher?.close();
@@ -2471,7 +2473,7 @@ export class ProjectService {
24712473
config!.watchedDirectories ||= new Map(),
24722474
new Map(Object.entries(config!.parsedCommandLine!.wildcardDirectories!)),
24732475
// Create new directory watcher
2474-
(directory, flags) => this.watchWildcardDirectory(directory as Path, flags, configFileName, config!),
2476+
(directory, flags) => this.watchWildcardDirectory(directory as Path, flags, configFileName, forProject.canonicalConfigFilePath, config!),
24752477
);
24762478
}
24772479
else {
@@ -3234,6 +3236,9 @@ export class ProjectService {
32343236
if (args.watchOptions) {
32353237
const result = convertWatchOptions(args.watchOptions);
32363238
this.hostConfiguration.watchOptions = result?.watchOptions;
3239+
if (this.hostConfiguration.watchOptions?.watchFactory) {
3240+
this.setWatchOptionsFactoryHost(this.hostConfiguration.watchOptions, /*canonicalConfigFilePath*/ undefined);
3241+
}
32373242
this.projectWatchOptions.clear();
32383243
this.logger.info(`Host watch options changed to ${JSON.stringify(this.hostConfiguration.watchOptions)}, it will be take effect for next watches.`);
32393244
if (result?.errors?.length) {
@@ -3249,22 +3254,30 @@ export class ProjectService {
32493254
}
32503255

32513256
/** @internal */
3252-
getWatchOptions(project: Project) {
3253-
return this.getWatchOptionsFromProjectWatchOptions(project.getWatchOptions());
3257+
getWatchOptions(project: Project): WatchOptions | undefined {
3258+
return this.getWatchOptionsFromProjectWatchOptions(project.getWatchOptions(), isConfiguredProject(project) ? project.canonicalConfigFilePath : undefined);
32543259
}
32553260

32563261
/** @internal */
3257-
private getWatchOptionsFromProjectWatchOptions(projectOptions: WatchOptions | undefined) {
3262+
private getWatchOptionsFromProjectWatchOptions(projectOptions: WatchOptions | undefined, canonicalConfigFilePath: NormalizedPath | undefined) {
32583263
if (!projectOptions) return this.hostConfiguration.watchOptions;
32593264
let options = this.projectWatchOptions.get(projectOptions);
32603265
if (options) return options;
32613266
this.projectWatchOptions.set(projectOptions, options = this.hostConfiguration.watchOptions ?
32623267
{ ...this.hostConfiguration.watchOptions, ...projectOptions } :
32633268
projectOptions
32643269
);
3270+
this.setWatchOptionsFactoryHost(options, canonicalConfigFilePath);
32653271
return options;
32663272
}
32673273

3274+
/** @internal */
3275+
private setWatchOptionsFactoryHost(options: WatchOptions, canonicalConfigFilePath: NormalizedPath | undefined) {
3276+
setWatchOptionInternalProperty(options, "getHost", memoize(() => ({
3277+
searchPaths: this.getProjectPluginSearchPaths(canonicalConfigFilePath)
3278+
})));
3279+
}
3280+
32683281
/** @internal */
32693282
clearWatchOptionsFromProjectWatchOptions(projectOptions: WatchOptions | undefined) {
32703283
if (projectOptions) this.projectWatchOptions.delete(projectOptions);
@@ -4272,6 +4285,27 @@ export class ProjectService {
42724285
return false;
42734286
}
42744287

4288+
/** @internal */
4289+
getGlobalPluginSearchPaths() {
4290+
// Search any globally-specified probe paths, then our peer node_modules
4291+
return [
4292+
...this.pluginProbeLocations,
4293+
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
4294+
combinePaths(this.getExecutingFilePath(), "../../.."),
4295+
];
4296+
}
4297+
4298+
/** @internal */
4299+
getProjectPluginSearchPaths(canonicalConfigFilePath: string | undefined) {
4300+
const searchPaths = this.getGlobalPluginSearchPaths();
4301+
if (canonicalConfigFilePath && this.allowLocalPluginLoads) {
4302+
const local = getDirectoryPath(canonicalConfigFilePath);
4303+
this.logger.info(`Local plugin loading enabled; adding ${local} to search paths`);
4304+
searchPaths.unshift(local);
4305+
}
4306+
return searchPaths;
4307+
}
4308+
42754309
/** @internal */
42764310
requestEnablePlugin(project: Project, pluginConfigEntry: PluginImport, searchPaths: string[]) {
42774311
if (!this.host.importPlugin && !this.host.require) {

src/server/project.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,12 +1786,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
17861786

17871787
/** @internal */
17881788
protected getGlobalPluginSearchPaths() {
1789-
// Search any globally-specified probe paths, then our peer node_modules
1790-
return [
1791-
...this.projectService.pluginProbeLocations,
1792-
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
1793-
combinePaths(this.projectService.getExecutingFilePath(), "../../.."),
1794-
];
1789+
return this.projectService.getProjectPluginSearchPaths(/*canonicalConfigFilePath*/ undefined);
17951790
}
17961791

17971792
protected enableGlobalPlugins(options: CompilerOptions): void {
@@ -1804,7 +1799,7 @@ export abstract class Project implements LanguageServiceHost, ModuleResolutionHo
18041799
}
18051800

18061801
// Enable global plugins with synthetic configuration entries
1807-
const searchPaths = this.getGlobalPluginSearchPaths();
1802+
const searchPaths = this.projectService.getGlobalPluginSearchPaths();
18081803
for (const globalPluginName of this.projectService.globalPlugins) {
18091804
// Skip empty names from odd commandline parses
18101805
if (!globalPluginName) continue;
@@ -2669,6 +2664,11 @@ export class ConfiguredProject extends Project {
26692664
return this.getCurrentProgram()?.forEachResolvedProjectReference(cb);
26702665
}
26712666

2667+
/** @internal */
2668+
protected getProjectPluginSearchPaths() {
2669+
return this.projectService.getProjectPluginSearchPaths(this.canonicalConfigFilePath);
2670+
}
2671+
26722672
/** @internal */
26732673
enablePluginsWithOptions(options: CompilerOptions): void {
26742674
this.plugins.length = 0;
@@ -2679,14 +2679,8 @@ export class ConfiguredProject extends Project {
26792679
return;
26802680
}
26812681

2682-
const searchPaths = this.getGlobalPluginSearchPaths();
2683-
if (this.projectService.allowLocalPluginLoads) {
2684-
const local = getDirectoryPath(this.canonicalConfigFilePath);
2685-
this.projectService.logger.info(`Local plugin loading enabled; adding ${local} to search paths`);
2686-
searchPaths.unshift(local);
2687-
}
2688-
26892682
// Enable tsconfig-specified plugins
2683+
const searchPaths = this.getProjectPluginSearchPaths();
26902684
if (options.plugins) {
26912685
for (const pluginConfigEntry of options.plugins) {
26922686
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
@@ -61,7 +61,7 @@ Info 6 [00:00:29.000] Search path: /user/username/projects/myproject
6161
Info 7 [00:00:30.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
6262
Info 8 [00:00:31.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
6363
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
64-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
64+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
6565
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
6666
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
6767
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
@@ -58,7 +58,7 @@ Info 6 [00:00:29.000] Search path: /user/username/projects/myproject
5858
Info 7 [00:00:30.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
5959
Info 8 [00:00:31.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
6060
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
61-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
61+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
6262
Require:: Module myplugin created with config: {"name":"myplugin"} and options: {"watchFactory":"myplugin"}
6363
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"}
6464
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
@@ -61,7 +61,7 @@ Info 6 [00:00:29.000] Search path: /user/username/projects/myproject
6161
Info 7 [00:00:30.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
6262
Info 8 [00:00:31.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
6363
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
64-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
64+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
6565
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
6666
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
6767
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
@@ -83,7 +83,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
8383
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
8484
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
8585
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
86-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
86+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
8787
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
8888
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
8989
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
@@ -80,7 +80,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
8080
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
8181
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
8282
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
83-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
83+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
8484
Require:: Module myplugin created with config: {"name":"myplugin"} and options: {"watchFactory":"myplugin"}
8585
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"}
8686
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
@@ -83,7 +83,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
8383
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
8484
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
8585
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
86-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
86+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
8787
Require:: Module myplugin created with config: {"name":"myplugin","myconfig":"somethingelse"} and options: {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
8888
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":{"name":"myplugin","myconfig":"somethingelse"}}
8989
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
@@ -80,7 +80,7 @@ Info 9 [00:00:32.000] Search path: /user/username/projects/myproject
8080
Info 10 [00:00:33.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
8181
Info 11 [00:00:34.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
8282
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
83-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
83+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
8484
Require:: Module myplugin created with config: {"name":"myplugin"} and options: {"watchFactory":"myplugin"}
8585
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"}
8686
Info 13 [00:00:36.000] Config: /user/username/projects/myproject/tsconfig.json : {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Info 6 [00:00:29.000] Search path: /user/username/projects/myproject
5858
Info 7 [00:00:30.000] For info: /user/username/projects/myproject/a.ts :: Config file name: /user/username/projects/myproject/tsconfig.json
5959
Info 8 [00:00:31.000] Creating configuration project /user/username/projects/myproject/tsconfig.json
6060
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
61-
CustomRequire:: Resolving myplugin from /a/lib/tsc.js/../../../node_modules
61+
CustomRequire:: Resolving myplugin from /a/pluginprobe1/node_modules
6262
Require:: Module myplugin created with config: {"name":"myplugin"} and options: {"watchFactory":"myplugin"}
6363
Custom watchFile: /user/username/projects/myproject/tsconfig.json 2000 {"watchFactory":"myplugin"}
6464
Info 10 [00:00:33.000] Config: /user/username/projects/myproject/tsconfig.json : {

0 commit comments

Comments
 (0)