2020-11-17 20:28:32 +02:00
import { PluginStates } from '../reducer' ;
2023-11-03 21:45:21 +02:00
import { ContentScriptType , ContentScriptContext , PostMessageHandler , ContentScriptModule } from '../api/types' ;
2024-01-18 13:20:10 +02:00
import { dirname } from '@joplin/utils/path' ;
2020-12-20 09:52:28 +02:00
import shim from '../../../shim' ;
2023-07-27 17:05:56 +02:00
import Logger from '@joplin/utils/Logger' ;
2021-01-12 01:33:10 +02:00
import PluginService from '../PluginService' ;
2021-01-03 15:21:48 +02:00
const logger = Logger . create ( 'loadContentScripts' ) ;
2020-11-17 20:28:32 +02:00
export interface ExtraContentScript {
id : string ;
module : any ;
assetPath : string ;
2023-11-03 21:45:21 +02:00
pluginId : string ;
2020-11-17 20:28:32 +02:00
}
2021-01-12 01:33:10 +02:00
function postMessageHandler ( pluginId : string , scriptType : ContentScriptType , contentScriptId : string ) : PostMessageHandler {
return ( message : any ) = > {
if ( scriptType === ContentScriptType . MarkdownItPlugin ) {
logger . error ( 'context.postMessage is not available to renderer content scripts' ) ;
} else {
const plugin = PluginService . instance ( ) . pluginById ( pluginId ) ;
return plugin . emitContentScriptMessage ( contentScriptId , message ) ;
}
} ;
}
2020-11-17 20:28:32 +02:00
export function contentScriptsToRendererRules ( plugins : PluginStates ) : ExtraContentScript [ ] {
return loadContentScripts ( plugins , ContentScriptType . MarkdownItPlugin ) ;
}
export function contentScriptsToCodeMirrorPlugin ( plugins : PluginStates ) : ExtraContentScript [ ] {
return loadContentScripts ( plugins , ContentScriptType . CodeMirrorPlugin ) ;
}
function loadContentScripts ( plugins : PluginStates , scriptType : ContentScriptType ) : ExtraContentScript [ ] {
2020-12-20 09:52:28 +02:00
if ( ! plugins ) return null ;
2020-11-17 20:28:32 +02:00
const output : ExtraContentScript [ ] = [ ] ;
for ( const pluginId in plugins ) {
const plugin = plugins [ pluginId ] ;
const contentScripts = plugin . contentScripts [ scriptType ] ;
if ( ! contentScripts ) continue ;
for ( const contentScript of contentScripts ) {
2021-01-03 15:21:48 +02:00
try {
const module = shim . requireDynamic ( contentScript . path ) ;
if ( ! module .default || typeof module .default !== 'function' ) throw new Error ( ` Content script must export a function under the "default" key: Plugin: ${ pluginId } : Script: ${ contentScript . id } ` ) ;
2021-01-12 01:33:10 +02:00
const context : ContentScriptContext = {
pluginId ,
contentScriptId : contentScript.id ,
2021-01-12 04:27:56 +02:00
postMessage : postMessageHandler ( pluginId , scriptType , contentScript . id ) ,
2021-01-12 01:33:10 +02:00
} ;
2023-11-03 21:45:21 +02:00
const loadedModule = module .default ( context ) as ContentScriptModule ;
2021-01-12 01:33:10 +02:00
2023-11-03 21:45:21 +02:00
if ( ! loadedModule . plugin && ! ( loadedModule as any ) . codeMirrorResources && ! ( loadedModule as any ) . codeMirrorOptions ) throw new Error ( ` Content script must export a "plugin" key or a list of CodeMirror assets or define a CodeMirror option: Plugin: ${ pluginId } : Script: ${ contentScript . id } ` ) ;
2021-01-03 15:21:48 +02:00
output . push ( {
id : contentScript.id ,
module : loadedModule ,
assetPath : dirname ( contentScript . path ) ,
2023-11-03 21:45:21 +02:00
pluginId ,
2021-01-03 15:21:48 +02:00
} ) ;
} catch ( error ) {
// This function must not throw as doing so would crash the
// application, which we want to avoid for plugins. Instead log
// the error, and continue loading the other content scripts.
logger . error ( error . message ) ;
}
2020-11-17 20:28:32 +02:00
}
}
return output ;
}