mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Plugins: Selectively load plugins using script tags (#85750)
* feat(plugins): introduce logic to selectively load fe plugins via script tags * feat(plugins): extend cache to store isAngular flag. use isAngular in shouldFetch * revert(plugins): remove unused prepareImport from SystemJSWithLoaderHooks type * fix(plugins): cache[path] maybe undefined if not registered or invalidated * Update public/app/features/plugins/plugin_loader.ts Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com> --------- Co-authored-by: Levente Balogh <balogh.levente.hu@gmail.com>
This commit is contained in:
parent
60ed6bfc33
commit
fd1bf66d86
@ -1,17 +1,22 @@
|
||||
import { clearPluginSettingsCache } from '../pluginSettings';
|
||||
|
||||
const cache: Record<string, string> = {};
|
||||
const cache: Record<string, CacheablePlugin> = {};
|
||||
const initializedAt: number = Date.now();
|
||||
|
||||
type CacheablePlugin = {
|
||||
path: string;
|
||||
version: string;
|
||||
isAngular?: boolean;
|
||||
};
|
||||
|
||||
export function registerPluginInCache({ path, version }: CacheablePlugin): void {
|
||||
export function registerPluginInCache({ path, version, isAngular }: CacheablePlugin): void {
|
||||
const key = extractPath(path);
|
||||
if (key && !cache[key]) {
|
||||
cache[key] = encodeURI(version);
|
||||
cache[key] = {
|
||||
version: encodeURI(version),
|
||||
isAngular,
|
||||
path,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,12 +33,19 @@ export function resolveWithCache(url: string, defaultBust = initializedAt): stri
|
||||
if (!path) {
|
||||
return `${url}?_cache=${defaultBust}`;
|
||||
}
|
||||
|
||||
const version = cache[path];
|
||||
const version = cache[path]?.version;
|
||||
const bust = version || defaultBust;
|
||||
return `${url}?_cache=${bust}`;
|
||||
}
|
||||
|
||||
export function getPluginFromCache(path: string): CacheablePlugin | undefined {
|
||||
const key = extractPath(path);
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
return cache[key];
|
||||
}
|
||||
|
||||
function extractPath(address: string): string | undefined {
|
||||
const match = /\/?.+\/(plugins\/.+\/module)\.js/i.exec(address);
|
||||
if (!match) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Extend the System type with the loader hooks we use
|
||||
// to provide backwards compatibility with older version of Systemjs
|
||||
export type SystemJSWithLoaderHooks = typeof System & {
|
||||
shouldFetch: () => Boolean;
|
||||
shouldFetch: (url: string) => Boolean;
|
||||
fetch: (url: string, options?: Record<string, unknown>) => Promise<Response>;
|
||||
onload: (err: unknown, id: string) => void;
|
||||
};
|
||||
|
@ -11,14 +11,14 @@ import { DataQuery } from '@grafana/schema';
|
||||
import { GenericDataSourcePlugin } from '../datasources/types';
|
||||
|
||||
import builtInPlugins from './built_in_plugins';
|
||||
import { registerPluginInCache } from './loader/cache';
|
||||
import { getPluginFromCache, registerPluginInCache } from './loader/cache';
|
||||
// SystemJS has to be imported before the sharedDependenciesMap
|
||||
import { SystemJS } from './loader/systemjs';
|
||||
// eslint-disable-next-line import/order
|
||||
import { sharedDependenciesMap } from './loader/sharedDependencies';
|
||||
import { decorateSystemJSFetch, decorateSystemJSResolve, decorateSystemJsOnload } from './loader/systemjsHooks';
|
||||
import { SystemJSWithLoaderHooks } from './loader/types';
|
||||
import { buildImportMap, resolveModulePath } from './loader/utils';
|
||||
import { buildImportMap, isHostedOnCDN, resolveModulePath } from './loader/utils';
|
||||
import { importPluginModuleInSandbox } from './sandbox/sandbox_plugin_loader';
|
||||
import { isFrontendSandboxSupported } from './sandbox/utils';
|
||||
|
||||
@ -28,9 +28,13 @@ SystemJS.addImportMap({ imports });
|
||||
|
||||
const systemJSPrototype: SystemJSWithLoaderHooks = SystemJS.constructor.prototype;
|
||||
|
||||
// Monaco Editors reliance on RequireJS means we need to transform
|
||||
// the content of the plugin code at runtime which can only be done with fetch/eval.
|
||||
systemJSPrototype.shouldFetch = () => true;
|
||||
// This instructs SystemJS to load a plugin using fetch and eval if it returns a truthy value, otherwise it will load the plugin using a script tag.
|
||||
// We only want to fetch and eval plugins that are hosted on a CDN or are Angular plugins.
|
||||
systemJSPrototype.shouldFetch = function (url) {
|
||||
const pluginInfo = getPluginFromCache(url);
|
||||
|
||||
return isHostedOnCDN(url) || Boolean(pluginInfo?.isAngular);
|
||||
};
|
||||
|
||||
const originalImport = systemJSPrototype.import;
|
||||
// Hook Systemjs import to support plugins that only have a default export.
|
||||
@ -68,7 +72,7 @@ export async function importPluginModule({
|
||||
isAngular?: boolean;
|
||||
}): Promise<System.Module> {
|
||||
if (version) {
|
||||
registerPluginInCache({ path, version });
|
||||
registerPluginInCache({ path, version, isAngular });
|
||||
}
|
||||
|
||||
const builtIn = builtInPlugins[path];
|
||||
|
Loading…
Reference in New Issue
Block a user