Skip to content

Commit

Permalink
configurePlugins command for tsserver
Browse files Browse the repository at this point in the history
Implements #18604
  • Loading branch information
minestarks committed Oct 26, 2018
1 parent 53105b3 commit 4273fd7
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 28 deletions.
16 changes: 14 additions & 2 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ namespace ts.server {
public readonly globalPlugins: ReadonlyArray<string>;
public readonly pluginProbeLocations: ReadonlyArray<string>;
public readonly allowLocalPluginLoads: boolean;
private currentPluginConfigOverrides: Map<any> | undefined;

public readonly typesMapLocation: string | undefined;

public readonly syntaxOnly?: boolean;
Expand Down Expand Up @@ -1667,7 +1669,7 @@ namespace ts.server {
project.enableLanguageService();
project.watchWildcards(createMapFromTemplate(parsedCommandLine.wildcardDirectories!)); // TODO: GH#18217
}
project.enablePluginsWithOptions(compilerOptions);
project.enablePluginsWithOptions(compilerOptions, this.currentPluginConfigOverrides);
const filesToAdd = parsedCommandLine.fileNames.concat(project.getExternalFiles());
this.updateRootAndOptionsOfNonInferredProject(project, filesToAdd, fileNamePropertyReader, compilerOptions, parsedCommandLine.typeAcquisition!, parsedCommandLine.compileOnSave!); // TODO: GH#18217
}
Expand Down Expand Up @@ -1857,7 +1859,7 @@ namespace ts.server {

private createInferredProject(currentDirectory: string | undefined, isSingleInferredProject?: boolean, projectRootPath?: NormalizedPath): InferredProject {
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory);
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath, currentDirectory, this.currentPluginConfigOverrides);
if (isSingleInferredProject) {
this.inferredProjects.unshift(project);
}
Expand Down Expand Up @@ -2806,6 +2808,16 @@ namespace ts.server {

return false;
}

configurePlugin(args: protocol.ConfigurePluginRequestArguments) {
// For any projects that already have the plugin loaded, configure the plugin
this.forEachEnabledProject(project => project.onPluginConfigurationChanged(args.pluginName, args.configuration));

// Also save the current configuration to pass on to any projects that are yet to be loaded.
// If a plugin is configured twice, only the latest configuration will be remembered.
this.currentPluginConfigOverrides = this.currentPluginConfigOverrides || createMap();
this.currentPluginConfigOverrides.set(args.pluginName, args.configuration);
}
}

