Swiper 8 and jest - reactjs

Swiper 8 and Jest (support ESM) Must use import to load ES Module
enter image description here
enter image description here
How we can solve if I need to keep swiper 8 (without downgrade)

In case someone runs into this same issue but is using NextJs 12.2, next/jest and Jest 28 the answer is a variation from Francisco Barros' answer.
// jest.config.js
const nextJest = require('next/jest')
const createJestConfig = nextJest({
// Path to Next.js app to load next.config.js
dir: './'
})
/** #type {import('#jest/types').Config.InitialOptions} */
const customJestConfig = {
/**
* Custom config goes here, I am not adding it to keep this example simple.
* See next/jest examples for more information.
*/
}
module.exports = async () => ({
/**
* Using ...(await createJestConfig(customJestConfig)()) to override transformIgnorePatterns
* provided byt next/jest.
*
* #link https://github.com/vercel/next.js/issues/36077#issuecomment-1096635363
*/
...(await createJestConfig(customJestConfig)()),
/**
* Swiper uses ECMAScript Modules (ESM) and Jest provides some experimental support for it
* but "node_modules" are not transpiled by next/jest yet.
*
* #link https://github.com/vercel/next.js/issues/36077#issuecomment-1096698456
* #link https://jestjs.io/docs/ecmascript-modules
*/
transformIgnorePatterns: [
'node_modules/(?!(swiper|ssr-window|dom7)/)',
]
})
The transformIgnorePatterns on jest.config.js prevents the Swiper files from being transformed by Jest but it affects the CSS files that are provided by this package.
Mocking these CSS files is the solution with the smallest configuration.
In my case, it didn't matter much about having access to these CSS files while testing but there are other approaches if these files are relevant to you.
// jest.setup.js
import "#testing-library/jest-dom/extend-expect";
jest.mock("swiper/css", jest.fn());
export {};
I created a repository for a full reference of the necessary setup.
https://github.com/markcnunes/with-jest-and-esm
Have in mind this setup might have to change for future Next.js / next/js versions but just sharing this approach in case this is useful for other people using this same setup.

I have the same issue and still searching for a solution; I believe what the OP is asking is how can we transform swiper/react into a CJS module on the JEST pipeline.
Since using ESM Experimental in jest is not a good option...
Downgrading to v6 is not an option;
Any code that imports Swiper v8 fails in JEST because Swiper 8 only exports ESM;
A few days have passed since my original response. In the mean time I have found a solution that I have been using to effectively use Swiper v8 while still being able to test components that depend on it using jest.
The trick is to map the ESM imports to actual JavaScript and CSS files exported by Swiper, which can then be processed by babel and transpiled into CJS.
Create a file
// ./mocks/misc/swiper.js
module.exports = {
// Rewrite Swiper ESM imports as paths (allows them to be transformed w/o errors)
moduleNameMapper: {
"swiper/react": "<rootDir>/node_modules/swiper/react/swiper-react.js",
"swiper/css": "<rootDir>/node_modules/swiper/swiper.min.css",
"swiper/css/bundle": "<rootDir>/node_modules/swiper/swiper-bundle.min.css",
"swiper/css/autoplay": "<rootDir>/node_modules/swiper/modules/autoplay/autoplay.min.css",
"swiper/css/free-mode": "<rootDir>/node_modules/swiper/modules/autoplay/free-mode.min.css",
"swiper/css/navigation": "<rootDir>/node_modules/swiper/modules/autoplay/navigation.min.css",
"swiper/css/pagination": "<rootDir>/node_modules/swiper/modules/autoplay/pagination.min.css"
},
// Allow Swiper js and css mapped modules to be imported in test files
transform: {
"^.+\\.(js|jsx|ts|tsx)$": ["babel-jest", { presets: ["next/babel"] }],
"^.+\\.(css)$": "<rootDir>/jest.transform.js"
},
// Do not transform any node_modules to CJS except for Swiper and Friends
transformIgnorePatterns: ["/node_modules/(?!swiper|swiper/react|ssr-window|dom7)"]
}
Create another file in the root of your repository
// jest.transform.js
"use strict"
const path = require("path")
// Requried to fix Swiper CSS imports during jest executions, it transforms imports into filenames
module.exports = {
process: (_src, filename) => `module.exports = ${JSON.stringify(path.basename(filename))};`
}
Finally in your jest configuration use the things you created
// jest.config.js
const swiperMocks = require("./mocks/misc/swipper");
module.exports = {
...yourConfigurations
moduleNameMapper: {
...yourOtherModuleNameMappers,
...swiperMocks.moduleNameMapper,
},
transform: {
...yourOtherTransforms
...swiperMocks.transform,
},
transformIgnorePatterns: [
...yourOtherTransformsIgnorePatterns
...swiperMocks.transformIgnorePatterns,
],
}

Related

After updating the query-string library, test:ci now fails

The development environment uses next.js 13.
After updating the query-string library to 8.1, test:ci now fails.
It fails at the following point.
before "query-string": "^7.1.0",
after "query-string": "^8.1.0",
error
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import * as queryString from './base.js';
^^^^^^
SyntaxError: Cannot use import statement outside a module
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.
Changed part.
before
import { stringifyUrl } from 'query-string';
.
. omission
.
stringifyUrl({url})
after
import queryString from 'query-string';
.
. omission
.
queryString.stringifyUrl({ url })
I am very troubled.
If anyone knows how to solve this problem, please let me know.
added
module.exports = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!**/*.d.ts'],
moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: {
// Handle CSS imports (with CSS modules)
// https://jestjs.io/docs/webpack#mocking-css-modules
'^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',
// Handle CSS imports (without CSS modules)
'^.+\\.(css|sass|scss)$': '<rootDir>/src/__mocks__/styleMock.ts',
// Handle image imports
// https://jestjs.io/docs/webpack#handling-static-assets
'^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/src/__mocks__/fileMock.ts`,
// Handle ESM packages
'^react-markdown$': '<rootDir>/src/__mocks__/react-markdown.tsx',
},
testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
testEnvironment: 'jest-environment-jsdom',
transform: {
// Use babel-jest to transpile tests with the next/babel preset
// https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
},
transformIgnorePatterns: [
'/node_modules/',
'^.+\\.module\\.(css|sass|scss)$',
],
};
The issue is query-string version 8 introduced a breaking to consuming applications because their dependencies upgraded to ESM. See their release notes here:
v8.0.0
Breaking
Require Node.js 14
This package is now pure ESM. Please read this.
Add "module": "node16", "moduleResolution": "node16" to your
tsconfig.json.
(Example)
And more!!!
It appears Jest in trying to use import but it's not configured to do so. Jest also provides some guidance for how to deal with this: https://jestjs.io/docs/ecmascript-modules
Since you're using Next.js, those steps don't really apply.
I'm reproducing and testing in a sandbox... Will update shorty.
For React apps:
Try setting "type": "module" inside package.json.
You should also update your package.json test script to:
"test": "node --experimental-vm-modules ./node_modules/.bin/jest"
Now inside jest.config.js you'll want to export transform: {}:
export default {
transform: {}
}
After all those steps, you should no longer have this issue.

#react-three/fiber node module not working in Expo app

I have an expo app .. all the mode modules are js ones. Now I am trying to add #react-three/fiber which I think is written in typescript. So, i feel the module is not working as expo is not compiling the ts or tsx files as it does with the js files. Can someone tell me how to make expo to build ts files also alongside the existing js files and node modules. Is expo capable of handling both js and ts in same project ? Below is the exception i am getting on opening the App
* /<projectpath>/node_modules/#react-three/fiber/dist/native.js(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
* /<projectpath>/node_modules/#react-three/fiber/dist/native.js/index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
Android Bundling failed 1083ms
While trying to resolve module `#react-three/fiber` from file `/<projectpath>/src/screens/spinner/spinner-3d-60deg.js`, the package `/<projectpath>/node_modules/#react-three/fiber/package.json` was successfully found. However, this package itself specifies a `main` module field that could not be resolved (`/mnt/57b4f320-a355-468b-a1f5-2d4d156e25e8/projects/external/PickABoo/node_modules/#react-three/fiber/dist/native.js`. Indeed, none of these files exist:
* /<projectpath>/node_modules/#react-three/fiber/dist/native.js(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
* /<projectpath>/node_modules/#react-three/fiber/dist/native.js/index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
You need to configure metro to compile typescript files.
Edit metro.config.js (located in your project's root folder) and make sure you have the following sourceExts: ['jsx', 'js', 'ts', 'tsx'] within your resolver options.
See relevant documentation here: https://facebook.github.io/metro/docs/configuration/#sourceexts
If you don't already have a metro.config.js file in your root folder, you can create one and enter the code below:
metro.config.js
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
resolver: {
sourceExts: ['jsx', 'js', 'ts', 'tsx'], // this line compiles typescript
},
};

Storybook - no stories showing up in typescript project with custom webpack / babel

I am trying to set up Storybook in a project. My project is runing on react#^16, and I'm using typescript, with a custom babel and webpack setup for development and build. To set up storybook, I did
npx sb init
This installs everything needed. It puts a .storybook folder in the root folder, and a stories folder in my src folder with some prefab components and stories in tsx format (which is what I want):
The .storybook/main.js file seems fine:
module.exports = {
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials"
]
}
And the average .stories.js file automatically installed by npx sb init also seems fine:
import React from 'react';
// also exported from '#storybook/react' if you can deal with breaking changes in 6.1
import { Story, Meta } from '#storybook/react/types-6-0';
import { Header, HeaderProps } from './Header';
export default {
title: 'Example/Header',
component: Header,
} as Meta;
const Template: Story<HeaderProps> = (args) => <Header {...args} />;
export const LoggedIn = Template.bind({});
LoggedIn.args = {
user: {},
};
export const LoggedOut = Template.bind({});
LoggedOut.args = {};
But when I run npm run storybook, the storybook landing page has no stories. Even though it had installed some default stories to start playing with. It says:
Oh no! Your Storybook is empty. Possible reasons why:
The glob specified in main.js isn't correct.
No stories are defined in your story files.
As requested, here is a link to the repo so you can dig a bit deeper into the structure, weback config, etc. Note I have not committed the npx sb init changes yet, so you won't see the files there, only my starting point just before running the sb init.
I haven't had any issues getting npx sb init to work with a standard create-react-app, but with my custom webpack build and typescript, its just empty. What's going wrong?
Edit: Additional detail
I realize that just running npx sb init, then npm run storybook throws this error:
ERROR in ./.storybook/preview.js-generated-config-entry.js
Module not found: Error: Can't resolve 'core-js/modules/es.array.filter'
Based on this thread, installing core-js#3 solves the problem and storybook runs, though with no stories.
It seems like the babel plugin transform-es2015-modules-amd doesn't fit right with storybook since sb still uses your babel configuration.
You might need to remove it then it would work:
{
"plugins": [
// "transform-es2015-modules-amd", // Remove this plugin
]
}
If you want to have a special babel configuration for storybook, place it .storybook/.babelrc so the configuration would be simple like this:
.storybook/.babelrc:
{
"presets": ["#babel/preset-env", "#babel/preset-react", "#babel/preset-typescript"]
}
NOTE: You might miss to forget install #babel/preset-typescript to help you transform your typescript code.
Maybe you have problems with the stories path, try to save only "../src/**/*.stories.js" in your config to see if its the reason
"stories": [
"../src/**/*.stories.mdx",
"../src/**/*.stories.#(js|jsx|ts|tsx)"
]
In case of dealing with arcgis-js-api in sb, you have to declare #arcgis/webpack-plugin in storybook's webpack configuration by adding to its config.
Here are a few steps you have to do:
Add webpackFinal property in .storybook/main.js with following content:
const ArcGISPlugin = require('#arcgis/webpack-plugin');
module.exports = {
// ...
webpackFinal: (config) => {
// Add your plugin
config.plugins.push(
new ArcGISPlugin(),
);
// Since this package has used some node's API so you might have to stop using it as client side
config.node = {
...config.node,
process: false,
fs: "empty"
};
return config;
}
};
One more thing to be aware of, some components are importing scss files, so you might need to support it by adding a scss addon '#storybook/preset-scss'
// Install
npm i -D #storybook/preset-scss css-loader sass-loader style-loader
// Add to your current addons
{
addons: ['#storybook/addon-links', '#storybook/addon-essentials', '#storybook/preset-scss'],
}
Like a tmhao2005 say. Storybook still uses your babel configuration. And this is the intended behavior. This thread at github also describes how the fix similar issue.
Updated your config .storybook/main.js.
If you use .babelrc:
babel: async options => ({ ...options, babelrc: false })
Or .babel.config.js:
babel: async options => ({ ...options, configFile: false })

Webpack + `create-react-app` | ProvidePlugin Not Loading React as a Module

* Note: I barely know anything about Webpack.
I want to load the react node module and other modules in Webpack via ProvidePlugin to have global access to them.
I created a create-react-app and ran eject and got access the pre-defined configuration for Webpack create-react-app provides.
I read this post about loading react globally via PluginProvidor and read about PluginProvidor itself in the Webpack docs, where the latter states:
By default, module resolution path is current folder (./**) and node_modules
Based on that, in webpack.config.js, in plugins, I added the following PluginProvidor:
...
plugins: [
new webpack.ProvidePlugin(
{
React: 'react'
}
)
]
...
But it didn't work - in a JSX file when I call React (e.g class MyComponent extends React.Component { ... }) I get an error that says that React isn't defined (and also a React-specfic error that React must be defined in JSX files).
Why doesn't it work? As far as I understand, I'm giving the same identifier I call in my JSX file, and like I mentioned, according to the Webpack docs, to the path of the react module in node_modules - both as required for it to work.
My configuation file: webpack.config.js
It's not a good idea to use ProvidePlugin, and even worse is to eject your CRA.
Instead of ProvidePlugin use globals:
// globals.js
import React from 'react';
window.React = React;
and then import './globals'
import './globals';
// no need import React
import ReactDOM from 'react-dom';
...
For adding plugins to CRA web pack refer to react-app-rewired.
Example of adding a plugin:
/* config-overrides.js */
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
module.exports = function override(config, env) {
if (!config.plugins) {
config.plugins = [];
}
config.plugins.push(new MonacoWebpackPlugin());
return config;
};
Demo:

ant design - huge imports

I'm using ant design library for my react application.
And I've faced with huge imports, that hurts my bundle (currently 1.1 mb in minified version because of ant-design lib).
How can I differently import antd components through all my app?
UPDATE:
Seems antd has some huge or non optimized modules.
Here the thing - only difference is import Datepicker module, and.. boom! + almost 2MB (in dev bundle ofc.)
UPD: the underlying issue seems to be resolved for the new (4.0) version of antd.
Therefore, if you try to resolve this issue for the earlier versions, the recommended way is to migrate onto antd 4
Previous answer:
At the moment, a huge part of antd dist is SVG icons.
There is no official way to deal with it yet (check the issue on github).
But a workaround exists.
Adapt webpack to resolve icons differently. In your webpack config:
module.exports = {
//...
resolve: {
alias: {
"#ant-design/icons/lib/dist$": path.resolve(__dirname, "./src/icons.js")
}
}
};
Create icons.js in the folder src/ or wherever you want it. Be sure it matches the alias path!
In this file, you define which icons antd should include.
export {
default as DownOutline
} from "#ant-design/icons/lib/outline/DownOutline";
It's also possible to do this with react-app-rewired (create-react-app modifications) within config-overrides.js
1) Prevent antd to load the all moment localization.
Add webpack plugin and configure it in webpack.config.js like the follow:
plugins: [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /ru/),
],
resolve: {
alias: {moment: `moment/moment.js`}
},
target: `web`
}
2) Use the same moment version as in antd library.
3) Use modularized antd
Use babel-plugin-import
// .babelrc or babel-loader option
{
"plugins": [
["import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" }]
// `style: true` for less
]
}
I use BundleAnalyzerPlugin to analyze the bundle.
plugins: [new BundleAnalyzerPlugin()]
Looking at the docs
https://ant.design/docs/react/getting-started#Import-on-Demand
there is a recommedation to import individual components on demand.
So, you can try and replace
import { Button} from 'antd'
with
import Button from 'antd/lib/button'
I reduced my bundle size by 500KB by editing config-override.js like so:
config-override.js
const { override, fixBabelImports } = require('customize-cra');
const path = require('path');
module.exports = override(
fixBabelImports('import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css'
}),
// used to minimise bundle size by 500KB
function(config, env) {
const alias = config.resolve.alias || {};
alias['#ant-design/icons/lib/dist$'] = path.resolve(__dirname, './src/icons.js');
config.resolve.alias = alias;
return config;
}
);
./src/icons.js
/**
* List all antd icons you want to use in your source code
*/
export {
default as SearchOutline
} from '#ant-design/icons/lib/outline/SearchOutline';
export {
default as CloseOutline
} from '#ant-design/icons/lib/outline/CloseOutline';
export {
default as QuestionCircleOutline
} from '#ant-design/icons/lib/outline/QuestionCircleOutline';
export {
default as PlayCircleOutline
} from '#ant-design/icons/lib/outline/PlayCircleOutline';
export {
default as PauseCircleOutline
} from '#ant-design/icons/lib/outline/PauseCircleOutline';
export {
default as LoadingOutline
} from '#ant-design/icons/lib/outline/LoadingOutline';
Before
After
Those few components are certainly not 1.2M together. Looks like you are importing the whole library when you only need a few components.
To get antd to load only the needed modules you should use babel-plugin-import. Check your console log for the "You are using a whole package of antd" warning described at that link.
Check out the docs for Create-React-App for how to implement it if you're using CRA.
Try using code splitting using webpack and react router. It will help you to load the modules asynchronously. This is the only solution helped me to improve the page load time when using ant framework.
Issue which caused large bundle size has been fixed in Ant Design 4.0.
Quoting from the release announcement.
Smaller size
In antd # 3.9.0, we introduced the svg icon ([Why use the svg icon?]
()). The icon API
using the string name cannot be loaded on demand, so the svg icon file
is fully introduced, which greatly increases the size of the packaged
product. In 4.0, we adjusted the icon usage API to support tree
shaking, reducing the default package size of Antant by about 150 KB
(Gzipped).
In order to install Ant Design 4 you have to do following
npm install antd#4.0.0-rc.1
// or in yarn
yarn add antd#4.0.0-rc.1

Resources