@@ -24,14 +24,24 @@ import {
24
24
export type ConfigType = 'js' | 'ts' ;
25
25
26
26
// definitions from from TypeScript server/project.ts
27
- interface Project {
28
- projectService : {
29
- logger : Logger ;
30
- } ;
31
- }
32
27
33
- type ServerHost = object ;
28
+ /**
29
+ * A plugin exports an initialization function, injected with
30
+ * the current typescript instance
31
+ */
32
+ type PluginModuleFactory = ( mod : { typescript : typeof ts } ) => PluginModule ;
33
+
34
+ /**
35
+ * A plugin presents this API when initialized
36
+ */
37
+ interface PluginModule {
38
+ create ( createInfo : PluginCreateInfo ) : ts . LanguageService ;
39
+ getExternalFiles ?( proj : Project ) : string [ ] ;
40
+ }
34
41
42
+ /**
43
+ * All of tsserver's environment exposed to plugins
44
+ */
35
45
interface PluginCreateInfo {
36
46
project : Project ;
37
47
languageService : ts . LanguageService ;
@@ -40,14 +50,24 @@ interface PluginCreateInfo {
40
50
config : any ;
41
51
}
42
52
43
- type RequireResult = { module : { } , error : undefined } | { module : undefined , error : { } } ;
44
-
45
- interface PluginModule {
46
- create ( createInfo : PluginCreateInfo ) : ts . LanguageService ;
47
- getExternalFiles ?( proj : Project ) : string [ ] ;
53
+ /**
54
+ * The portion of tsserver's Project API exposed to plugins
55
+ */
56
+ interface Project {
57
+ projectService : {
58
+ logger : Logger ;
59
+ } ;
48
60
}
49
61
50
- type PluginModuleFactory = ( mod : { typescript : typeof ts } ) => PluginModule ;
62
+ /**
63
+ * The portion of tsserver's ServerHost API exposed to plugins
64
+ */
65
+ type ServerHost = object ;
66
+
67
+ /**
68
+ * The result of a node require: a module or an error.
69
+ */
70
+ type RequireResult = { module : { } , error : undefined } | { module : undefined , error : { } } ;
51
71
52
72
/**
53
73
* ProjectManager translates VFS files to one or many projects denoted by [tj]config.json.
@@ -906,6 +926,27 @@ export class ProjectConfiguration {
906
926
// TODO: support globalPlugins flag if desired
907
927
}
908
928
929
+ /**
930
+ * Tries to load and enable a single plugin
931
+ * @param pluginConfigEntry
932
+ * @param searchPaths
933
+ */
934
+ private enablePlugin ( pluginConfigEntry : ts . PluginImport , searchPaths : string [ ] ) {
935
+ for ( const searchPath of searchPaths ) {
936
+ const resolvedModule = this . resolveModule ( pluginConfigEntry . name , searchPath ) as PluginModuleFactory ;
937
+ if ( resolvedModule ) {
938
+ this . enableProxy ( resolvedModule , pluginConfigEntry ) ;
939
+ return ;
940
+ }
941
+ }
942
+ this . logger . info ( `Couldn't find ${ pluginConfigEntry . name } anywhere in paths: ${ searchPaths . join ( ',' ) } ` ) ;
943
+ }
944
+
945
+ /**
946
+ * Load a plugin use a node require
947
+ * @param moduleName
948
+ * @param initialDir
949
+ */
909
950
private resolveModule ( moduleName : string , initialDir : string ) : { } | undefined {
910
951
const resolvedPath = path . resolve ( initialDir , 'node_modules' ) ;
911
952
this . logger . info ( `Loading ${ moduleName } from ${ initialDir } (resolved to ${ resolvedPath } )` ) ;
@@ -917,6 +958,20 @@ export class ProjectConfiguration {
917
958
return result . module ;
918
959
}
919
960
961
+ /**
962
+ * Resolves a loads a plugin function relative to initialDir
963
+ * @param initialDir
964
+ * @param moduleName
965
+ */
966
+ private requirePlugin ( initialDir : string , moduleName : string ) : RequireResult {
967
+ const modulePath = this . resolveJavaScriptModule ( moduleName , initialDir , this . fs ) ;
968
+ try {
969
+ return { module : require ( modulePath ) , error : undefined } ;
970
+ } catch ( error ) {
971
+ return { module : undefined , error } ;
972
+ }
973
+ }
974
+
920
975
// TODO: stolen from moduleNameResolver.ts because marked as internal
921
976
/**
922
977
* Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations.
@@ -934,26 +989,11 @@ export class ProjectConfiguration {
934
989
return resolvedModule . resolvedFileName ;
935
990
}
936
991
937
- private requirePlugin ( initialDir : string , moduleName : string ) : RequireResult {
938
- const modulePath = this . resolveJavaScriptModule ( moduleName , initialDir , this . fs ) ;
939
- try {
940
- return { module : require ( modulePath ) , error : undefined } ;
941
- } catch ( error ) {
942
- return { module : undefined , error } ;
943
- }
944
- }
945
-
946
- private enablePlugin ( pluginConfigEntry : ts . PluginImport , searchPaths : string [ ] ) {
947
- for ( const searchPath of searchPaths ) {
948
- const resolvedModule = this . resolveModule ( pluginConfigEntry . name , searchPath ) as PluginModuleFactory ;
949
- if ( resolvedModule ) {
950
- this . enableProxy ( resolvedModule , pluginConfigEntry ) ;
951
- return ;
952
- }
953
- }
954
- this . logger . info ( `Couldn't find ${ pluginConfigEntry . name } anywhere in paths: ${ searchPaths . join ( ',' ) } ` ) ;
955
- }
956
-
992
+ /**
993
+ * Replaces the LanguageService with an instance wrapped by the plugin
994
+ * @param pluginModuleFactory function to create the module
995
+ * @param configEntry extra settings from tsconfig to pass to the plugin module
996
+ */
957
997
private enableProxy ( pluginModuleFactory : PluginModuleFactory , configEntry : ts . PluginImport ) {
958
998
try {
959
999
if ( typeof pluginModuleFactory !== 'function' ) {
0 commit comments