mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Prototype TS plugins on web
This prototype allows service plugins to be loaded on web TSServer Main changes: - Adds a new host entryPoint called `importServicePlugin` for overriding how plugins can be loaded. This may be async - Implement `importServicePlugin` for webServer - The web server plugin implementation looks for a `browser` field in the plugin's `package.json` - It then uses `import(...)` to load the plugin (the plugin source must be compiled to support being loaded as a module)
This commit is contained in:
+21
-2
@@ -1577,7 +1577,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
protected enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<any> | undefined) {
|
||||
protected async enablePlugin(pluginConfigEntry: PluginImport, searchPaths: string[], pluginConfigOverrides: Map<any> | undefined) {
|
||||
this.projectService.logger.info(`Enabling plugin ${pluginConfigEntry.name} from candidate paths: ${searchPaths.join(",")}`);
|
||||
if (!pluginConfigEntry.name || parsePackageName(pluginConfigEntry.name).rest) {
|
||||
this.projectService.logger.info(`Skipped loading plugin ${pluginConfigEntry.name || JSON.stringify(pluginConfigEntry)} because only package name is allowed plugin name`);
|
||||
@@ -1589,8 +1589,27 @@ namespace ts.server {
|
||||
const logError = (message: string) => {
|
||||
(errorLogs || (errorLogs = [])).push(message);
|
||||
};
|
||||
const resolvedModule = firstDefined(searchPaths, searchPath =>
|
||||
|
||||
let resolvedModule: any | undefined;
|
||||
if (this.projectService.host.importServicePlugin) {
|
||||
for (const searchPath of searchPaths) {
|
||||
try {
|
||||
resolvedModule = await this.projectService.host.importServicePlugin(searchPath, pluginConfigEntry.name);
|
||||
}
|
||||
catch (e) {
|
||||
// TODO: log this?
|
||||
continue;
|
||||
}
|
||||
if (resolvedModule) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
resolvedModule = firstDefined(searchPaths, searchPath =>
|
||||
Project.resolveModule(pluginConfigEntry.name, searchPath, this.projectService.host, log, logError) as PluginModuleFactory | undefined);
|
||||
}
|
||||
|
||||
if (resolvedModule) {
|
||||
const configurationOverride = pluginConfigOverrides && pluginConfigOverrides.get(pluginConfigEntry.name);
|
||||
if (configurationOverride) {
|
||||
|
||||
@@ -16,5 +16,6 @@ declare namespace ts.server {
|
||||
gc?(): void;
|
||||
trace?(s: string): void;
|
||||
require?(initialPath: string, moduleName: string): RequireResult;
|
||||
importServicePlugin?(root: string, moduleName: string): Promise<any>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
/*@internal*/
|
||||
/// <reference lib="dom" />
|
||||
|
||||
namespace ts.server {
|
||||
export interface HostWithWriteMessage {
|
||||
writeMessage(s: any): void;
|
||||
@@ -137,6 +139,22 @@ namespace ts.server {
|
||||
/* eslint-enable no-restricted-globals */
|
||||
|
||||
require: () => ({ module: undefined, error: new Error("Not implemented") }),
|
||||
importServicePlugin: async (root: string, moduleName: string) => {
|
||||
const packageRoot = combinePaths(root, "node_modules", moduleName);
|
||||
|
||||
const packageJsonResponse = await fetch(combinePaths(packageRoot, "package.json"));
|
||||
const packageJson = await packageJsonResponse.json();
|
||||
const browser = packageJson.browser;
|
||||
if (!browser) {
|
||||
throw new Error("Could not load plugin. No 'browser' field found in package.json.");
|
||||
}
|
||||
|
||||
const scriptPath = combinePaths(packageRoot, browser);
|
||||
|
||||
// TODO: TS rewrites `import(...)` to `require`. Use eval to bypass this
|
||||
// eslint-disable-next-line no-eval
|
||||
return eval(`import(${JSON.stringify(scriptPath)})`);
|
||||
},
|
||||
exit: notImplemented,
|
||||
|
||||
// Debugging related
|
||||
|
||||
Reference in New Issue
Block a user