studio icon indicating copy to clipboard operation
studio copied to clipboard

Extensions should be able to load static resources

Open rgov opened this issue 3 years ago • 3 comments

As far as I can tell, there is no recommended way to bundle a static resource with an extension and reference it at runtime.

I can configure Webpack thusly to enable Asset Modules for things like images.

module.exports = {
    webpack: (config) => {
        config.module.rules.push({
            test: /\.(png|svg|jpg|jpeg|gif)$/i,
            type: "asset/inline",
        });
        return config;
    },
};

The use of asset/inline here loads the whole image as a Base64-encoded data: URL that gets embedded into the DOM when I write,

import MyImage from "./images/foobar.png";
...
<img src={MyImage} />

But I don't think it's possible to use asset/resource which instead inserts the path of the file, like <img src="./images/foobar.png">, because the base URI to the extension is not exposed at runtime.

Is it maybe possible to have the extension loader prepend something like let __webpack_public_path__ = "..."; to the extension source code before executing it?

rgov avatar May 18 '22 22:05 rgov

I spent an obscene amount of time toying with this and the solution described above does kind of work. See here: https://github.com/foxglove/studio/compare/main...rgov:rzg/extension-path

In my extension's Webpack config.ts I specify the asset/resource rule, and then before I import anything in my main file, I use

declare var __webpack_public_path__: string; 
__webpack_public_path__ = (window as any).foobar;

(Why window.foobar? I had to figure out some way to smuggle the path into the scope that overrides __webpack_public_path__. I'm sure there is a more appropriate way to do this. Keep in in mind also that Webpack minifies the variable name so __webpack_public_path__ becomes something like __webpack_require__.p.)

rgov avatar May 19 '22 06:05 rgov

A simpler way to do it, once the extension directory is plumbed through, is to set config.output.publicPath = "@FOXGLOVE_EXTENSION_PATH@" and have the ExtensionRegistryProvider use

const unwrappedExtensionSource = (await extensionLoader.loadExtension(extension.id))
          .replace("@FOXGLOVE_EXTENSION_PATH@", `file://${extension.directory}/dist/`);

Except that this doesn't quite play well with the Content-Security-Policy; it seems to work fine when the app is built standalone but when running it in development mode, it doesn't want to load local resources. Perhaps you could implement a URL scheme like x-foxglove-extension://<extension.id>/resource.png instead.

rgov avatar May 19 '22 21:05 rgov

From live discussion:

Problem statement

  • monaco is hard to import in webpack (or at least we have no example of this working). here is how we do it in studio.
  • importing theora ( https://github.com/brion/ogv.js ) with wasm binary is not working because it appears to expect the wasm blob to be available on a URL.

I created tickets to attempt examples for each of these

  • https://github.com/foxglove/create-foxglove-extension/issues/68
  • https://github.com/foxglove/create-foxglove-extension/issues/69

amacneil avatar Aug 05 '22 22:08 amacneil