2022-04-22 14:33:13 +01:00
import * as emotion from '@emotion/css' ;
2022-09-19 16:54:13 +02:00
import * as emotionReact from '@emotion/react' ;
2022-04-22 14:33:13 +01:00
import * as d3 from 'd3' ;
2017-12-20 12:33:33 +01:00
import jquery from 'jquery' ;
2022-04-22 14:33:13 +01:00
import _ from 'lodash' ; // eslint-disable-line lodash/import-scope
import moment from 'moment' ; // eslint-disable-line no-restricted-imports
2018-06-14 12:32:57 +01:00
import prismjs from 'prismjs' ;
import react from 'react' ;
import reactDom from 'react-dom' ;
2022-09-19 10:49:35 +01:00
import * as reactRedux from 'react-redux' ; // eslint-disable-line no-restricted-imports
2023-04-27 07:18:38 +02:00
import * as reactRouterDom from 'react-router-dom' ;
import * as reactRouterCompat from 'react-router-dom-v5-compat' ;
2019-11-01 09:05:18 -06:00
import * as redux from 'redux' ;
2022-04-22 14:33:13 +01:00
import * as rxjs from 'rxjs' ;
import * as rxjsOperators from 'rxjs/operators' ;
import slate from 'slate' ;
import slatePlain from 'slate-plain-serializer' ;
2022-09-06 15:23:48 +01:00
import slateReact from 'slate-react' ;
2018-06-14 12:32:57 +01:00
2022-04-22 14:33:13 +01:00
import * as grafanaData from '@grafana/data' ;
import * as grafanaRuntime from '@grafana/runtime' ;
import * as grafanaUIraw from '@grafana/ui' ;
2022-08-03 03:33:13 -07:00
import TableModel from 'app/core/TableModel' ;
2017-12-20 12:33:33 +01:00
import config from 'app/core/config' ;
2021-11-09 08:37:16 +01:00
import { appEvents , contextSrv } from 'app/core/core' ;
2019-04-12 09:45:22 -07:00
import { BackendSrv , getBackendSrv } from 'app/core/services/backend_srv' ;
2017-12-20 12:33:33 +01:00
import impressionSrv from 'app/core/services/impression_srv' ;
2022-04-22 14:33:13 +01:00
import TimeSeries from 'app/core/time_series2' ;
import * as flatten from 'app/core/utils/flatten' ;
import kbn from 'app/core/utils/kbn' ;
import * as ticks from 'app/core/utils/ticks' ;
2022-07-20 09:25:09 +02:00
import { GenericDataSourcePlugin } from '../datasources/types' ;
2022-04-22 14:33:13 +01:00
import builtInPlugins from './built_in_plugins' ;
2023-06-28 15:58:37 +02:00
import { PLUGIN_CDN_URL_KEY } from './constants' ;
2023-06-05 10:51:36 +02:00
import { sandboxPluginDependencies } from './sandbox/plugin_dependencies' ;
import { importPluginModuleInSandbox } from './sandbox/sandbox_plugin_loader' ;
2023-01-27 15:08:17 +01:00
import { locateFromCDN , translateForCDN } from './systemjsPlugins/pluginCDN' ;
import { fetchCSS , locateCSS } from './systemjsPlugins/pluginCSS' ;
import { locateWithCache , registerPluginInCache } from './systemjsPlugins/pluginCacheBuster' ;
2017-10-01 20:02:25 +02:00
2019-11-06 05:27:14 -08:00
// Help the 6.4 to 6.5 migration
// The base classes were moved from @grafana/ui to @grafana/data
// This exposes the same classes on both import paths
const grafanaUI = grafanaUIraw as any ;
grafanaUI . PanelPlugin = grafanaData . PanelPlugin ;
grafanaUI . DataSourcePlugin = grafanaData . DataSourcePlugin ;
grafanaUI . AppPlugin = grafanaData . AppPlugin ;
grafanaUI . DataSourceApi = grafanaData . DataSourceApi ;
2023-01-27 15:08:17 +01:00
grafanaRuntime . SystemJS . registry . set ( 'css' , grafanaRuntime . SystemJS . newModule ( { locate : locateCSS , fetch : fetchCSS } ) ) ;
2021-11-10 11:54:58 +01:00
grafanaRuntime . SystemJS . registry . set ( 'plugin-loader' , grafanaRuntime . SystemJS . newModule ( { locate : locateWithCache } ) ) ;
2023-01-27 15:08:17 +01:00
grafanaRuntime . SystemJS . registry . set (
'cdn-loader' ,
grafanaRuntime . SystemJS . newModule ( { locate : locateFromCDN , translate : translateForCDN } )
) ;
2018-05-29 12:01:10 +02:00
2019-07-04 13:32:09 +02:00
grafanaRuntime . SystemJS . config ( {
2017-12-20 12:33:33 +01:00
baseURL : 'public' ,
defaultExtension : 'js' ,
2017-10-01 20:02:25 +02:00
packages : {
2017-12-19 16:06:54 +01:00
plugins : {
2017-12-20 12:33:33 +01:00
defaultExtension : 'js' ,
} ,
2023-01-27 15:08:17 +01:00
'plugin-cdn' : {
defaultExtension : 'js' ,
} ,
2017-10-01 20:02:25 +02:00
} ,
map : {
2017-12-20 12:33:33 +01:00
text : 'vendor/plugin-text/text.js' ,
2017-10-01 20:02:25 +02:00
} ,
2017-10-18 11:15:46 +02:00
meta : {
2018-07-12 13:16:41 +02:00
'/*' : {
2017-11-01 10:45:20 +01:00
esModule : true ,
2017-12-20 12:33:33 +01:00
authorization : true ,
2018-05-29 12:01:10 +02:00
loader : 'plugin-loader' ,
2017-12-20 12:33:33 +01:00
} ,
2023-01-27 15:08:17 +01:00
'*.css' : {
loader : 'css' ,
} ,
2023-06-28 15:58:37 +02:00
[ ` ${ PLUGIN_CDN_URL_KEY } /* ` ] : {
2023-01-27 15:08:17 +01:00
esModule : true ,
authorization : false ,
loader : 'cdn-loader' ,
} ,
2017-12-20 12:33:33 +01:00
} ,
2017-10-01 20:02:25 +02:00
} ) ;
2022-02-16 17:14:33 +01:00
export function exposeToPlugin ( name : string , component : any ) {
2019-07-18 08:03:04 +02:00
grafanaRuntime . SystemJS . registerDynamic ( name , [ ] , true , ( require : any , exports : any , module : { exports : any } ) = > {
2017-10-02 15:30:14 +02:00
module . exports = component ;
} ) ;
2023-06-05 10:51:36 +02:00
// exposes this dependency to sandboxed plugins too.
// the following sandboxPluginDependencies don't depend or interact
// with SystemJS in any way.
sandboxPluginDependencies . set ( name , component ) ;
2017-10-02 15:30:14 +02:00
}
2019-06-18 08:17:27 -07:00
exposeToPlugin ( '@grafana/data' , grafanaData ) ;
2018-12-21 12:27:43 +01:00
exposeToPlugin ( '@grafana/ui' , grafanaUI ) ;
2019-06-18 08:17:27 -07:00
exposeToPlugin ( '@grafana/runtime' , grafanaRuntime ) ;
2017-12-20 12:33:33 +01:00
exposeToPlugin ( 'lodash' , _ ) ;
exposeToPlugin ( 'moment' , moment ) ;
exposeToPlugin ( 'jquery' , jquery ) ;
exposeToPlugin ( 'd3' , d3 ) ;
2019-09-19 16:27:31 +03:00
exposeToPlugin ( 'rxjs' , rxjs ) ;
exposeToPlugin ( 'rxjs/operators' , rxjsOperators ) ;
2023-04-27 07:18:38 +02:00
// Migration - React Router v5 -> v6
// =================================
// Plugins that still use "react-router-dom@v5" don't depend on react-router directly, so they will not use this import.
// (The react-router-dom@v5 that we expose for them depends on the "react-router" package internally from core.)
//
// Plugins that would like update to "react-router-dom@v6" will need to bundle "react-router-dom",
// however they cannot bundle "react-router" - this would mean that we have two instances of "react-router"
// in the app, which would casue issues. As the "react-router-dom-v5-compat" package re-exports everything from "react-router-dom@v6"
// which then re-exports everything from "react-router@v6", we are in the lucky state to be able to expose a compatible v6 version of the router to plugins by
// just exposing "react-router-dom-v5-compat".
//
// (This means that we are exposing two versions of the same package).
exposeToPlugin ( 'react-router' , reactRouterCompat ) ; // react-router-dom@v6, react-router@v6 (included)
exposeToPlugin ( 'react-router-dom' , reactRouterDom ) ; // react-router-dom@v5
2017-10-26 12:07:23 +02:00
2018-06-14 12:32:57 +01:00
// Experimental modules
exposeToPlugin ( 'prismjs' , prismjs ) ;
exposeToPlugin ( 'slate' , slate ) ;
2022-09-06 15:23:48 +01:00
exposeToPlugin ( 'slate-react' , slateReact ) ;
2022-09-12 14:22:01 +01:00
exposeToPlugin ( '@grafana/slate-react' , slateReact ) ; // for backwards compatibility with older plugins
2018-06-14 12:32:57 +01:00
exposeToPlugin ( 'slate-plain-serializer' , slatePlain ) ;
exposeToPlugin ( 'react' , react ) ;
exposeToPlugin ( 'react-dom' , reactDom ) ;
2019-08-14 00:20:23 -07:00
exposeToPlugin ( 'react-redux' , reactRedux ) ;
exposeToPlugin ( 'redux' , redux ) ;
2019-07-05 13:33:42 +02:00
exposeToPlugin ( 'emotion' , emotion ) ;
2021-04-01 14:15:23 +02:00
exposeToPlugin ( '@emotion/css' , emotion ) ;
2022-09-19 16:54:13 +02:00
exposeToPlugin ( '@emotion/react' , emotionReact ) ;
2018-06-14 12:32:57 +01:00
2017-12-20 12:33:33 +01:00
exposeToPlugin ( 'app/features/dashboard/impression_store' , {
2017-11-24 16:18:56 +01:00
impressions : impressionSrv ,
2017-12-20 12:33:33 +01:00
__esModule : true ,
2017-10-20 10:38:52 +02:00
} ) ;
2019-04-12 09:45:22 -07:00
/ * *
* NOTE : this is added temporarily while we explore a long term solution
* If you use this export , only use the :
* get / delete / p o s t / p a t c h / r e q u e s t m e t h o d s
* /
exposeToPlugin ( 'app/core/services/backend_srv' , {
BackendSrv ,
getBackendSrv ,
} ) ;
2021-02-11 13:45:25 +01:00
exposeToPlugin ( 'app/core/utils/datemath' , grafanaData . dateMath ) ;
2017-12-20 12:33:33 +01:00
exposeToPlugin ( 'app/core/utils/flatten' , flatten ) ;
exposeToPlugin ( 'app/core/utils/kbn' , kbn ) ;
exposeToPlugin ( 'app/core/utils/ticks' , ticks ) ;
exposeToPlugin ( 'app/core/config' , config ) ;
exposeToPlugin ( 'app/core/time_series' , TimeSeries ) ;
exposeToPlugin ( 'app/core/time_series2' , TimeSeries ) ;
exposeToPlugin ( 'app/core/table_model' , TableModel ) ;
exposeToPlugin ( 'app/core/app_events' , appEvents ) ;
exposeToPlugin ( 'app/core/core' , {
2017-10-10 15:06:54 +02:00
appEvents : appEvents ,
contextSrv : contextSrv ,
2017-12-20 12:33:33 +01:00
__esModule : true ,
2017-10-10 15:06:54 +02:00
} ) ;
2017-10-01 20:02:25 +02:00
2017-12-20 12:33:33 +01:00
import 'vendor/flot/jquery.flot' ;
import 'vendor/flot/jquery.flot.selection' ;
import 'vendor/flot/jquery.flot.time' ;
import 'vendor/flot/jquery.flot.stack' ;
import 'vendor/flot/jquery.flot.stackpercent' ;
import 'vendor/flot/jquery.flot.fillbelow' ;
import 'vendor/flot/jquery.flot.crosshair' ;
import 'vendor/flot/jquery.flot.dashes' ;
2018-07-18 12:05:45 +02:00
import 'vendor/flot/jquery.flot.gauge' ;
2017-10-01 20:02:25 +02:00
2017-10-12 15:33:37 +02:00
const flotDeps = [
2017-12-20 12:33:33 +01:00
'jquery.flot' ,
'jquery.flot.pie' ,
'jquery.flot.time' ,
'jquery.flot.fillbelow' ,
'jquery.flot.crosshair' ,
'jquery.flot.stack' ,
'jquery.flot.selection' ,
'jquery.flot.stackpercent' ,
'jquery.flot.events' ,
2018-07-18 12:05:45 +02:00
'jquery.flot.gauge' ,
2017-10-12 15:33:37 +02:00
] ;
2018-07-05 13:10:39 -07:00
2018-08-26 17:14:40 +02:00
for ( const flotDep of flotDeps ) {
2017-12-19 16:06:54 +01:00
exposeToPlugin ( flotDep , { fakeDep : 1 } ) ;
2017-10-01 20:02:25 +02:00
}
2023-06-05 10:51:36 +02:00
export async function importPluginModule ( {
path ,
version ,
isAngular ,
pluginId ,
} : {
path : string ;
pluginId : string ;
version? : string ;
isAngular? : boolean ;
} ) : Promise < any > {
2021-11-10 11:54:58 +01:00
if ( version ) {
registerPluginInCache ( { path , version } ) ;
}
2018-08-26 17:14:40 +02:00
const builtIn = builtInPlugins [ path ] ;
2017-10-01 20:02:25 +02:00
if ( builtIn ) {
2019-09-03 09:29:02 +01:00
// for handling dynamic imports
if ( typeof builtIn === 'function' ) {
return await builtIn ( ) ;
} else {
2021-11-10 19:06:55 +01:00
return builtIn ;
2019-09-03 09:29:02 +01:00
}
2017-10-01 20:02:25 +02:00
}
2023-06-05 10:51:36 +02:00
// the sandboxing environment code cannot work in nodejs and requires a real browser
2023-07-05 11:16:56 +02:00
if ( isFrontendSandboxSupported ( { isAngular , pluginId } ) ) {
2023-06-05 10:51:36 +02:00
return importPluginModuleInSandbox ( { pluginId } ) ;
}
2019-07-04 13:32:09 +02:00
return grafanaRuntime . SystemJS . import ( path ) ;
2017-10-01 20:02:25 +02:00
}
2023-07-05 11:16:56 +02:00
function isFrontendSandboxSupported ( { isAngular , pluginId } : { isAngular? : boolean ; pluginId : string } ) : boolean {
2023-06-21 14:49:22 +02:00
// To fast test and debug the sandbox in the browser.
const sandboxQueryParam = location . search . includes ( 'nosandbox' ) && config . buildInfo . env === 'development' ;
2023-07-05 11:16:56 +02:00
const isPluginExcepted = config . disableFrontendSandboxForPlugins . includes ( pluginId ) ;
2023-06-21 14:49:22 +02:00
return (
! isAngular &&
Boolean ( config . featureToggles . pluginsFrontendSandbox ) &&
process . env . NODE_ENV !== 'test' &&
2023-07-05 11:16:56 +02:00
! isPluginExcepted &&
2023-06-21 14:49:22 +02:00
! sandboxQueryParam
) ;
2023-06-05 10:51:36 +02:00
}
2021-02-11 13:45:25 +01:00
export function importDataSourcePlugin ( meta : grafanaData.DataSourcePluginMeta ) : Promise < GenericDataSourcePlugin > {
2023-06-05 10:51:36 +02:00
return importPluginModule ( {
path : meta.module ,
version : meta.info?.version ,
isAngular : meta.angularDetected ,
pluginId : meta.id ,
} ) . then ( ( pluginExports ) = > {
2019-04-04 18:30:15 +02:00
if ( pluginExports . plugin ) {
2019-11-15 09:18:09 +00:00
const dsPlugin = pluginExports . plugin as GenericDataSourcePlugin ;
2019-04-30 22:36:46 -07:00
dsPlugin . meta = meta ;
return dsPlugin ;
2019-04-04 18:30:15 +02:00
}
if ( pluginExports . Datasource ) {
2021-02-11 13:45:25 +01:00
const dsPlugin = new grafanaData . DataSourcePlugin <
grafanaData . DataSourceApi < grafanaData.DataQuery , grafanaData.DataSourceJsonData > ,
grafanaData . DataQuery ,
grafanaData . DataSourceJsonData
2019-11-15 09:18:09 +00:00
> ( pluginExports . Datasource ) ;
2019-04-04 18:30:15 +02:00
dsPlugin . setComponentsFromLegacyExports ( pluginExports ) ;
2019-04-30 22:36:46 -07:00
dsPlugin . meta = meta ;
2019-04-04 18:30:15 +02:00
return dsPlugin ;
}
throw new Error ( 'Plugin module is missing DataSourcePlugin or Datasource constructor export' ) ;
} ) ;
}
2021-02-11 13:45:25 +01:00
export function importAppPlugin ( meta : grafanaData.PluginMeta ) : Promise < grafanaData.AppPlugin > {
2023-06-05 10:51:36 +02:00
return importPluginModule ( {
path : meta.module ,
version : meta.info?.version ,
isAngular : meta.angularDetected ,
pluginId : meta.id ,
} ) . then ( ( pluginExports ) = > {
2021-02-11 13:45:25 +01:00
const plugin = pluginExports . plugin ? ( pluginExports . plugin as grafanaData . AppPlugin ) : new grafanaData . AppPlugin ( ) ;
2019-05-20 13:05:23 -07:00
plugin . init ( meta ) ;
plugin . meta = meta ;
2019-06-15 08:26:31 +02:00
plugin . setComponentsFromLegacyExports ( pluginExports ) ;
2019-04-30 22:36:46 -07:00
return plugin ;
2019-04-04 18:30:15 +02:00
} ) ;
}