Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate to import { initAll } from ES module bundle #3833

Merged
merged 5 commits into from
Jun 22, 2023

Conversation

colinrotherham
Copy link
Contributor

@colinrotherham colinrotherham commented Jun 20, 2023

This PR follows our switch to ES modules:

It changes our review app to use ES module import not window.GOVUKFrontend.* for #3508

Before

We include our JavaScript as an IIFE via <script type="module">

<script type="module" src="/javascripts/all.min.js"></script>
<script type="module">window.GOVUKFrontend.initAll()</script>

After

We import our JavaScript as an ES module bundle via <script type="module">

<script type="module" src="/javascripts/all.bundle.min.mjs"></script>
<script type="module">
  import { initAll } from '/javascripts/all.bundle.min.mjs'
  initAll()
</script>

Whilst the first <script> is optional we've kept it for the browser preload scanner (see comment)

@colinrotherham colinrotherham added documentation User requests new documentation or improvements to existing documentation javascript labels Jun 20, 2023
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3833 June 20, 2023 07:16 Inactive
@@ -4,6 +4,10 @@
<link rel="stylesheet" href="/stylesheets/app.min.css">

{% block styles %}{% endblock %}

<script type="importmap">
{ "imports": { "govuk-frontend": "/javascripts/all.bundle.min.mjs" } }
Copy link
Contributor Author

@colinrotherham colinrotherham Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An import map has been added to resolve govuk-frontend to /javascripts/all.bundle.min.mjs when run inside Puppeteer page.evaluate() like this:

await page.evaluate(async (exportName) => {
  const { CharacterCount } = await import('govuk-frontend')

  const $module = document.querySelector('[data-module]')
  const component = new CharacterCount($module)

  component.init()
})

It also ensures that our on-page evaluated JavaScript can be type checked

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question Is that importmap used outside of the test boilerplate? If not, I'd suggest moving it to that template specifically (even if it lives at the top of the <body> tag) as it's a feature to help with the tests 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used by renderAndInitialise() via /tests/boilerplate

const namespace = await import('govuk-frontend')

(Few other tests also use boilerplate)

But all the globals tests (now "exports" rather than globals) use the home page /

const namespace = await import('govuk-frontend')

const namespace = await import('govuk-frontend')

Object.keys(await import('govuk-frontend'))

Let me just double check they'd all work from the boilerplate too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@romaricpascal Yeah all fine, I've moved it to the boilerplate only

@@ -15,6 +19,9 @@
{% set mainClasses = 'govuk-main-wrapper--auto-spacing' %}

{% block bodyEnd %}
<script type="module" src="/javascripts/all.min.js"></script>
<script type="module">window.GOVUKFrontend.initAll()</script>
<script type="module" src="/javascripts/all.bundle.min.mjs"></script>
Copy link
Contributor Author

@colinrotherham colinrotherham Jun 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keeping the <script> tag allows browser preload scanners to download our ES module bundle early

It's optional and can be replaced with modulepreload in future (Safari 17+)

<link rel="modulepreload" href="/javascripts/all.bundle.min.mjs">

Note: See browser support for <link rel="modulepreload">

@colinrotherham colinrotherham linked an issue Jun 20, 2023 that may be closed by this pull request
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3833 June 20, 2023 12:44 Inactive
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3833 June 20, 2023 14:51 Inactive
@colinrotherham
Copy link
Contributor Author

colinrotherham commented Jun 22, 2023

@romaricpascal We're 100% "double download" free in

Safari 11.1
Safari 12.1

☝️ Both browsers show concurrent download alongside the CSS

Additional checks in Internet Explorer 11 and Safari 10.1 show components without JavaScript

These tests appear to use dynamic `import()` but really they’re transformed to `require()` by Jest to clear the “require cache” between test runs

Since we’d like to start using _real_ dynamic `import()` in our Puppeteer tests (to import `govuk-frontend` as an ES module) the task arguments must be converted back to CommonJS

See: https://jestjs.io/docs/ecmascript-modules
This fixes an issue running `import()` via Puppeteer by turning off dynamic import `require()` transforms that use Babel runtime helper
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3833 June 22, 2023 16:44 Inactive
An import map has been added to resolve `govuk-frontend` to `/javascripts/all.bundle.min.mjs` when run inside Puppeteer `page.evaluate()`
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3833 June 22, 2023 16:50 Inactive
Copy link
Member

@romaricpascal romaricpascal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice to totally take advantage of the ES modules for importing our API 🥳

I think the review app and its tests are in a bit of a wonky state, though, as it neither follows:

  1. an import version of https://frontend.design-system.service.gov.uk/importing-css-assets-and-javascript/#add-the-javascript-file-to-your-html, where we would serve node_modules/govuk-frontend/dist/govuk/all.mjs without bundling
  2. https://frontend.design-system.service.gov.uk/importing-css-assets-and-javascript/#import-javascript-using-a-bundler, where the bundled file would be the one initialising the components

So we're not quite testing that GOV.UK Frontend does what it's meant to in our tests, more that our review app bundle does what GOV.UK Frontend is meant to do (in a closer way than before so that's a win, though 🥳 ).

Let's merge this step forward and I'll add a note on #3508 to update how the review app serves/bundles govuk-frontend so we do test govuk-frontend and not the review app bundle (I'd say in favour of 1 so we actually test govuk-frontend through the review app).

@colinrotherham colinrotherham merged commit 51461de into main Jun 22, 2023
@colinrotherham colinrotherham deleted the es-module-import branch June 22, 2023 17:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation User requests new documentation or improvements to existing documentation javascript
Projects
Development

Successfully merging this pull request may close these issues.

Update review app to use new JavaScript loading strategy
3 participants