Next week I’m going to merge this PR that allows themes and components to have QUnit tests, but it also changes how themes JavaScript is processed/transpiled by Discourse. Making those changes in a backward-compatible manner is very difficult without reworking lots of code in core (which may very well introduce other backward-incompatible changes), so the changes may break the JavaScript of your themes/components when you upgrade your site.
In this post I’m going to explain what the changes are that may affect your themes/components and what you need to do to fix them.
1. JavaScript inside <script type="text/discourse-plugin">
tags will be invoked with strict mode enabled
This change doesn’t affect JS code that’s not inside <script type="text/discourse-plugin">
tags. You’re completely safe from this change if your code is in regular <script>
tags or in standalone .js
files.
The easiest way to tell if your theme/component is affected by this change is to wrap your JS code inside an Immediately-Invoked Function Expression (IIFE) and stick a "use strict";
at the top of your code. For example, let’s say this is how your theme code looks like right now:
<script type="text/discourse-plugin" version="0.8.11">
a = 5;
console.log(a);
</script>
After you wrap it in an IIFE it should look like this ("use strict";
is important because it enables strict mode and we want to test how our code behaves in strict mode):
<script type="text/discourse-plugin" version="0.8.11">
(function() {
"use strict";
a = 5;
console.log(a);
})();
</script>
If your component stops working after you do this, then it’s going to break when you upgrade your site. To fix your code, I highly recommend that you first read MDN documentation for JavaScript Strict mode and then see if your theme/component does anything prohibited under strict mode. If it does, then you need to refactor your code so it doesn’t do anything prohibited.
The most likely error that you’ll see is ReferenceError
when declaring a variable without the var
(or let
/const
) keyword. In the example above, the line a = 5;
will throw a ReferenceError
exception under strict mode because we forgot to add var
. The code looks like this after we fix it:
<script type="text/discourse-plugin" version="0.8.11">
(function() {
"use strict";
var a = 5;
console.log(a);
})();
</script>
Once you’re done testing/fixing your code, feel free to remove the IIFE and the "use strict";
line.
2. Themes JavaScript modules paths will be prefixed with theme IDs.
A while back we added a new feature that allowed themes JavaScript to be split into multiple files, and I need to give a little bit of context on how this feature to explain the upcoming change.
When a theme/component that ships with standalone JavaScript files is installed on a Discourse instance, Discourse loops through all the JavaScript files and creates a JavaScript module for each one. Each module needs to have a unique identifier (a.k.a path), so Discourse uses the file path (with minor changes) as the module’s path.
For example, if your theme/component has a file at javascripts/discourse/helpers/my-helper.js
, Discourse will create a module for that file and assign discourse/helpers/my-helper
as its path, and the module will contain a transpiled version of the JavaScript inside the original file.
The nice thing about modules is that you can import classes/functions/objects etc. from one module into another. For example, you could import a function called xyz
from my-helper
into other modules by using an import
statement like so:
// javascripts/discourse/controllers/my-theme-controller.js
import { xyz } from "discourse/helpers/my-helper";
The PR that I’m going to merge next week will add a prefix to themes modules paths. So in our example my-helper
will change from discourse/helpers/my-helper
to discourse/theme-<theme_id>/helpers/my-helper
. This means our import
statement will stop working because the module path will have changed. To fix it we need to simply change the path in the import
statement from absolute to relative like so:
// javascripts/discourse/controllers/my-theme-controller.js
import { xyz } from "../helpers/my-helper";
And now our import
statement should work again. See these PRs 1 and 2 for real examples of components that are affected by (2) and how they’re fixed.
Again, this only affects your theme/component if it imports from one of its own modules; importing core modules is not affected by this change.
Hope this helps. If you have any questions, please let me know!