The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/public/app/features/plugins/loader/systemjsHooks.ts

87 lines
3.3 KiB

import { config } from '@grafana/runtime';
import { transformPluginSourceForCDN } from '../cdn/utils';
import { resolveWithCache } from './cache';
import { LOAD_PLUGIN_CSS_REGEX, JS_CONTENT_TYPE_REGEX, SHARED_DEPENDENCY_PREFIX } from './constants';
import { SystemJS } from './systemjs';
import { SystemJSWithLoaderHooks } from './types';
import { isHostedOnCDN } from './utils';
export async function decorateSystemJSFetch(
systemJSFetch: SystemJSWithLoaderHooks['fetch'],
url: string,
options?: Record<string, unknown>
) {
const res = await systemJSFetch(url, options);
const contentType = res.headers.get('content-type') || '';
if (JS_CONTENT_TYPE_REGEX.test(contentType)) {
const source = await res.text();
let transformedSrc = source;
// JS files on the CDN need their asset paths transformed in the source
if (isHostedOnCDN(res.url)) {
const cdnTransformedSrc = transformPluginSourceForCDN({ url: res.url, source: transformedSrc });
return new Response(new Blob([cdnTransformedSrc], { type: 'text/javascript' }));
}
return new Response(new Blob([transformedSrc], { type: 'text/javascript' }));
}
return res;
}
export function decorateSystemJSResolve(
this: SystemJSWithLoaderHooks,
originalResolve: SystemJSWithLoaderHooks['resolve'],
id: string,
parentUrl?: string
) {
try {
const url = originalResolve.apply(this, [id, parentUrl]);
const cleanedUrl = getBackWardsCompatibleUrl(url);
const isFileSystemModule =
(cleanedUrl.endsWith('.js') || cleanedUrl.endsWith('.css')) && !isHostedOnCDN(cleanedUrl);
// Add a cache query param for filesystem module.js requests
// CDN hosted plugins contain the version in the path so skip
return isFileSystemModule ? resolveWithCache(cleanedUrl) : cleanedUrl;
} catch (err) {
// Provide fallback for plugins that use `loadPluginCss` to load theme styles
// Regex only targets plugins on the filesystem.
if (LOAD_PLUGIN_CSS_REGEX.test(id)) {
const prefixId = `${config.appSubUrl ?? ''}/public/${id}`;
const url = originalResolve.apply(this, [prefixId, parentUrl]);
return resolveWithCache(url);
}
console.warn(`SystemJS: failed to resolve '${id}'`);
return id;
}
}
export function decorateSystemJsOnload(err: unknown, id: string) {
// IF the url is relative resolve to current origin, absolute urls passed in will ignore base.
const url = new URL(id, window.location.origin);
if (url.pathname.endsWith('.css') && !err) {
const module = SystemJS.get(id);
const styles = module?.default;
if (styles) {
document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles];
}
}
}
// This function handles the following legacy SystemJS functionality:
// - strips legacy loader wildcard from urls
// - support config.defaultExtension for System.register deps that lack an extension (e.g. './my_ctrl')
function getBackWardsCompatibleUrl(url: string) {
if (url.startsWith(`${SHARED_DEPENDENCY_PREFIX}:`)) {
return url;
}
if (url.endsWith('!')) {
url = url.slice(0, -1);
}
const systemJSFileExtensions = ['css', 'js', 'json', 'wasm'];
const hasValidFileExtension = systemJSFileExtensions.some((extensionName) => url.endsWith(extensionName));
return hasValidFileExtension ? url : url + '.js';
}