Skip to content

Commit

Permalink
RFC-160 Use ES6 Modules in govuk_publishing_components
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickpatrickpatrick committed May 30, 2023
1 parent 40d844a commit 96c2f33
Showing 1 changed file with 123 additions and 0 deletions.
123 changes: 123 additions & 0 deletions rfc-160-using-es6-modules-in-govuk_publishing_components.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# Use ES6 Modules for components in govuk_publishing_components

## Summary

govuk_publishing_components should make the switch over to ES6 and convert the existing Javascript for components into ES6 modules. This would not only mean govuk_publishing_components could use contemporary Javascript (now supported for several years by the most popular browsers) but it could also serve individal Javascript files per component. The existing work on AssetHelper could be built upon to add this support. This would exclude the Javascript used for analytics as it is desirable that this should still work on all browsers that are used to visit GOV.UK.

This RFC builds upon ideas from:
- [RFC #149: Serve component CSS and Javascript as individual files](https://github.com/alphagov/govuk-rfcs/blob/main/rfc-149-switch-to-per-page-asset-loading.md)

## Problem

To support Internet Explorer 11 (IE11), in govuk_publishing_components all the Javascript is written in ES5. This is because IE11 does not support ES6. However lately in the GOV.UK Frontend Community there have been calls to drop JS support for IE11. As of June 2022, [Microsoft no longer supports IE11](https://learn.microsoft.com/en-us/lifecycle/products/internet-explorer-11). From 24th of April to the 21st of May, the number of users that accessed GOV.UK using IE11 was 26,205. This is 0.067% of all users (38,821,598) who accessed GOV.UK during that time.

The next release of `govuk-frontend` (which is used by components in govuk_publishing_components) is going to drop support for ES5 and only use modules going forward. So in order to recieve further updates from govuk-frontend, the components need to start supporting ES6 modules.

In each application on GOV.UK, Javascript is all imported in a single application.js file. This includes all the Javascript for every component used in an application. This application.js file is then loaded no matter page the user visits, even though a single page typically uses a subset of all the components in an application. [RFC #149](https://github.com/alphagov/govuk-rfcs/blob/main/rfc-149-switch-to-per-page-asset-loading.md) proposed serving assets as individual files and while we have implemented serving CSS seperately we are yet to implement serving JS seperately.

## Proposal

We should make the change in govuk_publishing_components from using ES5 Javascript to ES6 Javascript. This will involve:

### 1. Converting the javascript of each component to ES6

Basically in govuk_publishing_components we use a 'module'-like implementation in which the Javascript of each component is self-contained in a function. This function is then assigned to the GOVUK window object and then the components are initialised using an "initAll" method which iterates through the components and runs each initialisation method.

As this 'module'-like pattern has already been implemented, the actual conversion to ES6 is very straight forward. It would consist of wrapping the existing code in a `Class` and removing the `prototype` syntax.

```
(function (Modules) {
function ModuleName ($module) {
this.$module = $module;
}
ModuleName.prototype.utilityFunction = function () {}
ModuleName.prototype.init = function () {}
})(window.GOVUK.Modules)
```
to

```
Class ModuleName {
constructor($module) {
this.$module = $module;
}
utilityFunction() {}
init() {}
}
export default ModuleName;
```

Sprockets doesn't do any transpiling. This means we don't need to change anything in the build process. We already use Uglify to compress our Javascript so we don't need to switch to alternative.

### 2. Modifying AssetHelper to append Javascript tags to components

AssetHelper already allows the individual loading of stylesheets for components. It does this by generating a list of paths of stylesheets which are then appended to `manifest.js` for precompiling. In addition it renders a block containing all the stylesheet import tags.

We would need to add a new set of functions that would act the same as the stylesheet functions (`add_gem_component_stylesheet`, `add_app_component_stylesheet`, `add_view_stylesheet` and `add_stylesheet_path`) but add the path of a Javascript file. In addition we would need a new function to add javascript file from govuk-frontend (i.e. `add_frontend_component_javascript`). We would also need a new function that would generate a script tag with an import for each Javascript file:

```
<script type="module">
import ModuleName from path/to/moduleName.js;
import ModuleName2 from path/to/moduleName2.js
...
window.GOVUK.ModuleName = ModuleName;
window.GOVUK.ModuleName = ModuleName;
</script>
```

### 3. Add an unique id for each component

When a component is initialised, it will need a unique ID. This is in the instance that there are multiple instances of the same component on the same page. It means that they can be initialised seperately.

```
<% id ||= "default-id-#{SecureRandom.hex(4)}" %>
```

### 4. Add an inline Javascript block to intialise the Javascript in each component partial

We then need to initialise each instance of the component on the page seperately. Some components use Javascript from the gem and `govuk-frontend` and both modules will need to be initialised in this inline Javscript block.

```
<%= javascript_tag nonce: true, type: "module" do -%>
const el = document.querySelector("#<%= id %>");
const module = new window.GOVUK.ModuleName(el);
const moduleFromGovUkFrontend = new window.GOVUK.ModuleNameFromGOVUKFrontend(el);
module.init();
module.init(moduleFromGovUkFrontend);
<% end %>
```

Slimmer pushes all the script tags to the bottom of body, which means the load order will be correct.

### Peformance Testing

The results here are coming from a test integrating the proposed changes to Javascript structuring to the collections application. Collections uses the following components from govuk_pubishing_components:

- Accordion
- Details
- Govspeak
- Image Card
- Metadata
- Step by step nav

The lines importing the javascript for each component was removed from `application.js`. In addition, the lines importing polyfils for supporting older browsers were removed. The lines kept in application.js were importing Javscript for application components or pages. If these were refactored to use individual Javascript loading then the `application.js` of static could be removed entirely.

The modified collections application was deployed to integration and compared to the main branch on integration.

### Considerations

## Analytics

Although the percentage of all users who accessed GOV.UK with IE11 in the timeframe from 24th of April to the 21st of May was 0.067%, that's still 26,205 users. We still want analytics to run for those users and as we aren't changing anything about how Javascript is built we could keep the existing analytics code as is. We would have to move the Javascript for the Cookie Banner component to the rest of the analytics code because it would also need to be in ES5 for it to run on IE11. As there's no changes needed to Sprockets or the rest of the Javascript build process we could run the two 'codebases' side-by-side. An alternative could be to convert the analytics code to ES6 and use a transpiler. This would involve adding some new processes to the way we build JS and integrating a bundler (as well as polyfills for any of the ES6 features that are being used). There isn't an obvious advantage to this method though given that the analytics code is already in ES5 (unless there are ES6 features it would be useful to take advantage of) and adding more polyfills for extra features would increase the size of application.js.

In the Javascript for components there are extra analytics features added. For instance, in the JS for the accordion component there are analytics added for when the user opens and closes the sections. If the JS for accordion from govuk-frontend will not be running on IE11 (because it will be using ES6) then there will be no analytics to recieve from the user as they would not be able to use the Javascript enabled features of the accordion. Removing polyfills from the `application.js` of individual applications will also not effect the analytics as they are imported in the static application which imports the required polyfills and supporting libraries for analytics to function.

0 comments on commit 96c2f33

Please sign in to comment.