Loading js libraries via register_asset

I noticed that the js files added using register_asset via plugins are passed as a string to eval function.

The problem with eval is, it doesn’t add the object to the global scope as discussed here. https://stackoverflow.com/questions/4670805/javascript-eval-on-global-scope which is essential for using a js library.

To be specific, I was trying to use https://github.com/muaz-khan/RecordRTC/blob/master/RecordRTC.js in a plugin but the functions aren’t accessible in the ember files.

The workaround for this is to process the file with webpack, using libraryTarget as umd.

Many of the libraries are processed this way which seems to work out of the box.

Now I’m not an expert on these things, but just wondering whether it makes sense for discourse to add the registered assets to the global scope.

Note: Apologies if some of this sounds silly. I didn’t expect to fiddle with webpack to get js libraries working with discourse which motivated me to post this writeup. :wink:

1 Like

Something seems off here - I looked at our lazyYT plugin that includes an asset using register_asset and it was not in an eval block.

Could you provide a small example plugin that reproduces the problem?

2 Likes

I wonder if this is an unintended side-effect of

(i.e. this only happens in development)

1 Like

I wondered that too - if that’s the case there are workarounds but I’d like a reproducible example.

2 Likes

Sure.

Interesting. I can run the plugin in production to check if that’s the case.

@eviltrout
A small plugin using the same library mentioned in the OP GitHub - fzngagan/js-test

I simply tried to call the function added by the library in the initializer and in the js console I get ReferenceError: RecordRTC is not defined

I’ve used the unminified version of the library which might be helpful for debugging.

Also, a screenshot from the sources tab to show that the whole thing was passed to the eval function.

Another finding I have is that the included js files in both the discourse-spoiler-alert, and lazy-yt inject functions into the global jQuery object but do not declare any top level functions.

I looked at your plugin and I can confirm the eval is only present in development mode. In production it will be inserted in the global scope. I understand that’s not ideal but I also don’t think libraries should normally be distributed without some kind of support for modules (for example, UMD.)

My suggestion would be to repackage the JS with some kind of UMD support and then import it but that is somewhat tricky to set up if you don’t have a lot of JS experience with modules.

Instead, an easy way to get this to work is to edit RecordRTC.js and add the following line:

window.RecordRTC = RecordRTC;

It would be even better to surround the entire contents in an IIFE:

(function(exports) {
  // original file contents go here
  exports.RecordRTC = RecordRTC;
})(window);
5 Likes

Yes, I agree. Many of them are distributed that way and those work seamlessly. Thanks for looking into this and for the confirmation :+1: I went the route of packaging the library via webpack and it works without even importing. I should have tested in production mode though.

1 Like