Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Plugin: Dynamic webpack publicPath #966

Merged
merged 5 commits into from
Jul 4, 2024
Merged

Conversation

jackw
Copy link
Collaborator

@jackw jackw commented Jun 18, 2024

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

✨ Test out this PR locally via:

npm install @grafana/[email protected]
# or 
yarn add @grafana/[email protected]

@jackw jackw added create-plugin related to the create-plugin tool minor Increment the minor version when merged labels Jun 18, 2024
@jackw jackw self-assigned this Jun 18, 2024
@jackw jackw added the release Create a release when this pr is merged label Jun 18, 2024
Copy link

github-actions bot commented Jun 18, 2024

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 jackw changed the title [WIP] Create Plugin: Dynamic publicPath [WIP] Create Plugin: Dynamic webpack publicPath Jun 18, 2024
@jackw jackw changed the title [WIP] Create Plugin: Dynamic webpack publicPath Create Plugin: Dynamic webpack publicPath Jun 19, 2024
@jackw jackw marked this pull request as ready for review June 19, 2024 15:16
@jackw jackw requested a review from a team as a code owner June 19, 2024 15:16
@jackw jackw requested review from oshirohugo, mckn, academo and sunker and removed request for a team June 19, 2024 15:16
Copy link
Collaborator

@mckn mckn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Contributor

@sunker sunker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

@jackw
Copy link
Collaborator Author

jackw commented Jun 20, 2024

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...

  • 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 [email protected] 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
Loading

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.

Copy link
Member

@academo academo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this with a dynamic chunk using react lazy-suspense, an image, and svg and a font file (global css font inside plugin files) with frontend sandbox off. it works as expected.

With the sandbox on it was a different situation I got the following error

Error: [sandbox] Could not resolve dependency module
    at resolvePluginDependencies (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:279121:21)
    at define (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:279073:34)
    at http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:47619:18
    at BoundaryProxyHandler.applyOrConstructTrapForTwoOrMoreArgs [as applyTrapForTwoOrMoreArgs] (eval at createRedConnector (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:48381:10), <anonymous>:774:32)
    at BoundaryProxyHandler.applyOrConstructTrapForTwoOrMoreArgs (eval at createRedConnector (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:48381:10), <anonymous>:822:175)
    at eval (eval at <anonymous> (eval at createRedConnector (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:48381:10)), <anonymous>:9:1)
    at eval (<anonymous>)
    at eval (eval at createRedConnector (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:48381:10), <anonymous>:2490:18)
    at VirtualEnvironment.redCallableEvaluate (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:48547:135)
    at VirtualEnvironment.evaluate (http://localhost:3000/public/build/app.c7db8e66365e9f6062b5.js:48576:48)

Copy link
Member

@academo academo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Further tests show that the changes in grafana/grafana#89274 fix this problem.,

Considering the sandbox is not used in any on-prem instance we won't have problems with people updating plugins using this new approach in old grafana instances and getting a broken plugin.

Copy link
Member

@academo academo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

nit: please change the variable names of module and export to avoid confusions,.

@sunker
Copy link
Contributor

sunker commented Jun 20, 2024

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 [email protected] 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
Loading

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!

@jackw jackw force-pushed the jackw/dynamic-public-path branch from 44ebd48 to f65b479 Compare July 1, 2024 15:14
@jackw jackw merged commit c73b8ec into main Jul 4, 2024
13 checks passed
@jackw jackw deleted the jackw/dynamic-public-path branch July 4, 2024 12:10
@grafana-plugins-platform-bot
Copy link

🚀 PR was released in @grafana/[email protected] 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
create-plugin related to the create-plugin tool minor Increment the minor version when merged release Create a release when this pr is merged released This issue/pull request has been released.
Projects
Development

Successfully merging this pull request may close these issues.

4 participants