grafana / plugin-tools

Create Grafana plugins with ease.
https://grafana.com/developers/plugin-tools/
Apache License 2.0
57 stars 30 forks source link

Create Plugin: Dynamic webpack publicPath #966

Closed jackw closed 3 months ago

jackw commented 3 months ago

What this PR does / why we need it: Historically the plugins publicPath has been hardcoded to public/plugins/<plugin_id>. This came with the assumption that we always want to load the plugin from that location however these days that isn't always true. Being able to build a plugin once and host it from anywhere makes it much easier for us to move plugins around in different environments without fear of causing loading errors.

To solve this we can take advantage of __webpack_public_path__ and amd magic modules so at runtime Grafana (which knows where the plugin is loading from courtesy of the backend) can pass that information into the plugin via module.uri.

To support backwards compatibility we additionally check that module exists and has a uri property otherwise we fallback to the original hardcoded value of public/plugins/<plugin_id>.

To do this without expecting plugin developers to make changes to source code I've opted to create the publicPath file in the webpack config directory and then use imports-loader to add it to the top of the imports list in module.tsx?. Due to hoisting this will make sure the webpack publicPath property is set before any other code executes.

image

Which issue(s) this PR fixes:

Related grafana/grafana-community-team/issues/133

Special notes for your reviewer:

📦 Published PR as canary version: Canary Versions
:sparkles: Test out this PR locally via: ```bash npm install @grafana/create-plugin@4.15.0-canary.966.a493180.0 # or yarn add @grafana/create-plugin@4.15.0-canary.966.a493180.0 ```
github-actions[bot] commented 3 months ago

Hello! 👋 This repository uses Auto for releasing packages using PR labels.

✨ This PR can be merged and will trigger a new minor release. NOTE: When merging a PR with the release label please avoid merging another PR. For further information see here.

jackw commented 3 months ago

This is neat! Is the backend code that sets module.uri already in place? If so, would you mind sharing a link to the relevant code?

Sorry I should have been a bit more clear on how all this comes together...

graph TD
    subgraph Backend
        A[Set module in factory.go] -->|Pass to frontend| B[frontendSettings/bootData]
    end

    subgraph Frontend
        B --> C{Is Sandbox feature flag enabled?}
        C -->|No| D["System.import(plugin.module)"]
        D --> E[Resolve module.uri via SystemJS AMD extra]

        C -->|Yes| F[Sandbox AMD loader]
        F --> G[Resolve module.uri via Sandbox AMD define]
    end

So to take full advantage of this dynamic path setting we need to get that grafana/grafana PR merged for the sandbox. But we're using the ternary so if the plugin is loaded in a version of Grafana where module or module.uri is not available the publicPath will fall back to the original value. Given the e2e tests in this repo run against a scaffolded plugin using G10.3 I'm pretty confident this is working as expected.

sunker commented 3 months ago

Sorry I should have been a bit more clear on how all this comes together...

  • The backend sets module here which is passed to the frontend via frontendSettings/bootData. The frontend uses this like System.import(plugin.module).
  • This SystemJS PR introduced support for setting module.uri in its AMD extra. Here it's using the _context.meta.url which is resolved from plugin.module by systemjs and then setting it as module.uri. This was released in Systemjs@6.15.1 and bumped in this PR. This is already available in core and will be released in G11.1
  • This Grafana PR introduces the "magic modules" to the fe sandbox loader so we will have compatibility there as well.
graph TD
    subgraph Backend
        A[Set module in factory.go] -->|Pass to frontend| B[frontendSettings/bootData]
    end

    subgraph Frontend
        B --> C{Is Sandbox feature flag enabled?}
        C -->|No| D["System.import(plugin.module)"]
        D --> E[Resolve module.uri via SystemJS AMD extra]

        C -->|Yes| F[Sandbox AMD loader]
        F --> G[Resolve module.uri via Sandbox AMD define]
    end

So to take full advantage of this dynamic path setting we need to get that grafana/grafana PR merged for the sandbox. But we're using the ternary so if the plugin is loaded in a version of Grafana where module or module.uri is not available the publicPath will fall back to the original value. Given the e2e tests in this repo run against a scaffolded plugin using G10.3 I'm pretty confident this is working as expected.

Thanks for detailed explanation @jackw. Great work on this!

grafana-plugins-platform-bot[bot] commented 3 months ago

:rocket: PR was released in @grafana/create-plugin@4.15.0 :rocket: