I'm building react app provided by create-react-app.
I use craco to make configuration simplify and have set up alise for TypeScript and Webpack.
But when I run test command, the following error is displayed.
Error Message
spec/showMessage.test.tsx
● Test suite failed to run
Cannot find module '#/components/atom/TextButton' from 'src/pages/index.tsx'
Require stack:
src/pages/index.tsx
spec/showMessage.test.tsx
3 | import styled from 'styled-components';
4 |
> 5 | import { TextButton } from '#/components/atom/TextButton';
| ^
This is my craco.config.js
craco.config.js
const path = require("path");
module.exports = {
jest: {
configure: {
roots: ['<rootDir>/src', '<rootDir>/spec'],
testMatch: ['<rootDir>/spec/**/*.{spec,test}.{js,jsx,ts,tsx}'],
},
},
webpack: {
alias: {
"#": path.resolve(__dirname, "./src/"),
"##": path.resolve(__dirname, "./")
},
extensions: ['.ts', '.tsx'],
},
};
I found the solution, I updated package.json like below, it solves this problem.
{
"name": "xxxxx",
:
},
"jest": {
"moduleNameMapper": {
"^#/(.+)": "<rootDir>/src/$1"
}
}
}
Related
I have noticed that Storybook can't process Typescript files if they are from another project (monorepo), but it is doing okay with TS files that are within its project.
How do I configure Storybook to handle TS files outside the project?
Structure of the monorepo:
package1
.storybook
main.ts
preview.ts
component.tsx (this component imports file.ts)
component.stories.tsx
package2
file.ts <- (Storybook can't process this file: ModuleParseError: Module parse failed: Unexpected token)
Here is the main.ts config file:
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const path = require('path');
const toPath = (filePath) => path.join(process.cwd(), filePath);
module.exports = {
"stories": [
"../src/**/*.stories.#(mdx|js|jsx|ts|tsx)"
],
"addons": [
"#storybook/addon-links",
"#storybook/addon-essentials",
"#storybook/addon-interactions",
"#storybook/addon-knobs/register",
"#storybook/preset-create-react-app",
"#storybook/addon-a11y",
'storybook-addon-styled-component-theme/dist/preset',
'storybook-addon-themes',
"storybook-dark-mode",
],
"framework": "#storybook/react",
"core": {
"builder": "#storybook/builder-webpack5",
"disableTelemetry": true,
},
features: {
emotionAlias: false,
},
typescript: { reactDocgen: false },
webpackFinal: async (config, { configType }) => {
return {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'#emotion/core': toPath('node_modules/#emotion/react'),
'emotion-theming': toPath('node_modules/#emotion/react'),
},
plugins: [new TsconfigPathsPlugin()]
},
}
}
}
Here is the error:
ModuleParseError: Module parse failed: Unexpected token (7:11)
File was processed with these loaders:
* ../../node_modules/#pmmmwh/react-refresh-webpack-plugin/loader/index.js
You may need an additional loader to handle the result of these loaders.
| import { Palette as palette } from '../Palette';
|
> const Paper: any = [
| {
If I import the same TS file, but manually precompiled into regular JS - Storybook works.
I have no idea what to try to solve this :(
Add this to .storybook/main.js
webpackFinal: async (config, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.
// Storybook uses its own webpack config, so we need to merge our config with it
// See https://storybook.js.org/docs/configurations/custom-webpack-config/
// Add typescript loader to process TS-files from other packages
config.module.rules.push({
test: /\.(ts|tsx)$/,
use: [
{
loader: require.resolve("ts-loader"),
options: {
reportFiles: [
"../**/src/**/*.{ts,tsx}"
]
}
},
]
});
config.resolve.extensions.push(".ts", ".tsx");
return config;
}
Problem
I have a project let's call it root (based on Preact) that relies on a components package (based on React):
Building it using rollup works fine. When I switch components to use vitejs to build, root fails at runtime with this error:
index.es.js? [sm]:770 Uncaught TypeError: Cannot read properties of undefined (reading 'current')
at jsxDEV (index.es.js? [sm]:770:64)
at jsxWithValidation (index.es.js? [sm]:954:17)
at jsxWithValidationDynamic (index.es.js? [sm]:992:13)
at d.RecipesExplorer [as constructor] (index.es.js? [sm]:1915:33)
at d.O [as render] (index.js:532:14)
at j (index.js:190:14)
at w (children.js:137:3)
at L (index.js:418:4)
at j (index.js:246:20)
at w (children.js:137:3)
I'm not changing anything in root or how it's run. It's built using vitejs as well (in both scenarios).
Rollup config
Here's the rollup.config.js for components (this is the build scenario that works without issue):
// rollup.config.js for components
import { defineConfig } from 'rollup'
import typescript from 'rollup-plugin-typescript2'
import json from '#rollup/plugin-json'
import styles from 'rollup-plugin-styles'
export default defineConfig([
{
input: 'src/index.ts',
output: [
// I don't think we need both of these. Keeping them since we had them for now in webpack
{
dir: 'dist/esm',
format: 'es',
},
{
dir: 'dist/cjs',
format: 'cjs',
},
],
plugins: [typescript({}), json()],
external: ['react', 'react-dom', 'styled-components'],
}
])
Vitejs config
When I switch to using vitejs, this is the vite config:
// vite.config.js for components
import { defineConfig } from 'vite'
import react from '#vitejs/plugin-react'
import { resolve } from 'path'
export default defineConfig({
mode: 'development',
server: {
port: 3000,
open: false,
},
plugins: [react()],
build: {
minify: false,
target: 'modules',
outDir: './dist',
rollupOptions: {
external: ['react', 'react-dom', 'styled-components'],
},
lib: {
entry: resolve(__dirname, './src/index.ts'),
formats: ['cjs', 'es'],
fileName: format => `index.${format}.js`,
},
emptyOutDir: true,
},
})
Package.json snippet
Since vitejs and rollup place files in different directories, I change package.json as well.
For vitejs:
// package.json when configured for vitejs
"name": "components",
...
"exports": {
".": {
"import": "./dist/index.es.js",
"require": "./dist/index.cjs.js"
}
},
"scripts": {
"build": "vite build",
"build:rollup": "rollup -c",
},
...
For rollup:
// package.json when configured for rollup
"name": "components",
...
"exports": {
".": {
"import": "./dist/esm/index.js",
"require": "./dist/cjs/index.js"
}
},
"scripts": {
"build": "vite build",
"build:rollup": "rollup -c",
},
...
Command I'm running & conclusion
When I build components using npm run build (with vite), I get the above error. When I build with npm run build:rollup I get no error.
What am I doing wrong?
Appendix: Root config and package.json
My vitejs config file for root in case that's helpful. Root uses preact and components are built in React.
// vite.config.js for root
import { defineConfig } from 'vite';
import preact from '#preact/preset-vite';
import { resolve } from 'path';
const config = {
RecipeExplorer: {
entry: resolve(__dirname, './src/recipe-explorer.ts'),
fileName: 'recipe-explorer',
},
RecipeList: {
entry: resolve(__dirname, './src/recipe-list.ts'),
fileName: 'recipe-list',
},
RecipeDetailsLegacy: {
entry: resolve(__dirname, './src/recipe-details.ts'),
fileName: 'recipe-details',
},
};
/** IMPORTANT:
* We're using this LIB_NAME env variable so we can create multiple input and output files at build time.
* At the moment this is a work around. Issue is here: https://github.com/vitejs/vite/issues/4530
* Once the above issue gets resolved, we can do away with this workaround.
*/
const currentConfig = config[process.env.LIB_NAME];
if (currentConfig === undefined) {
console.warn(
'LIB_NAME is not defined or is not valid. If you are running a build command LIB_NAME must be specified.',
);
}
// https://vitejs.dev/config/
export default defineConfig({
resolve: {
alias: {
react: 'preact/compat',
'react-dom': 'preact/compat',
'react-dom/test-utils': 'preact/test-utils',
'react/jsx-runtime': 'preact/jsx-runtime',
},
},
plugins: [preact()],
server: {
port: 3010,
host: '0.0.0.0',
},
build: {
outDir: './dist',
lib: {
...currentConfig,
formats: ['cjs', 'es'],
},
emptyOutDir: false,
},
});
I run root using npm run dev which is defined as vite:
// package.json for root
"name": "root",
"scripts": {
"lint": "eslint 'src/**/*.{ts,tsx}'",
"test": "jest",
"dev": "vite",
...
}
...
I'm trying to write a NPM package with some React stuff included, at the moment it's just a component and a hook. To build the package I'm using Webpack. I've added react and react-dom to the externals section to ensure that it's not included in the bundle. I've also marked react as a peerDependency in the package.json and included it as a devDependency. Still I'm getting the error Invalid hook call when trying to use the bundle in another project. I think I've tried everything that I can Google my way to (like using the package with the purpose to solve this) with no luck.
My Webpack config looks like this at the moment:
const path = require('path');
const isProduction = process.env.NODE_ENV?.toLowerCase() === 'production';
const config = {
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
libraryTarget: 'umd',
},
plugins: [],
module: {
rules: [
{
test: /\.(tsx?)$/i,
loader: 'ts-loader',
exclude: ['/node_modules/'],
},
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader',
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.graphql', '.json'],
},
externals: {
react: 'react',
'react-dom': 'react-dom',
},
};
module.exports = () => {
if (isProduction) {
config.mode = 'production';
} else {
config.mode = 'development';
}
return config;
};
The essentials from package.json looks like this:
{
...
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
...
"scripts": {
...
"build": "webpack --mode=production --node-env=production",
"build:dev": "webpack --mode=development",
...
"prepublishOnly": "npm run build"
},
...
"peerDependencies": {
"react": "16.8.0 || ^17.0.2"
},
"peerDependenciesMeta": {
"react": {
"optional": true
}
},
"dependencies": {
"#apollo/client": "^3.3.19",
"apollo-server-errors": "^2.5.0",
"joi": "^17.4.0",
"lodash": "^4.17.21"
},
"devDependencies": {
...
"react": "^17.0.2",
"react-dom": "^17.0.2",
...
}
}
The hook is very simple, it's just trying to a context created by the component using useContext, to be sure that there isn't an issue with this logic I've tried to just use setState with the same result. The hook looks somewhat like this:
function useClient(): Client {
return useContext(getContext());
}
getContext is just a function which either creates or re-uses a existing React.Context (heavily inspired by Apollo Client):
const cache = new (canUseWeakMap ? WeakMap : Map)<
typeof createContext,
Context<Client | undefined>
>();
function getContext() {
let context = cache.get(createContext);
if (!context) {
context = createContext<Client | undefined>(undefined);
context.displayName = 'ClientContext';
cache.set(createContext, context);
}
return context;
}
export default getContext;
The component where I'm trying to use the hook is a simple functional component:
const HelloWorld: FC<HelloWorldProps> = () => {
const client = useClient();
return (
<div>Hello World!</div>
);
};
What am I missing? Really appreciate the help!
EDIT:
I reproduced the issue in a small sample app with just the basics, a external package using setState and then using that package in a create-react-app with the same result:
https://github.com/ganhammar/invalid-hook-call
Thanks for all the help!
The issue was that I stopped publishing packages and instead installed the dependency locally using file:../Client. That caused duplicate instances of React since it used the local-to-the-Client-package instance of React. Publishing only the built output and then installing that dependency solved the issue for me.
I found the following answer helpful for me to realize this (linking the react dependency between the two packages) if anyone else stumbles upon this.
I am testing with Jest and react-testing-library a component that is calling an async function. When I run the test I get the error ReferenceError: waitForElement is not defined
Following this instructions I have tried:
without the useBuiltinsoption in .babelrc, including #babel/polyfill at the top of the app.test.jsx file, , and without #babel/polyfill in the entry array in webpack.config.js. I get the error ReferenceError: waitForElement is not defined from the test run but the application compiles succesfully
with useBuiltIns: 'entry', including #babel/polyfill at the top of the app.test.jsx file, and without #babel/polyfill in the entry array in webpack.config.js . I get Cannot find module 'core-js/modules/es6.array.every' from 'app.test.jsx' and the application fails to compile.
with useBuiltIns: 'entry', NOT including #babel/polyfill at the top of the app.test.jsx file, and WITH #babel/polyfill in the entry array in webpack.config.js. I get the error ReferenceError: waitForElement is not defined from the test run and the application fails to compile.
Here is the code from case 1:
imports in app.test.jsx
import '#babel/polyfill';
import React from 'react';
import { render, fireEvent, cleanup } from 'react-testing-library';
import AppContainer from '../components/AppContainer';
test in app.test.jsx
test('State change', async () => {
const { debug, getByLabelText, getByTestId, getByText } = render(<AppContainer />);
fireEvent.change(getByLabelText('testfield'), { target: { value: 'Hello' } });
fireEvent.click(getByTestId('button'));
await waitForElement(() => getByText('return value'));
debug();
});
webpack.config.js
const HtmlWebPackPlugin = require('html-webpack-plugin');
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.html$/,
use: [
{
loader: 'html-loader',
},
],
},
],
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: [
new HtmlWebPackPlugin({
template: './src/index.html',
filename: './index.html',
}),
],
};
.babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"],
"plugins": [
[
"#babel/plugin-proposal-class-properties",
{
"loose": true
}
]
]
}
I am expecting the waitForElement function to be awaiting for the "return value" text to appear in a second textfield, and then the debug() function to print the page html code. Instead I get the above mentioned errors.
You have to import waitForElement from react-testing-library:
import { render, waitForElement } from 'react-testing-library'
There's no need to install dom-testing-library because RTL depends on it already.
Ok, I solved the problem. I was missing the dom-testing-library dependency.
Here is the working solution.
Install dom-testing library: npm install --save-dev dom-testing-library.
In app.test.jsximport waitForElement and #babel/polyfill:
import '#babel/polyfill';
import { waitForElement } from 'dom-testing-library';
The rest of the code is as in case 1 above.
When I try to run my test they failed in my class he's trying to import my markdown files:
import StartRecord from './Documentation/content/API/Start_record.md';
import UseLive from './Documentation/content/User/Live.md';
import UseRecord from './Documentation/content/User/Record.md';
import UseReplay from './Documentation/content/User/Replay.md';
import Settings from './Documentation/content/User/Settings.md';
Here is my failed message:
FAIL src/__test__/App.test.js
● Test suite failed to run
/Users/anyone/Desktop/web/src/Modules/Dashboard/Documentation/content/User/Live.md:1
({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){# Interface
^
SyntaxError: Invalid or unexpected token
8 | // Content
9 | import StartRecord from './Documentation/content/API/Start_record.md';
> 10 | import UseLive from './Documentation/content/User/Live.md';
| ^
11 | import UseRecord from './Documentation/content/User/Record.md';
12 | import UseReplay from './Documentation/content/User/Replay.md';
13 | import Settings from './Documentation/content/User/Settings.md';
I have search many error like but never found with markdown.
I use webpack and have already configure it to read markdown. (It's a part of the webconfig file)
{
test: /\.md$/,
include: /node_modules/,
use: [
{
loader: "html-loader"
},
{
loader: "markdown-loader"
}
]
}
I have found solution in my jest config by adding to extensions and moduleNameMapper the html and md like that:
module.exports = {
moduleFileExtensions: ['js', 'jsx', 'json', 'png', 'md', 'html'],
transform: {
'^.+\\.(js|jsx)?$': 'babel-jest'
},
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|md|html)$": "<rootDir>/assetsTransformer.js",
"\\.(css|less)$": "<rootDir>/assetsTransformer.js"
},
"setupFiles": [
"./configJSDom.js"
],
"collectCoverage": true,
"collectCoverageFrom": [
"src/*.js",
"src/*/*.js",
"src/*/*/*.js",
"!src/test.js",
"!src/serviceWorker.js",
"!src/index.js"
]
};