forked from gitbucket/gitbucket-explorer-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
Plugin asset extension point
aadrian edited this page Sep 25, 2026
·
1 revision
-
javaScripts(explorer plugin #66): matches a regex againstgetRequestURI, which includes the context path. The pattern cannot distinguish repository pages from otherx/yURLs (/root/signinvs./signin,/gist/:user/:id,/:user/_edit, ...). One slot, at the end of<body>; the plugin writes the<link>/<script>markup and the URLs itself. -
repositoryHeaders(explorer plugin #73): rendered frommenu.scala.html, so only on pages with the repository sidebar. The plugin still writes the markup and URLs; the CSS ends up in the body, the script mid-page. Repository pages only.
The plugin declares which assets it needs, per page. GitBucket, not the plugin, produces the tags:
- builds the URL (
context.path/baseUrl+/plugin-assets/+ path), optionally with a version or digest, - puts CSS in
<head>(aftergitbucket.css) and JS before</body>, withdefer, - de-duplicates an asset requested more than once.
Bundling, minification and hashing stay in the plugin's own build (npm/Vite/webpack). Only the inclusion is handled by GitBucket; resolving a hashed file name through a build manifest could be added to the URL step later.
sealed trait AssetKind
object AssetKind { case object Css extends AssetKind; case object Js extends AssetKind }
// path is relative to /plugin-assets/, e.g. "explorer/bundle.js"
case class PluginAsset(path: String, kind: AssetKind, defer: Boolean = true)
// Plugin
def pageAssets(registry: PluginRegistry, context: ServletContext, settings: SystemSettings)
: Seq[(Option[RepositoryInfo], Context) => Seq[PluginAsset]] = Nil| File | Change |
|---|---|
plugin/Plugin.scala |
val + def pair, registered in initialize
|
plugin/PluginRegistry.scala |
queue, addPageAssets, getPageAssets
|
main.scala.html |
CSS loop in <head> after gitbucket.css; JS loop before </body>
|
-
mainalready takesrepository: Option[RepositoryInfo]. Of the templates that render the repository sidebar, 33 passSome(repository)tohtml.mainand 5 do not (issues/labels/list,issues/milestones/{edit,list,milestone},issues/priorities/list). - Additive, empty default. A plugin using it requires a GitBucket version that has it; older versions ignore it.
asset-pipeline separates build-time processing (bundling, minification, digests) from render-time tags.
- A Grails plugin ships assets under
grails-app/assets/, in the same URL space as the application's; they are packaged into the plugin jar and found on the classpath (META-INF/assets). An application file with the same path overrides the plugin's (organization). - The application includes assets by logical name (
//= requirein a manifest, or<asset:stylesheet>/<asset:javascript>in a view or layout). Inclusion is decided by the application, not by the plugin. -
<asset:script>blocks andasset-defer="true"are collected while rendering and emitted at<asset:deferredScripts/>(taglibs). - Difference: in GitBucket the plugin adds its own assets to pages, so the declaration is on the plugin side and the layout only places it.
Independent one-liner: getJavaScript(context.currentPath) instead of getRequestURI removes the context path
from all existing regexes.
Open: page kind beyond Option[RepositoryInfo]; ordering between plugins; deprecating javaScripts.