/* @internal */
Expand Down
63 changes: 42 additions & 21 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ namespace ts.server {
export interface PluginModule {
create(createInfo: PluginCreateInfo): LanguageService;
getExternalFiles?(proj: Project): string[];
onConfigurationChanged?(config: any): void;
}

export interface PluginModuleWithName {
name: string;
module: PluginModule;
}

export type PluginModuleFactory = (mod: { typescript: typeof ts }) => PluginModule;
Expand All @@ -92,7 +98,7 @@ namespace ts.server {
private program: Program;
private externalFiles: SortedReadonlyArray<string>;
private missingFilesMap: Map<FileWatcher>;
private plugins: PluginModule[] = [];
private plugins: PluginModuleWithName[] = [];

/*@internal*/
/**
Expand Down Expand Up @@ -549,9 +555,9 @@ namespace ts.server {

getExternalFiles(): SortedReadonlyArray<string> {
return toSortedArray(flatMap(this.plugins, plugin => {
if (typeof plugin.getExternalFiles !== "function") return;
if (typeof plugin.module.getExternalFiles !== "function") return;
try {
return plugin.getExternalFiles(this);
return plugin.module.getExternalFiles(this);
}
catch (e) {
this.projectService.logger.info(`A plugin threw an exception in getExternalFiles: ${e}`);
Expand Down Expand Up @@ -1105,7 +1111,7 @@ namespace ts.server {
this.rootFilesMap.delete(info.path);
}

protected enableGlobalPlugins(options: CompilerOptions) {
protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
const host = this.projectService.host;

if (!host.require) {
Expand All @@ -1128,12 +1134,13 @@ namespace ts.server {

// 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);

this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths, pluginConfigOverrides);
}
}
}

protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]) {
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<any> | undefined) {
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);

const log = (message: string) => {
Expand All @@ -1143,18 +1150,21 @@ namespace ts.server {
const resolvedModule = firstDefined(searchPaths, searchPath =>
<PluginModuleFactory | undefined>Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log));
if (resolvedModule) {
const configurationOverride = pluginConfigOverrides && pluginConfigOverrides.get(pluginConfigEntry.name);
if (configurationOverride) {
// Preserve the name property since it's immutable
const pluginName = pluginConfigEntry.name;
pluginConfigEntry = configurationOverride;
pluginConfigEntry.name = pluginName;
}

this.enableProxy(resolvedModule, pluginConfigEntry);
}
else {
this.projectService.logger.info(`Couldn't find ${pluginConfigEntry.name}`);
}
}

/** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */
refreshDiagnostics() {
this.projectService.sendProjectsUpdatedInBackgroundEvent();
}

private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: PluginImport) {
try {
if (typeof pluginModuleFactory !== "function") {
Expand All @@ -1180,12 +1190,26 @@ namespace ts.server {
}
this.projectService.logger.info(`Plugin validation succeded`);
this.languageService = newLS;
this.plugins.push(pluginModule);
this.plugins.push({ name: configEntry.name, module: pluginModule });
}
catch (e) {
this.projectService.logger.info(`Plugin activation failed: ${e}`);
}
}

/*@internal*/
onPluginConfigurationChanged(pluginName: string, configuration: any) {
this.plugins.filter(plugin => plugin.name === pluginName).forEach(plugin => {
if (plugin.module.onConfigurationChanged) {
plugin.module.onConfigurationChanged(configuration);
}
});
}

/** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */
refreshDiagnostics() {
this.projectService.sendProjectsUpdatedInBackgroundEvent();
}
}

/**
Expand Down Expand Up @@ -1241,7 +1265,8 @@ namespace ts.server {
documentRegistry: DocumentRegistry,
compilerOptions: CompilerOptions,
projectRootPath: NormalizedPath | undefined,
currentDirectory: string | undefined) {
currentDirectory: string | undefined,
pluginConfigOverrides: Map<any> | undefined) {
super(InferredProject.newName(),
ProjectKind.Inferred,
projectService,
Expand All @@ -1257,7 +1282,7 @@ namespace ts.server {
if (!projectRootPath && !projectService.useSingleInferredProject) {
this.canonicalCurrentDirectory = projectService.toCanonicalFileName(this.currentDirectory);
}
this.enableGlobalPlugins(this.getCompilerOptions());
this.enableGlobalPlugins(this.getCompilerOptions(), pluginConfigOverrides);
}

addRoot(info: ScriptInfo) {
Expand Down Expand Up @@ -1402,12 +1427,8 @@ namespace ts.server {
return program && program.getResolvedProjectReferences();
}

enablePlugins() {
this.enablePluginsWithOptions(this.getCompilerOptions());
}

/*@internal*/
enablePluginsWithOptions(options: CompilerOptions) {
enablePluginsWithOptions(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined) {
const host = this.projectService.host;

if (!host.require) {
Expand All @@ -1428,11 +1449,11 @@ namespace ts.server {
// Enable tsconfig-specified plugins
if (options.plugins) {
for (const pluginConfigEntry of options.plugins) {
this.enablePlugin(pluginConfigEntry, searchPaths);
this.enablePlugin(pluginConfigEntry, searchPaths, pluginConfigOverrides);
}
}

this.enableGlobalPlugins(options);
this.enableGlobalPlugins(options, pluginConfigOverrides);
}

/**
Expand Down
11 changes: 11 additions & 0 deletions src/server/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ namespace ts.server.protocol {
GetEditsForFileRename = "getEditsForFileRename",
/* @internal */
GetEditsForFileRenameFull = "getEditsForFileRename-full",
ConfigurePlugin = "configurePlugin"

// NOTE: If updating this, be sure to also update `allCommandNames` in `harness/unittests/session.ts`.
}
Expand Down Expand Up @@ -1368,6 +1369,16 @@ namespace ts.server.protocol {
export interface ConfigureResponse extends Response {
}

export interface ConfigurePluginRequestArguments {
pluginName: string;
configuration: any;
}

export interface ConfigurePluginRequest extends Request {
command: CommandTypes.ConfigurePlugin;
arguments: ConfigurePluginRequestArguments;
}

/**
* Information found in an "open" request.
*/
Expand Down
8 changes: 8 additions & 0 deletions src/server/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1946,6 +1946,10 @@ namespace ts.server {
this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false);
}

private configurePlugin(args: protocol.ConfigurePluginRequestArguments) {
this.projectService.configurePlugin(args);
}

getCanonicalFileName(fileName: string) {
const name = this.host.useCaseSensitiveFileNames ? fileName : fileName.toLowerCase();
return normalizePath(name);
Expand Down Expand Up @@ -2267,6 +2271,10 @@ namespace ts.server {
[CommandNames.GetEditsForFileRenameFull]: (request: protocol.GetEditsForFileRenameRequest) => {
return this.requiredResponse(this.getEditsForFileRename(request.arguments, /*simplifiedResult*/ false));
},
[CommandNames.ConfigurePlugin]: (request: protocol.ConfigurePluginRequest) => {
this.configurePlugin(request.arguments);
return this.notRequired();
}
});

public addProtocolHandler(command: string, handler: (request: protocol.Request) => HandlerResponse) {
Expand Down
27 changes: 22 additions & 5 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5668,7 +5668,8 @@ declare namespace ts.server.protocol {
GetApplicableRefactors = "getApplicableRefactors",
GetEditsForRefactor = "getEditsForRefactor",
OrganizeImports = "organizeImports",
GetEditsForFileRename = "getEditsForFileRename"
GetEditsForFileRename = "getEditsForFileRename",
ConfigurePlugin = "configurePlugin"
}
/**
* A TypeScript Server message
Expand Down Expand Up @@ -6609,6 +6610,14 @@ declare namespace ts.server.protocol {
*/
interface ConfigureResponse extends Response {
}
interface ConfigurePluginRequestArguments {
pluginName: string;
configuration: any;
}
interface ConfigurePluginRequest extends Request {
command: CommandTypes.ConfigurePlugin;
arguments: ConfigurePluginRequestArguments;
}
/**
* Information found in an "open" request.
*/
Expand Down Expand Up @@ -8035,6 +8044,11 @@ declare namespace ts.server {
interface PluginModule {
create(createInfo: PluginCreateInfo): LanguageService;
getExternalFiles?(proj: Project): string[];
onConfigurationChanged?(config: any): void;
}
interface PluginModuleWithName {
name: string;
module: PluginModule;
}
type PluginModuleFactory = (mod: {
typescript: typeof ts;
Expand Down Expand Up @@ -8173,11 +8187,12 @@ declare namespace ts.server {
filesToString(writeProjectFileNames: boolean): string;
setCompilerOptions(compilerOptions: CompilerOptions): void;
protected removeRoot(info: ScriptInfo): void;
protected enableGlobalPlugins(options: CompilerOptions): void;
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[]): void;
protected enableGlobalPlugins(options: CompilerOptions, pluginConfigOverrides: Map<any> | undefined): void;
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<any> | undefined): void;
private enableProxy;
onPluginConfigurationChanged(pluginName: string, configuration: any): void;
/** Starts a new check for diagnostics. Call this if some file has updated that would cause diagnostics to be changed. */
refreshDiagnostics(): void;
private enableProxy;
}
/**
* If a file is opened and no tsconfig (or jsconfig) is found,
Expand Down Expand Up @@ -8218,7 +8233,6 @@ declare namespace ts.server {
getConfigFilePath(): NormalizedPath;
getProjectReferences(): ReadonlyArray<ProjectReference>;
updateReferences(refs: ReadonlyArray<ProjectReference> | undefined): void;
enablePlugins(): void;
/**
* Get the errors that dont have any file name associated
*/
Expand Down Expand Up @@ -8463,6 +8477,7 @@ declare namespace ts.server {
readonly globalPlugins: ReadonlyArray<string>;
readonly pluginProbeLocations: ReadonlyArray<string>;
readonly allowLocalPluginLoads: boolean;
private currentPluginConfigOverrides;
readonly typesMapLocation: string | undefined;
readonly syntaxOnly?: boolean;
/** Tracks projects that we have already sent telemetry for. */
Expand Down Expand Up @@ -8638,6 +8653,7 @@ declare namespace ts.server {
applySafeList(proj: protocol.ExternalProject): NormalizedPath[];
openExternalProject(proj: protocol.ExternalProject): void;
hasDeferredExtension(): boolean;
configurePlugin(args: protocol.ConfigurePluginRequestArguments): void;
}
}
declare namespace ts.server {
Expand Down Expand Up @@ -8808,6 +8824,7 @@ declare namespace ts.server {
private convertTextChangeToCodeEdit;
private getBraceMatching;
private getDiagnosticsForProject;
private configurePlugin;
getCanonicalFileName(fileName: string): string;
exit(): void;
private notRequired;
Expand Down

0 comments on commit 4273fd7

Please sign in to comment.