@@ -23,17 +23,15 @@ import {
23
23
24
24
export type ConfigType = 'js' | 'ts' ;
25
25
26
+ // definitions from from TypeScript server/project.ts
26
27
interface Project {
27
28
projectService : {
28
29
logger : Logger ;
29
30
} ;
30
31
}
31
32
32
- type ServerHost = any ;
33
+ type ServerHost = object ;
33
34
34
- type RequireResult = { module : { } , error : undefined } | { module : undefined , error : { } } ;
35
-
36
- // copied from TypeScript server/project.ts
37
35
interface PluginCreateInfo {
38
36
project : Project ;
39
37
languageService : ts . LanguageService ;
@@ -42,6 +40,8 @@ interface PluginCreateInfo {
42
40
config : any ;
43
41
}
44
42
43
+ type RequireResult = { module : { } , error : undefined } | { module : undefined , error : { } } ;
44
+
45
45
interface PluginModule {
46
46
create ( createInfo : PluginCreateInfo ) : ts . LanguageService ;
47
47
getExternalFiles ?( proj : Project ) : string [ ] ;
@@ -882,87 +882,68 @@ export class ProjectConfiguration {
882
882
this . initialized = true ;
883
883
}
884
884
885
- private serverHostRequire ( initialDir : string , moduleName : string ) : RequireResult {
886
- // const modulePath = ts.resolveJavaScriptModule(moduleName, initialDir, this.fs));
887
- const modulePath = initialDir + '/' + moduleName ;
888
- try {
889
- return { module : require ( modulePath ) , error : undefined } ;
890
- } catch ( error ) {
891
- return { module : undefined , error } ;
892
- }
893
- }
894
-
895
- // // marked @internal in ts :(
896
- // private resolveJavaScriptModule(moduleName: string, initialDir: string, host: ts.ModuleResolutionHost): string {
897
- // const { resolvedModule, failedLookupLocations } =
898
- // ts.nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
899
- // if (!resolvedModule) {
900
- // throw new Error(`Could not resolve JS module ${moduleName} starting at ${initialDir}. Looked in: ${failedLookupLocations.join(', ')}`);
901
- // }
902
- // return resolvedModule.resolvedFileName;
903
- // }
904
-
905
- enablePlugins ( options : ts . CompilerOptions ) {
906
- // const host = this.getHost();
907
- // const options = this.getCompilerOptions();
908
-
909
- // if (!host.require) {
910
- // this.logger.info("Plugins were requested but not running in environment that supports 'require'. Nothing will be loaded");
911
- // return;
912
- // }
913
-
914
- // Search our peer node_modules, then any globally-specified probe paths
915
- // ../../.. to walk from X/node_modules/typescript/lib/tsserver.js to X/node_modules/
916
- // const searchPaths = [combinePaths(host.getExecutingFilePath(), "../../.."), ...this.projectService.pluginProbeLocations];
885
+ private enablePlugins ( options : ts . CompilerOptions ) {
917
886
const searchPaths = [ ] ;
918
- // if (this.projectService.allowLocalPluginLoads) {
919
- // const local = ts.getDirectoryPath(this.configFilePath);
920
- const local = this . rootFilePath ;
921
- this . logger . info ( `enablePlugins for ${ this . configFilePath } ` ) ;
922
- this . logger . info ( `Local plugin loading enabled; adding ${ local } to search paths` ) ;
923
- searchPaths . unshift ( local ) ;
924
- // }
887
+ // TODO: support pluginProbeLocations?
888
+ // TODO: add peer node_modules to source path.
889
+
890
+ // TODO: determine how to expose this setting.
891
+ // VS Code starts tsserver with --allowLocalPluginLoads by default: https://github.com/Microsoft/TypeScript/pull/15924
892
+ const allowLocalPluginLoads = true ;
893
+ if ( allowLocalPluginLoads ) {
894
+ const local = this . rootFilePath ;
895
+ this . logger . info ( `Local plugin loading enabled; adding ${ local } to search paths` ) ;
896
+ searchPaths . unshift ( local ) ;
897
+ }
925
898
926
899
// Enable tsconfig-specified plugins
927
900
if ( options . plugins ) {
928
901
for ( const pluginConfigEntry of options . plugins as ts . PluginImport [ ] ) {
929
902
this . enablePlugin ( pluginConfigEntry , searchPaths ) ;
930
903
}
931
904
}
932
- const file = '' ;
933
- file . toLowerCase ( ) ;
934
-
935
- // if (this.projectService.globalPlugins) {
936
- // // Enable global plugins with synthetic configuration entries
937
- // for (const globalPluginName of this.projectService.globalPlugins) {
938
- // // Skip already-locally-loaded plugins
939
- // if (options.plugins && options.plugins.some(p => p.name === globalPluginName)) continue;
940
-
941
- // // Provide global: true so plugins can detect why they can't find their config
942
- // this.enablePlugin({ name: globalPluginName, global: true } as PluginImport, searchPaths);
943
- // }
944
- // }
905
+
906
+ // TODO: support globalPlugins flag if desired
945
907
}
946
908
947
909
private resolveModule ( moduleName : string , initialDir : string ) : { } | undefined {
948
- // const resolvedPath = ts.normalizeSlashes(host.resolvePath(ts.combinePaths(initialDir, 'node_modules')));
949
- const pathResolver = initialDir . includes ( '\\' ) ? path . win32 : path . posix ;
950
-
951
- const resolvedPath = pathResolver . resolve ( initialDir , 'node_modules' ) ;
910
+ const resolvedPath = path . resolve ( initialDir , 'node_modules' ) ;
952
911
this . logger . info ( `Loading ${ moduleName } from ${ initialDir } (resolved to ${ resolvedPath } )` ) ;
953
- const result = this . serverHostRequire ( resolvedPath , moduleName ) ;
912
+ const result = this . requirePlugin ( resolvedPath , moduleName ) ;
954
913
if ( result . error ) {
955
914
this . logger . info ( `Failed to load module: ${ JSON . stringify ( result . error ) } ` ) ;
956
915
return undefined ;
957
916
}
958
917
return result . module ;
959
918
}
960
919
961
- private enablePlugin ( pluginConfigEntry : ts . PluginImport , searchPaths : string [ ] ) {
962
- // const log = (message: string) => {
963
- // this.logger.info(message);
964
- // };
920
+ // TODO: stolen from moduleNameResolver.ts because marked as internal
921
+ /**
922
+ * Expose resolution logic to allow us to use Node module resolution logic from arbitrary locations.
923
+ * No way to do this with `require()`: https://github.com/nodejs/node/issues/5963
924
+ * Throws an error if the module can't be resolved.
925
+ */
926
+ private resolveJavaScriptModule ( moduleName : string , initialDir : string , host : ts . ModuleResolutionHost ) : string {
927
+ const { resolvedModule /* , failedLookupLocations */ } =
928
+ ts . nodeModuleNameResolver ( moduleName , /* containingFile */ initialDir , { moduleResolution : ts . ModuleResolutionKind . NodeJs , allowJs : true } , this . fs , undefined ) ;
929
+ // TODO: jsOnly flag missing :(
930
+ if ( ! resolvedModule ) {
931
+ // TODO: add Looked in: ${failedLookupLocations.join(', ')} back into error.
932
+ throw new Error ( `Could not resolve JS module ${ moduleName } starting at ${ initialDir } .` ) ;
933
+ }
934
+ return resolvedModule . resolvedFileName ;
935
+ }
965
936
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 [ ] ) {
966
947
for ( const searchPath of searchPaths ) {
967
948
const resolvedModule = this . resolveModule ( pluginConfigEntry . name , searchPath ) as PluginModuleFactory ;
968
949
if ( resolvedModule ) {
@@ -976,21 +957,20 @@ export class ProjectConfiguration {
976
957
private enableProxy ( pluginModuleFactory : PluginModuleFactory , configEntry : ts . PluginImport ) {
977
958
try {
978
959
if ( typeof pluginModuleFactory !== 'function' ) {
979
- this . logger . info ( `Skipped loading plugin ${ configEntry . name } because it did expose a proper factory function` ) ;
960
+ this . logger . info ( `Skipped loading plugin ${ configEntry . name } because it didn't expose a proper factory function` ) ;
980
961
return ;
981
962
}
982
963
983
964
const info : PluginCreateInfo = {
984
965
config : configEntry ,
985
- project : { projectService : { logger : this . logger } } , // TODO: this will never work.
966
+ project : { projectService : { logger : this . logger } } , // TODO: may need more support
986
967
languageService : this . getService ( ) ,
987
968
languageServiceHost : this . getHost ( ) ,
988
- serverHost : undefined // TODO: may need to be faked.
969
+ serverHost : { } // TODO: may need an adapter
989
970
} ;
990
971
991
972
const pluginModule = pluginModuleFactory ( { typescript : ts } ) ;
992
973
this . service = pluginModule . create ( info ) ;
993
- // this.plugins.push(pluginModule); TODO: is this needed?
994
974
} catch ( e ) {
995
975
this . logger . info ( `Plugin activation failed: ${ e } ` ) ;
996
976
}
0 commit comments