How to require external libraries in a component helper file?

AI-generated summary

Nacho_Caballero is seeking a better approach to require external libraries in a Discourse theme component helper file. They are currently loading the libraries in the initializer and passing them to the helper function.

Merefield suggests using the approach in their discourse-tc-tag-cloud plugin, where libraries are registered in the about.json file and loaded from the assets folder.

Nacho_Caballero tries this approach but encounters issues, including a Content Security Policy (CSP) error. Merefield suggests that the uploads directory URL should be added to the CSP src setting in the settings.yml file or in the Discourse settings.

Nacho_Caballero finds that adding the uploads directory URL to the CSP src setting in the Discourse settings resolves the issue, but notes that setting it in settings.yml does not work with localhost.

I’m writing a theme component and I need to access two external libraries from a helper file. My current approach works, but it feels a bit clunky:

In my initializer:

import { apiInitializer } from 'discourse/lib/api';
import loadScript from 'discourse/lib/load-script';
import myHelper from '../lib/helper';

export default apiInitializer('0.11.1', (api) => {
  api.decorateCookedElement(
    (elem) => {
      loadScript(D3_URL).then(() => {
        loadScript(LUXON_URL).then(() => {
          ...
          myHelper(whatever, d3, luxon)
          ...
        })
      })
    },
    {id: 'discourse-xyz', onlyStream: true}
  )
});

Then in myHelper.js:

export default function myHelper(whatever, d3, luxon) {
  d3();
  luxon();
  ...
}

I tried requiring the libraries from the helper file directly, but that means I’d have to return a promise and then do myHelper(whatever, d3, luxon).then(...) in the initializer, which I’d rather not do for other reasons.

Is there a better approach?

You can use the approach I use in GitHub - merefield/discourse-tc-tag-cloud: A Discourse Theme Component that displays a tag cloud above the tag lists on the tags page · GitHub

Note the assets are registered here:

And sit in the assets folder.

Thanks, Robert. I gave it a try but it’s not finding “theme_uploads”. Is this something I should set up in settings.yml?

No, you shouldn’t need to do that.

I note the syntax is still in the spec: discourse/spec/models/theme_spec.rb at c8f4f7c235ca38b39d4b66bf9e1305410e36815e · discourse/discourse · GitHub

Ah, my bad. I had the wrong filename in assets. I’ve fixed it, but now I’m getting a CSP error:

load-script.js:35 Refused to load the script 
'http://localhost:4200/uploads/default/original/1X/c4a31754250cf6a40f7cbed182cfe2456d9be9fe.js' because it violates the following Content Security Policy directive: 
"script-src http://localhost:4200/logs/ http://localhost:4200/sidekiq/ http://localhost:4200/mini-profiler-resources/ http://localhost:4200/assets/ http://localhost:4200/brotli_asset/ http://localhost:4200/extra-locales/ http://localhost:4200/highlight-js/ http://localhost:4200/javascripts/ http://localhost:4200/plugins/ http://localhost:4200/theme-javascripts/ http://localhost:4200/svg-sprite/ 
'unsafe-eval' http://localhost:4200/ember-cli-live-reload.js http://localhost:4200/_lr/ /uploads". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

I didn’t see you use an extend_content_security_policy in settings.yml, so I don’t know what I’m missing.

I’ve got the uploads directory url added to my CSP src setting in any case. But I might add that which would make it more targeted :+1:t2:

Oh, you mean here? It works if I do this:

I thought there was a way to do it in settings.yml, like this (but it doesn’t seem to work with localhost):

extend_content_security_policy:
  type: list
  default: "script_src: http://localhost:4200/"