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

[Bug]: SyntaxError: Cannot use 'import.meta' outside a module #12183

Closed
taniarascia opened this issue Dec 22, 2021 · 11 comments
Closed

[Bug]: SyntaxError: Cannot use 'import.meta' outside a module #12183

taniarascia opened this issue Dec 22, 2021 · 11 comments

Comments

@taniarascia
Copy link

Version

27.4.5

Steps to reproduce

Use import.meta.url in code, such as SharedWorker:

const sharedWorker = new SharedWorker(new URL('path', import.meta.url), {
  type: 'module',
  name: 'whatever',
})

Expected behavior

I expect to be able to continue to run tests without import.meta.url failing.

Actual behavior

SyntaxError: Cannot use 'import.meta' outside a module appears in any test touching the file.

Additional context

Setting NODE_OPTIONS=--experimental-vm-modules does not work.
Trying to mock out global.import.meta does not work.
global.import.meta cannot be saved to a variable or exported from another file, nor the entire URL object.

Environment

System:
    OS: macOS 11.6.2
    CPU: (8) x64 Intel(R) Core(TM) i7-1068NG7 CPU @ 2.30GHz
  Binaries:
    Node: 14.17.0 - ~/.nvm/versions/node/v14.17.0/bin/node
    npm: 6.14.13 - ~/.nvm/versions/node/v14.17.0/bin/npm
  npmPackages:
    jest: ^27.4.5 => 27.4.5
@SimenB
Copy link
Member

SimenB commented Dec 22, 2021

There is no such thing as global.import.meta - import is not a global.

However, using import.meta.url in a test should work fine as long as the test is run as ESM. Can you provide a reproduction? We use import.meta in out tests, so I assume this is just a configuration issue (more steps than just the flag is required to activate it, see https://jestjs.io/docs/ecmascript-modules)

@taniarascia
Copy link
Author

Hi @SimenB, thanks for getting back to me.

Looking at the steps in the linked article, if I follow step one:

Ensure you either disable code transforms by passing transform: {} or otherwise configure your transformer to emit ESM rather than the default CommonJS (CJS).

Changing it to transform: {} causes the entire test suite to fail. They all fail in the setup with:

import '@testing-library/jest-dom'
^^^^^^

SyntaxError: Cannot use import statement outside a module

Of course, if I remove any import it will just keep going down the line and fail anywhere there's an import. Are there instructions for "otherwise configure your transformer to emit ESM rather than the default CommonJS"?

For step two:

Execute node with --experimental-vm-modules, e.g. node --experimental-vm-modules node_modules/jest/bin/jest.js or NODE_OPTIONS=--experimental-vm-modules npx jest etc.. On Windows, you can use cross-env to be able to set environment variables.

Trying either version of those options makes no difference to the tests. They still fail at SyntaxError: Cannot use 'import.meta' outside a module.

For step three:

Beyond that, we attempt to follow node's logic for activating "ESM mode" (such as looking at type in package.json or .mjs files), see their docs for details.

I would prefer not to use type: module in package.json or change all files to .mjs files.

Is there some other form that the transform property should take to allow using import.meta.url?

@SimenB
Copy link
Member

SimenB commented Dec 29, 2021

.js files needs the package.json change. Other files can use https://jestjs.io/docs/configuration#extensionstotreatasesm-arraystring

Same as running files with node

@SimenB
Copy link
Member

SimenB commented Dec 29, 2021

A small reproduction of your setup and config would allow me to confirm what the issue is, at least 🙂

@taniarascia
Copy link
Author

@SimenB Thanks for the quick response.

Changing type: "module" in package.json breaks a lot of things. Even if I convert every instance of module.exports to export default in config files, I still get:

Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.

So if I change babel.config.js to babel.config.cjs and add { targets: { node: 'current', },},, I then get every require failing:

    ReferenceError: require is not defined

    >  8 | require('jest-fetch-mock').enableMocks()

In every test file. Attempting to import here leads to more rabbit holes, with neither enableMocks or enableFetchMocks working.

With the original way I had it, those requires worked fine, all tests using import/export pass just fine, and didn't have an issue until I added the code with import.meta.url in it.

Is there no way for tests to pass with import.meta.url in use without considering everything a module? I don't understand why all other imports work fine but that doesn't.

@taniarascia
Copy link
Author

I'll work on a getting a small repo up for it.

@taniarascia
Copy link
Author

@SimenB Here's a repo, running npm run test will pass if shared worker is not imported, and you will get my error otherwise.

https://github.com/taniarascia/jest-issue

@taniarascia
Copy link
Author

@SimenB Any idea about this? I made a repro (https://github.com/taniarascia/jest-issue) but I still can't figure it out. Is it impossible to do what I'm attempting to do? Unfortunately I can't update to webpack 5 until I figure out this issue.

@taniarascia
Copy link
Author

This issue is not resolved natively, however I made a workaround:

Create a babel-jest.config.js that uses babel-plugin-transform-import-meta.

module.exports = {
  presets: ['@babel/preset-env', '@babel/preset-react'],
  plugins: ['@babel/transform-runtime', 'babel-plugin-transform-import-meta'],
}

Then add this to jest.config.js:

transform: {
    '\\.js$': ['babel-jest', { configFile: './babel-jest.config.js' }],
  },

Hopefully that helps anyone in the future.

@SimenB
Copy link
Member

SimenB commented Jan 4, 2022

Hi there!

import.meta is an ESM only feature and cannot be used in CJS (aka when using require), so you need the files to interpreted as ESM. E.g. doing node src/SharedWorker.js in your repo produces the identical SyntaxError: Cannot use 'import.meta' outside a module error.

One thing you can do is rename it to SharedWorker.mjs and import it using dynamic import (import(./SharedWorker.mjs)) and run Jest with --experimental-vm-modules. That will then fail with ReferenceError: SharedWorker is not defined which is the same error as you get by adding the babel plugin you found to the repo.

tl;dr: if you wanna use import.meta you need to either execute the file as ESM, or transform the syntax away (like what you do with the babel plugin you found).


That said, I think Jest does the correct thing here (under the restraints Node's implementation of ESM places on us). If you have ideas for how we can improve our docs I'd be happy to tweak them, but I don't think there's a bug here, so I'll close it.

Thanks for taking the time to put together a repository so I could investigate!

@github-actions
Copy link

github-actions bot commented Feb 4, 2022

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 4, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants