Why isn't jest handling ES Module dependencies? - javascript

My package imports an ESM package called c2pa. When I run jest I get an error like this:
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.
Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.
By default "node_modules" folder is ignored by transformers.
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
• If you are trying to use TypeScript, see https://jestjs.io/docs/getting-started#using-typescript
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/configuration
For information about custom transformations, see:
https://jestjs.io/docs/code-transformation
I believe the docs for ECMAScript Modules are for if my package is ESM, but I am having an issue with a dependency.
I also updated my Jest config to try out transformIgnorePatterns override mentioned in the error and Jest gives an error: "SyntaxError: Unexpected token export".
const babelConfig = require('../../babel.config.json');
module.exports = {
preset: 'ts-jest/presets/js-with-babel',
testEnvironment: 'jsdom',
roots: ['tests', 'src'],
collectCoverageFrom: ['src/**/*.ts'],
coveragePathIgnorePatterns: ['__mocks__'],
moduleNameMapper: {
'^uxp$': '<rootDir>/../../dependency-mocks/uxp.js',
},
transformIgnorePatterns: ['/node_modules/(?!c2pa)/'],
globals: {
'ts-jest': {
babelConfig,
},
tsConfig: {
declaration: false,
},
PLUGIN_REV: '__pluginRev__',
},
setupFilesAfterEnv: ['./tests/test.setup.js'],
};
I also tried a babel plugin that is mentioned in the linked SO question.
"env": {
"test": {
"plugins": ["#babel/plugin-transform-modules-commonjs"]
}
},
I'm not sure if there's any other jest of babel config I might be missing here. Clearly something is wrong!

Aside from ignorePattern, I also updated preset:
preset: 'ts-jest/presets/js-with-ts',
The babel config changes were not necessary at all.

Related

node-fetch 3.0.0 and jest gives SyntaxError: Cannot use import statement outside a module

I'm trying to upgrade my api to use node-fetch 3.0.0. Part of the breaking changes in their documentation is that node-fetch is now a pure ESM module.
https://github.com/node-fetch/node-fetch/blob/main/docs/CHANGELOG.md
My unit tests have started breaking from this change. I was using jest.requireActual("node-fetch") for the Response object
const { Response } = jest.requireActual("node-fetch");
However, with the new change, I get:
"Property 'Response' does not exist on type '{}'."
I tried changing to an import statement which seems to fix that error:
import { Response } from "node-fetch"
Now, I get the following error when I run my unit tests:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
C:\Users\{mypath}\api\node_modules\node-fetch\src\index.js:9
import http from 'http';
^^^^^^
SyntaxError: Cannot use import statement outside a module
2 | import { AuthProvider, TokenCache } from "./tokenTypes";
3 | import { getUserEmail, getUserId, getUserRole } from "../common/authUtil";
> 4 | import fetch from "node-fetch";
The error seems to be occurring within node-fetch itself.
I tried changing the test script in my package.json to match what the jest documentation suggests for ESM modules. https://jestjs.io/docs/ecmascript-modules
package.json
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
The documentation also suggests changing the transform property in jest.config.js to take an empty object, but my transform object looks like the following for typescript and ts-jest:
jest.config.js
transform: { "^.+\\.ts?$": "ts-jest" }
If I change this to an empty object all of my tests break. I'm not very familiar with CJS vs ESM, so any help would be appreciated.
Fetch 3.0 is designed for using esmodules instead of commonjs. Therefore you have to make sure you import it into a module.
For example: to import it to app.js : Add "type":"module" in your package.json and then import it.
For importing it to other files or app.js even, you can also change the extension from .js to mjs that will tell nodejs to treat as a esmodule.
Otherwise downgrade fetch to use an older version that supports commonjs like 2.something.
npm install node-fetch#2
npm install #types/node-fetch#2
I had the same problem!
adding
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
"^.+\\.(js)$": "babel-jest",
},
transformIgnorePatterns: [
],
to my jest.config.js file solved it.
and btw these are my versions:
"jest": "^27.3.1",
"ts-jest": "^27.0.7",
"node-fetch": "^3.1.0"

JEST config testing node_module even if its disabled?

