@@ -25,14 +25,24 @@ import {
25
25
export type ConfigType = 'js' | 'ts' ;
26
26
27
27
// definitions from from TypeScript server/project.ts
28
- interface Project {
29
- projectService : {
30
- logger : Logger ;
31
- } ;
32
- }
33
28
34
- type ServerHost = object ;
29
+ /**
30
+ * A plugin exports an initialization function, injected with
31
+ * the current typescript instance
32
+ */
33
+ type PluginModuleFactory = ( mod : { typescript : typeof ts } ) => PluginModule ;
34
+
35
+ /**
36
+ * A plugin presents this API when initialized
37
+ */
38
+ interface PluginModule {
39
+ create ( createInfo : PluginCreateInfo ) : ts . LanguageService ;
40
+ getExternalFiles ?( proj : Project ) : string [ ] ;
41
+ }
35
42
43
+ /**
44
+ * All of tsserver's environment exposed to plugins
45
+ */
36
46
interface PluginCreateInfo {
37
47
project : Project ;
38
48
languageService : ts . LanguageService ;
@@ -41,14 +51,24 @@ interface PluginCreateInfo {
41
51
config : any ;
42
52
}
43
53
44
- type RequireResult = { module : { } , error : undefined } | { module : undefined , error : { } } ;
45
-
46
- interface PluginModule {
47
- create ( createInfo : PluginCreateInfo ) : ts . LanguageService ;
48
- getExternalFiles ?( proj : Project ) : string [ ] ;
54
+ /**
55
+ * The portion of tsserver's Project API exposed to plugins
56
+ */
57
+ interface Project {
58
+ projectService : {
59
+ logger : Logger ;
60
+ } ;
49
61
}
50
62
51
- type PluginModuleFactory = ( mod : { typescript : typeof ts } ) => PluginModule ;
63
+ /**
64
+ * The portion of tsserver's ServerHost API exposed to plugins
65
+ */
66
+ type ServerHost = object ;
67
+
68
+ /**
69
+ * The result of a node require: a module or an error.
70
+ */
71
+ type RequireResult = { module : { } , error : undefined } | { module : undefined , error : { } } ;
52
72
53
73
/**
54
74
* ProjectManager translates VFS files to one or many projects denoted by [tj]config.json.
@@ -964,6 +984,27 @@ export class ProjectConfiguration {
964
984
// TODO: support globalPlugins flag if desired
965
985
}
966
986
987
+ /**
988
+ * Tries to load and enable a single plugin
989
+ * @param pluginConfigEntry
990
+ * @param searchPaths
991
+ */
992
+ private enablePlugin ( pluginConfigEntry : ts . PluginImport , searchPaths : string [ ] ) {
993
+ for ( const searchPath of searchPaths ) {
994
+ const resolvedModule = this . resolveModule ( pluginConfigEntry . name , searchPath ) as PluginModuleFactory ;
995
+ if ( resolvedModule ) {
996
+ this . enableProxy ( resolvedModule , pluginConfigEntry ) ;
997
+ return ;
998
+ }
999
+ }
1000
+ this . logger . info ( `Couldn't find ${ pluginConfigEntry . name } anywhere in paths: ${ searchPaths . join ( ',' ) } ` ) ;
1001
+ }
1002
+
1003
+ /**
1004
+ * Load a plugin use a node require
1005
+ * @param moduleName
1006
+ * @param initialDir
1007
+ */
967
1008
private resolveModule ( moduleName : string , initialDir : string ) : { } | undefined {
968
1009
const resolvedPath = path . resolve ( initialDir , 'node_modules' ) ;
969
1010
this . logger . info ( `Loading ${ moduleName } from ${ initialDir } (resolved to ${ resolvedPath } )` ) ;
@@ -975,6 +1016,20 @@ export class ProjectConfiguration {
975
1016
return result . module ;
976
1017
}
977
1018
1019
+ /**
1020
+ * Resolves a loads a plugin function relative to initialDir
1021
+ * @param initialDir
1022
+ * @param moduleName
1023
+ */
1024
+ private requirePlugin ( initialDir : string , moduleName : string ) : RequireResult {
1025
+ const modulePath = this . resolveJavaScriptModule ( moduleName , initialDir , this . fs ) ;
1026
+ try {
1027
+ return { module : require ( modulePath ) , error : undefined } ;
1028
+ } catch ( error ) {
1029
+ return { module : undefined , error } ;
1030
+ }
1031
+ }
1032
+
978
1033
// TODO: stolen from moduleNameResolver.ts because marked as internal
979
1034
/**
980
1035
* Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations.
@@ -992,26 +1047,11 @@ export class ProjectConfiguration {
992
1047
return resolvedModule . resolvedFileName ;
993
1048
}
994
1049
995
- private requirePlugin ( initialDir : string , moduleName : string ) : RequireResult {
996
- const modulePath = this . resolveJavaScriptModule ( moduleName , initialDir , this . fs ) ;
997
- try {
998
- return { module : require ( modulePath ) , error : undefined } ;
999
- } catch ( error ) {
1000
- return { module : undefined , error } ;
1001
- }
1002
- }
1003
-
1004
- private enablePlugin ( pluginConfigEntry : ts . PluginImport , searchPaths : string [ ] ) {
1005
- for ( const searchPath of searchPaths ) {
1006
- const resolvedModule = this . resolveModule ( pluginConfigEntry . name , searchPath ) as PluginModuleFactory ;
1007
- if ( resolvedModule ) {
1008
- this . enableProxy ( resolvedModule , pluginConfigEntry ) ;
1009
- return ;
1010
- }
1011
- }
1012
- this . logger . info ( `Couldn't find ${ pluginConfigEntry . name } anywhere in paths: ${ searchPaths . join ( ',' ) } ` ) ;
1013
- }
1014
-
1050
+ /**
1051
+ * Replaces the LanguageService with an instance wrapped by the plugin
1052
+ * @param pluginModuleFactory function to create the module
1053
+ * @param configEntry extra settings from tsconfig to pass to the plugin module
1054
+ */
1015
1055
private enableProxy ( pluginModuleFactory : PluginModuleFactory , configEntry : ts . PluginImport ) {
1016
1056
try {
1017
1057
if ( typeof pluginModuleFactory !== 'function' ) {
0 commit comments