I have a module that needs to execute some JS on the startup of every perspective page, because its pulling in some additional CSS that isn't located in the themes
directory.
I managed to copy some strategies in @bmusson's embr modules
Where essentially on startup of the module, I iterate through all of the components in the component registry, add my resource as a dependency, and then when I shut down I remove that reference as well. This forces the page to always load my content, without my component having to be on the page (Because I dont have one!)
I recognize this is a bit hacky, and side note, it would be ideal for module developers to have a way to load in their own browser resources into Perspective overall...
Regardless, I noticed that when I do this, it only actually adds the resource IF I add a component as well. Seems that maybe this is triggering some type of "refresh" on the actual registry that causes it to show up? If I dont, my resource wont show up in the client until after a successful project scan happens when a view is modified, or if I manually restart the Perspective module.
I really hate this solution of making a fake component just for the sake of triggering this "refresh". Is there any less hacky ways to do what I am trying to do here?
public static final Set<BrowserResource> BROWSER_RESOURCES = Set.of(
new BrowserResource(
"example-bootstrap",
"/res/browser-resource-example/bootstrap.js",
BrowserResource.ResourceType.JS));
@Override
public void startup(LicenseState activationState) {
PerspectiveContext perspectiveContext = PerspectiveContext.get(context);
this.componentRegistry = perspectiveContext.getComponentRegistry();
this.modifiedComponents = new ArrayList<>();
ComponentRegistryUtils.addResourcesTo(this.componentRegistry, BROWSER_RESOURCES,
component -> {
boolean matches = component.moduleId().equals(PerspectiveModule.MODULE_ID);
if (matches) {
modifiedComponents.add(component);
}
return matches;
});
// This is cursed....
ComponentDescriptor fakeComponent = ComponentDescriptorImpl.ComponentBuilder.newBuilder()
.setId("resource.fake.component")
.setResources(BrowserResourceGatewayHook.BROWSER_RESOURCES)
.build();
this.componentRegistry.registerComponent(fakeComponent);
this.componentRegistry.removeComponent(fakeComponent.id());
}
Here is a link to the repo with this example in it: An Example module for showing loading a browser resource in perspective without components
EDIT: I realized I should have also thrown in what addResourcesTo
is really doing, here is what it ends up calling for each descriptor and resource
private static void addBrowserResource(ComponentDescriptor descriptor, BrowserResource resource) {
try {
Set<BrowserResource> browserResources = new HashSet<>(descriptor.browserResources());
browserResources.add(resource);
Field field = ComponentDescriptorImpl.class.getDeclaredField("browserResources");
field.setAccessible(true);
field.set(descriptor, browserResources);
} catch (NoSuchFieldException | IllegalAccessException e) {
logger.error("Failed to add browser resource {} to descriptor {}", resource.name, descriptor.id(), e);
throw new RuntimeException("Failed to add browser resource", e);
}
}