I am adding tests to existing codebase with Jest. This is the error I am getting
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
/home/me/app/node_modules/register-service-worker/index.js:32
export function register (swUrl, hooks) {
^^^^^^
SyntaxError: Unexpected token export
I do not understand how to configure the transformIgnorePatterns properly?
But my jest.config.js is setup to ignore node_modules ?
Looking into the react example but can not really make sense how it translates to my stuff...? I'm in VueJS project, no typescript is used.
module.exports = {
preset: "#vue/cli-plugin-unit-jest/presets/typescript-and-babel",
moduleNameMapper: {
"^#/(.*)$": "<rootDir>/src/$1",
},
transformIgnorePatterns: ["/node_modules/"],
// setupFiles: [
// "./tests/unit/setup.ts",
// ],
};
It seems extending jest.config.js solved the error above.
"transform": {
".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",
"^.+\\.js$": "<rootDir>/node_modules/babel-jest"
},
I now have a different error, but this is more Vue specific now.

Jest React Native Expo.fx.js encountered an unexpected token

This is from the boilerplate Expo app built with create-react-native-app
Here's the jest section of my package.json:
"jest": {
"preset": "react-native",
"testEnvironment": "node"
},
Here's the output when I attempt to run jest:
> jest
FAIL src/__tests__/App.js
● Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
/home/charney/om/star-harvester-centauri/node_modules/expo/build/Expo.fx.js:1
import './environment/react-native-logs.fx';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at Runtime._execModule (node_modules/jest-runtime/build/index.js:1157:58)
at Object.<anonymous> (node_modules/expo/src/Expo.ts:1:1)
Same answer as Jest encountered an unexpected token with react-native
"jest": {
"preset": "react-native",
"transform": {
"^.+\\.js$": "<rootDir>/node_modules/react-native/jest/preprocessor.js" },
"testEnvironment": "node"
},

Using native ECMAScript modules in Babeljs config

I'm very, very confused about using babel config with native ECMAScript modules, and "type": "module" set in package.json. As far as I understand Babel docs (here, under "Supported file extensions"), its should be possible. But if I try with config like this:
const config = () => {
const presets = [
"#babel/preset-react",
[
"#babel/preset-env",
{
bugfixes: true,
useBuiltIns: "usage",
corejs: { version: "3.6", proposals: true },
},
],
];
const plugins = ["#babel/plugin-transform-runtime"];
return { presets, plugins };
};
export default config;
I get Error while loading config - You appear to be using a native ECMAScript module configuration file, which is only supported when running Babel asynchronously.
This is expected as the said docs states that "native ECMAScript modules are asynchronous". Alas, sprinkling the above config with async / await doesn't solve the problem. I'm running babel through parcel - is this a issue with parcel? Did I misunderstood the docs? I really appreciate if someone could clarify it for me.
I had a similar problem, and reading Babel site, I came to conclusion that whatever is using your babel config is not calling it asynchronously. In my case it was jest 26.
I got around the problem by changing the config to a json file - babel.config.json.
Other people have changed their config file to a commonjs file - babel.config.cjs, then you will need to change your config file to be commonjs, i.e. to use module.exports = {rest of your config}
I think the problem is that your package.json says you are using ES6 modules, but your Babel config is using module.exports which is CommonJS (not ES6 modules).
Rename babel.config.js to babel.config.cjs

Browserify, minifyify, conditional compilation

TL;DR
minifyify (the Browserify plugin) makes use of uglify-js but appears to be unable to handle Conditional compilation:
compression works
uglifyjs alone works for conditional compilation
minifyify provides additional compilation optimization but I have been unable to use conditional compilation with it
I'm using Browserify with the babelify transformer and the minifyify plugin. Here is the cmd, broken down in readable parts:
browserify
src/scripts/app/index.js
-o
build/prod/public/assets/js/appBundle.min.js
-t
[ babelify --presets [ es2015 ] ]
-p
[ minifyify --no-map --uglify [ --compress [ --drop_console --dead_code --conditionals --unused --if_return ] --mangle --screw-ie8 --define [ DEBUG=false ] ] ]
I've gotten every setting/option to work. However, I am unable to get conditional compilation to work.
Minifyify uses uglifyjs' minify method. The fact I'm passing by minifyify shouldn't really change anything.
Building directly through uglifyjs works
uglifyjs input.js --compress --dead_code --define DEBUG=false -o output.js
But then I lose the additional compressions/optimizations provided by minifyify.
I'm also open to another build process. My needs are resumed in the settings of the current process:
CommonJS required modules
transpiling of ES6 to ES5
advanced minification/compression
It turns out that the culprit was, more or less, uglifyjs. The property name for global definition in the task is different between CMD and Programmatic API.
cmd line: --define VARNAME=VALUE
programmatic: compress: {global_defs: { varname: value } }
That being said, it also seems that minifyify or browserify isn't passing the cmd-line options properly for global definitions - we're still investigating this
programmatic solution
By using the Browserify & minifyify programmatic API, the build task works. Below is the same task as the one in OP, but it works:
"use strict";
var browserify = require("browserify"),
fs = require("fs");
browserify("src/scripts/app/index.js")
.transform("babelify", {presets: ["es2015"], plugins: ["transform-object-assign"]})
.plugin("minifyify", {map: false, uglify: {
compress: {
drop_console: true,
dead_code: true,
conditionals: true,
unused: true,
if_return: true,
global_defs: {
DEBUG: false
}
},
mangle: true,
"screw-ie8": true
}})
.bundle()
.pipe(fs.createWriteStream("build/prod/public/assets/js/appBundle.js"));
update in uglifyjs docs
I've proposed a modification to the current uglifyjs docs, providing an example using the programmatic API as above, so as to avoid this issue for others in the future.

Categories