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

Babel transform only *.mjs files for Jest #3851

Merged
merged 6 commits into from
Jun 29, 2023
Merged

Conversation

colinrotherham
Copy link
Contributor

@colinrotherham colinrotherham commented Jun 26, 2023

Quick spike to solve to blockers in #3812

  1. Puppeteer needs to keep dynamic import() to load in a browser
  2. Jest needs to replace dynamic import() with require() for virtual machine contexts

By running Babel only on *.mjs ES modules we can use dynamic import() in *.js CommonJS

Update: Gave it a couple of runs on Windows to confirm it works:

@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 26, 2023 09:08 Inactive
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 26, 2023 10:01 Inactive
@romaricpascal
Copy link
Member

I like that, that sounds a very “working with how Node works” way to sort ourselves out 🙌🏻 Two little questions:

  1. Is it worth linting for it, or would Jest fail with enough clarity that we'd know that it's because it transpiled an import to require that our tests failed
  2. Do del and slash need to be treated specially because they're dependencies with "type":"module" in their package? Would there be other situation we'd want to transpile .js files (for ex. some we do author)?

@colinrotherham
Copy link
Contributor Author

colinrotherham commented Jun 26, 2023

Thanks @romaricpascal

Using ESLint

  1. Is it worth linting for it, or would Jest fail with enough clarity that we'd know that it's because it transpiled an import to require that our tests failed

Yeah we could add ESLint rules but you can't identify Puppeteer tests by filename

Puppeteer tests with import() in *.mjs would fail with missing Babel helpers

ReferenceError: _interopRequireWildcard is not defined
pptr:evaluate;Object.%3Canonymous%3E%20(%2FUsers%2Fcolin%2FSites%2FGDS%2Fgovuk-frontend%2Fpackages%2Fgovuk-frontend%2Fsrc%2Fgovuk%2Fcomponents%2Fglobals.test.mjs%3A13%3A29):1:61

Other tests with import() in *.js would fail with Jest runtime syntax errors

Jest encountered an unexpected token

Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

ESM-only packages

  1. Do del and slash need to be treated specially because they're dependencies with "type":"module" in their package?

Babel doesn't transpile code in node_modules by default so we:

  1. Update transformIgnorePatterns to allow paths to del and slash
  2. Update transform to transpile the full *.js path to del and slash

Yeah they use "type":"module" so their *.js paths would be skipped if we only transpile *.mjs

Would there be other situation we'd want to transpile .js files (for ex. some we do author)?

Simplest way to opt in to Babel transpiling would be to use the *.mjs extension still

@colinrotherham
Copy link
Contributor Author

There are also Jest environments that avoid Node.js VM contexts like:

@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 26, 2023 10:26 Inactive
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 26, 2023 10:29 Inactive
@colinrotherham colinrotherham changed the title [SPIKE] Babel transform only *.mjs files for Jest Babel transform only *.mjs files for Jest Jun 26, 2023
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 26, 2023 15:20 Inactive
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 26, 2023 16:01 Inactive
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 26, 2023 18:13 Inactive
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 28, 2023 15:08 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.

Looking good! 🙌🏻 There's a reference to globals.test.mjs in testing-and-linting.md which could use updating as part of 84f3263. And I think it'd be worth highlighting the reason for picking a .js file over a .mjs (.mjs by default, unless you need to do a dynamic import() in the file, is that it?). Would you mind adding that, please?

@@ -60,6 +60,7 @@ const config = {
*/
export default {
collectCoverageFrom: ['./packages/govuk-frontend/src/**/*.{js,mjs}'],
coverageProvider: 'v8',
Copy link
Member

Choose a reason for hiding this comment

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

question Does that affect the coverage of the tests that do run through Babel? I think what I'm worried about is the Babel helpers being considered for the coverage if it is collected via v8, but excluded with default setting. I wouldn't block the PR as we're not looking much at coverage at the moment, but it'll be something to dig into if/when we get to #3101 😊

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah a little bit

Coverage from coverageProvider: 'v8' is generated from transpiled code but it's mapped back to the original source using source maps and will exclude sneaky helpers. Read about when it launched in Jest v25

I'm sure we'll see minor differences in coverage percentages. Turning off Babel transforms for *.js stops babel-plugin-istanbul from instrumenting the code so it's better than losing it entirely?

Copy link
Member

Choose a reason for hiding this comment

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

The situation doesn't sound too bad if the v8 thing takes sourcemaps into account. Like I said, not a blocker, just wanted to understand a bit and flag for #3101 😊

Previously we excluded Babel `transform-dynamic-import` but we realised that some tests DO actually need to transform `import()` to `require()` or they hit Jest limitations:

>You need to run with a version of node that supports ES Modules in the VM API. See https://jestjs.io/docs/ecmascript-modules

By switching to CommonJS we can continue to pass `import('govuk-frontend')` into Puppeteer but avoid Babel transforms that run on ES modules
We can collect coverage using the Node.js V8 engine to avoid running every file through Babel
We don’t need to transform `*.js` files as they’re typically supported by Jest already, but we’ll add another commit to handle some exceptions
We need to include ESM-only modules and in future any files with dynamic `import()` so Jest can run them as compatible CommonJS
@govuk-design-system-ci govuk-design-system-ci temporarily deployed to govuk-frontend-pr-3851 June 29, 2023 11:58 Inactive
@colinrotherham
Copy link
Contributor Author

Looking good! 🙌🏻

Thanks @romaricpascal

There's a reference to globals.test.mjs in testing-and-linting.md which could use updating as part of 84f3263.

✅ Done

And I think it'd be worth highlighting the reason for picking a .js file over a .mjs (.mjs by default, unless you need to do a dynamic import() in the file, is that it?). Would you mind adding that, please?

✅ Done

I've gone with the text below as it's only dynamic import() in browsers (e.g. Puppeteer) that we need to call natively without Babel transforms. All other tests using dynamic import() have been reverted and work great still

Tests should be written using ES modules (*.mjs) by default, but use CommonJS modules (*.js) for tests using browser import() in Puppeteer. This avoids Babel transforms until Jest supports import() and ES modules in future.

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.

Neat! Thanks for updating the docs 🥳 ⛵

@colinrotherham colinrotherham merged commit 6e85ce0 into main Jun 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants