Skip to content
This repository was archived by the owner on Oct 16, 2020. It is now read-only.

Commit 4004060

Browse files
committed
fix: Improve plugin import logic
Also clean up unused code adding TODOs
1 parent 1c2e75e commit 4004060

File tree

1 file changed

+49
-69
lines changed

1 file changed

+49
-69
lines changed

src/project-manager.ts

Lines changed: 49 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,15 @@ import {
2323

2424
export type ConfigType = 'js' | 'ts';
2525

26+
// definitions from from TypeScript server/project.ts
2627
interface Project {
2728
projectService: {
2829
logger: Logger;
2930
};
3031
}
3132

32-
type ServerHost = any;
33+
type ServerHost = object;
3334

34-
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
35-
36-
// copied from TypeScript server/project.ts
3735
interface PluginCreateInfo {
3836
project: Project;
3937
languageService: ts.LanguageService;
@@ -42,6 +40,8 @@ interface PluginCreateInfo {
4240
config: any;
4341
}
4442

43+
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
44+
4545
interface PluginModule {
4646
create(createInfo: PluginCreateInfo): ts.LanguageService;
4747
getExternalFiles?(proj: Project): string[];
@@ -882,87 +882,68 @@ export class ProjectConfiguration {
882882
this.initialized = true;
883883
}
884884

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) {
917886
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+
}
925898

926899
// Enable tsconfig-specified plugins
927900
if (options.plugins) {
928901
for (const pluginConfigEntry of options.plugins as ts.PluginImport[]) {
929902
this.enablePlugin(pluginConfigEntry, searchPaths);
930903
}
931904
}
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
945907
}
946908

947909
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');
952911
this.logger.info(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
953-
const result = this.serverHostRequire(resolvedPath, moduleName);
912+
const result = this.requirePlugin(resolvedPath, moduleName);
954913
if (result.error) {
955914
this.logger.info(`Failed to load module: ${JSON.stringify(result.error)}`);
956915
return undefined;
957916
}
958917
return result.module;
959918
}
960919

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+
}
965936

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[]) {
966947
for (const searchPath of searchPaths) {
967948
const resolvedModule = this.resolveModule(pluginConfigEntry.name, searchPath) as PluginModuleFactory;
968949
if (resolvedModule) {
@@ -976,21 +957,20 @@ export class ProjectConfiguration {
976957
private enableProxy(pluginModuleFactory: PluginModuleFactory, configEntry: ts.PluginImport) {
977958
try {
978959
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`);
980961
return;
981962
}
982963

983964
const info: PluginCreateInfo = {
984965
config: configEntry,
985-
project: { projectService: { logger: this.logger }}, // TODO: this will never work.
966+
project: { projectService: { logger: this.logger }}, // TODO: may need more support
986967
languageService: this.getService(),
987968
languageServiceHost: this.getHost(),
988-
serverHost: undefined // TODO: may need to be faked.
969+
serverHost: {} // TODO: may need an adapter
989970
};
990971

991972
const pluginModule = pluginModuleFactory({ typescript: ts });
992973
this.service = pluginModule.create(info);
993-
// this.plugins.push(pluginModule); TODO: is this needed?
994974
} catch (e) {
995975
this.logger.info(`Plugin activation failed: ${e}`);
996976
}

0 commit comments

Comments
 (0)