mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 12:15:34 -06:00
Load global plugins for inferred projects
Fixes #18322 Ensure that we also try to load global plugins for inferred projects. Moves global plugin loading logic to base `Project` class from `ConfigureProject` .
This commit is contained in:
parent
ccd5608392
commit
993a21e4e2
@ -121,6 +121,7 @@ namespace ts.server {
|
||||
private program: Program;
|
||||
private externalFiles: SortedReadonlyArray<string>;
|
||||
private missingFilesMap: Map<FileWatcher>;
|
||||
private plugins: PluginModule[] = [];
|
||||
|
||||
private cachedUnresolvedImportsPerFile = new UnresolvedImportsMap();
|
||||
private lastCachedUnresolvedImportsList: SortedReadonlyArray<string>;
|
||||
@ -515,7 +516,18 @@ namespace ts.server {
|
||||
}
|
||||
|
||||
getExternalFiles(): SortedReadonlyArray<string> {
|
||||
return emptyArray as SortedReadonlyArray<string>;
|
||||
return toSortedArray(flatMap(this.plugins, plugin => {
|
||||
if (typeof plugin.getExternalFiles !== "function") return;
|
||||
try {
|
||||
return plugin.getExternalFiles(this);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`);
|
||||
if (e.stack) {
|
||||
this.projectService.logger.info(e.stack);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
getSourceFile(path: Path) {
|
||||
@ -1029,6 +1041,84 @@ namespace ts.server {
|
||||
orderedRemoveItem(this.rootFiles, info);
|
||||
this.rootFilesMap.delete(info.path);
|
||||
}
|
||||
|
||||
protected enableGlobalPlugins() {
|
||||
const host = this.projectService.host;
|
||||
const options = this.getCompilationSettings();
|
||||
|
||||
if (!host.require) {
|
||||
this.projectService.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Search our peer node_modules, then any globally-specified probe paths
|
||||
// ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
|
||||
const searchPaths = [combinePaths(this.projectService.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
|
||||
|
||||
if (this.projectService.globalPlugins) {
|
||||
// Enable global plugins with synthetic configuration entries
|
||||
for (const globalPluginName of this.projectService.globalPlugins) {
|
||||
// Skip empty names from odd commandline parses
|
||||
if (!globalPluginName) continue;
|
||||
|
||||
// Skip already-locally-loaded plugins
|
||||
if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue;
|
||||
|
||||
// Provide global: true so plugins can detect why they can't find their config
|
||||
this.projectService.logger.info(`Loading global plugin ${globalPluginName}`);
|
||||
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
|
||||
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
|
||||
|
||||
const log = (message: string) => {
|
||||
this.projectService.logger.info(message);
|
||||
};
|
||||
|
||||
for (const searchPath of searchPaths) {
|
||||
const resolvedModule = <PluginModuleFactory>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log);
|
||||
if (resolvedModule) {
|
||||
this.enableProxy(resolvedModule, pluginConfigEntry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
|
||||
}
|
||||
|
||||
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
|
||||
try {
|
||||
if (typeof pluginModuleFactory !== "function") {
|
||||
this.projectService.logger.info(`Skipped loading plugin ${configEntry.name} because it did expose a proper factory function`);
|
||||
return;
|
||||
}
|
||||
|
||||
const info: PluginCreateInfo = {
|
||||
config: configEntry,
|
||||
project: this,
|
||||
languageService: this.languageService,
|
||||
languageServiceHost: this,
|
||||
serverHost: this.projectService.host
|
||||
};
|
||||
|
||||
const pluginModule = pluginModuleFactory({ typescript: ts });
|
||||
const newLS = pluginModule.create(info);
|
||||
for (const k of Object.keys(this.languageService)) {
|
||||
if (!(k in newLS)) {
|
||||
this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`);
|
||||
(newLS as any)[k] = (this.languageService as any)[k];
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Plugin validation succeded`);
|
||||
this.languageService = newLS;
|
||||
this.plugins.push(pluginModule);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`Plugin activation failed: ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1092,6 +1182,7 @@ namespace ts.server {
|
||||
projectService.host,
|
||||
currentDirectory);
|
||||
this.projectRootPath = projectRootPath && projectService.toCanonicalFileName(projectRootPath);
|
||||
this.enableGlobalPlugins();
|
||||
}
|
||||
|
||||
addRoot(info: ScriptInfo) {
|
||||
@ -1153,8 +1244,6 @@ namespace ts.server {
|
||||
/*@internal*/
|
||||
configFileSpecs: ConfigFileSpecs;
|
||||
|
||||
private plugins: PluginModule[] = [];
|
||||
|
||||
/** Ref count to the project when opened from external project */
|
||||
private externalProjectRefCount = 0;
|
||||
|
||||
@ -1236,69 +1325,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.projectService.globalPlugins) {
|
||||
// Enable global plugins with synthetic configuration entries
|
||||
for (const globalPluginName of this.projectService.globalPlugins) {
|
||||
// Skip empty names from odd commandline parses
|
||||
if (!globalPluginName) continue;
|
||||
|
||||
// Skip already-locally-loaded plugins
|
||||
if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue;
|
||||
|
||||
// Provide global: true so plugins can detect why they can't find their config
|
||||
this.projectService.logger.info(`Loading global plugin ${globalPluginName}`);
|
||||
this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
|
||||
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
|
||||
|
||||
const log = (message: string) => {
|
||||
this.projectService.logger.info(message);
|
||||
};
|
||||
|
||||
for (const searchPath of searchPaths) {
|
||||
const resolvedModule = <PluginModuleFactory>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log);
|
||||
if (resolvedModule) {
|
||||
this.enableProxy(resolvedModule, pluginConfigEntry);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
|
||||
}
|
||||
|
||||
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
|
||||
try {
|
||||
if (typeof pluginModuleFactory !== "function") {
|
||||
this.projectService.logger.info(`Skipped loading plugin ${configEntry.name} because it did expose a proper factory function`);
|
||||
return;
|
||||
}
|
||||
|
||||
const info: PluginCreateInfo = {
|
||||
config: configEntry,
|
||||
project: this,
|
||||
languageService: this.languageService,
|
||||
languageServiceHost: this,
|
||||
serverHost: this.projectService.host
|
||||
};
|
||||
|
||||
const pluginModule = pluginModuleFactory({ typescript: ts });
|
||||
const newLS = pluginModule.create(info);
|
||||
for (const k of Object.keys(this.languageService)) {
|
||||
if (!(k in newLS)) {
|
||||
this.projectService.logger.info(`Plugin activation warning: Missing proxied method ${k} in created LS. Patching.`);
|
||||
(newLS as any)[k] = (this.languageService as any)[k];
|
||||
}
|
||||
}
|
||||
this.projectService.logger.info(`Plugin validation succeded`);
|
||||
this.languageService = newLS;
|
||||
this.plugins.push(pluginModule);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`Plugin activation failed: ${e}`);
|
||||
}
|
||||
this.enableGlobalPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1327,21 +1354,6 @@ namespace ts.server {
|
||||
return this.typeAcquisition;
|
||||
}
|
||||
|
||||
getExternalFiles(): SortedReadonlyArray<string> {
|
||||
return toSortedArray(flatMap(this.plugins, plugin => {
|
||||
if (typeof plugin.getExternalFiles !== "function") return;
|
||||
try {
|
||||
return plugin.getExternalFiles(this);
|
||||
}
|
||||
catch (e) {
|
||||
this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`);
|
||||
if (e.stack) {
|
||||
this.projectService.logger.info(e.stack);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
watchWildcards(wildcardDirectories: Map<WatchDirectoryFlags>) {
|
||||
updateWatchingWildcardDirectories(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user