how to set base path for all imports in react - reactjs

In a create-react-app cli project, using configs below to set all import paths to src as base url
jsconfig.json
{
"compilerOptions": {
"jsx": "react",
"baseUrl": "src/",
"paths": {
"~/*": ["./*"]
}
}
}
package.json
..
"scripts": {
"start": "react-scripts start",
"build": "react-scripts --max_old_space_size=4096 build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
all I want to use import statments like this:
import { PanelHeader } from '#/components/panel';
instead of this:
import { PanelHeader } from '../../../components/panel';

By checking Absolute Imports on CRA you can see two things:
(1) Setting baseUrl to src
{
"compilerOptions": {
"baseUrl": "src"
},
"include": ["src"]
}
(2) Using components/.. when importing a component from folder called components (subfolder of src folder)
Example — import Button from 'components/Button';
So, get rid of #/ part.

You will need to add it to your webpack config and jsconfig files.
Install the following packages,
npm install customize-cra react-app-rewired
Then create a config-overrides.js in root project and add the following,
const { override, addWebpackAlias } = require('customize-cra');
const path = require('path');
module.exports = override(
addWebpackAlias({
'#': path.resolve(__dirname, 'src')
})
);
Add the following to your jsconfig.json,
{
"compilerOptions": {
"jsx": "react",
"baseUrl": "src",
"paths": {
"#/*": ["*"]
}
}
}
You will have to use react-app-rewired to start your project or add it to your package.json scripts,
npx react-app-rewired start
or
...
"scripts": {
"start": "react-app-rewired start",
...

Related

Add cypress code coverage to react project created without cra

I have React project created without cra. I need to add code coverage for cypress e2e tests.
In app created with cra I do the following instructions for add code coverage. And add this line of code in package.json
"start": "react-scripts -r #cypress/instrument-cra start",
This work's well with cra.
But in app without cra I can't add react-scripts or #cypress/instrument-cra for get code coverage information.
How to realize this?
My current configuration ->
babel.config.json
{
"presets": [
"#babel/preset-env",
[
"#babel/preset-react",
{
"runtime": "automatic"
}
],
"#babel/preset-typescript"
],
"plugins": [
"istanbul",
"transform-class-properties",
[
"#babel/plugin-transform-runtime",
{
"useESModules": true,
"regenerator": false
}
]
],
"env": {
"development": {
"plugins": ["istanbul", "transform-class-properties"]
},
"test": {
"presets": [
["#babel/preset-env", {
"targets": "current node"
}]
]
}
}
}
e2e.ts
// Import commands.js using ES2015 syntax:
import "#cypress/code-coverage/support";
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.on('uncaught:exception', () => {
/**
* Returning false here prevents Cypress from
* failing the test when one of requests fails
*/
return false
});
package.json
"scripts": {
"start": "webpack-cli serve --port 9007 --env currentEnv=local",
"build": "webpack --mode production",
"serve": "serve dist -p xxxx",
"clean": "rm -rf dist",
"test": "cross-env BABEL_ENV=test jest",
"cy:open": "cypress open",
"cy:run": "cypress run",
"pretest:e2e:run": "npm run build",
"test:e2e:run": "start-server-and-test start http://localhost:9000 cy:run",
"test:e2e:dev": "start-server-and-test start http://localhost:9000 cy:open",
"watch-tests": "cross-env BABEL_ENV=test jest --watch",
"check:coverage": "nyc report --reporter=text-summary --check-coverage",
"prepare": "husky install"
},
// ...
"nyc": {
"all": true,
"excludeAfterRemap": true,
"check-coverage": true,
"extension": [
".tsx"
],
"include": [
"src/views/**/*.tsx"
]
}
cypress.config.ts
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here,
require("#cypress/code-coverage/task")(on, config);
// include any other plugin code...
// It's IMPORTANT to return the config object
// with any changed environment variables
return config;
},
video: false,
baseUrl: "http://localhost:3000/",
},
});
Currently, in browser after each test does finished I get the following error
Could not find any coverage information in your application by looking at the window coverage object. Did you forget to instrument your application? See code-coverage#instrument-your-application [#cypress/code-coverage]

How to enable import assertions for Babel?

In my React app I want to use import assertion:
import data from "./json/clients-m.json" assert { type: "json" }
However, I get the following error:
ERROR in ./src/Clients.js
Module build failed (from ./node_modules/babel-loader/lib/index.js):
SyntaxError: E:\src\Clients.js: Support for the experimental syntax 'importAssertions' isn't currently enabled.
Add #babel/plugin-syntax-import-assertions (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-import-assertions) to the 'plugins' section of your Babel config to enable parsing.
Line 1:41: Parsing error: This experimental syntax requires enabling the parser plugin: "importAssertions". (1:41)
I have installed this plugin:
npm install #babel/plugin-syntax-import-assertions --save-dev
Then I created .babelrc.json:
{
"plugins": [
"#babel/plugin-syntax-import-assertions"
]
}
And also added this plugin into package.json:
{
"name": "clients-frontend",
"version": "0.1.0",
"private": true,
"babel": {
"plugins": [
"#babel/plugin-syntax-import-assertions"
]
},
"dependencies": {
"#testing-library/jest-dom": "^5.16.4",
"#testing-library/react": "^13.1.1",
"#testing-library/user-event": "^13.5.0",
"bootstrap": "^5.1.3",
"react": "^18.1.0",
"react-dom": "^18.1.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"devDependencies": {
"#babel/plugin-syntax-import-assertions": "^7.16.7"
}
}
However, I keep getting this error. 😐
react-scripts doesn't load babel configuration by default. Install the following packages
npm i -D customize-cra react-app-rewired
These packages let you customize react-scripts's default configuration for babel so you can use additional plugins
Now, change the scripts in your package.json
"scripts": {
- "start": "react-scripts start",
+ "start": "react-app-rewired start",
- "build": "react-scripts build",
+ "build": "react-app-rewired build",
- "test": "react-scripts test",
+ "test": "react-app-rewired test"
}
Create a file config-overrides.js at the root of your app with the following content
/* config-overrides.js */
/* eslint-disable react-hooks/rules-of-hooks */
const { useBabelRc, override } = require('customize-cra');
module.exports = override(useBabelRc());
Now, create .babelrc at the root of your package
{
"plugins": [
"#babel/plugin-syntax-import-assertions"
]
}
Now, your babel configuration will be loaded correctly. There's another library craco that lets you customize the react-scripts configuration
Try installing the babel-eslint package as well. And, in your .eslintrc.json file add:
{
"parserOptions": {
"babelOptions": {
"parserOpts": {
"plugins": ["importAssertions"]
}
}
}
}
npm install #babel/plugin-syntax-import-assertions --save-dev
babel.config.cjs
module.exports = function (api) {
api.cache(true);
let presets = [];
let plugins = [];
switch (process.env['NODE_ENV']) {
case 'test':
presets = [
[
'#babel/preset-env',
{
targets: {
node: 'current',
},
},
],
];
plugins = [
'babel-plugin-transform-import-meta',
'#babel/plugin-proposal-export-default-from',
'#babel/plugin-syntax-import-assertions'
];
break;
default:
presets = [
[
'#babel/preset-env',
{
modules: false, //Setting this to false will preserve ES services
targets: {
node: 'current',
},
},
],
];
plugins = ['#babel/plugin-syntax-import-assertions'];
break;
}
return {
presets,
plugins,
};
};
If you (like me) use an ejected react-script app, maybe you need to set your configuration in webpack.config.js in the application-scope babel-loader:
// Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
{
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
customize: require.resolve('babel-preset-react-app/webpack-overrides',),
presets: [/* ... */],
plugins: [ // ADD THIS 👇
require.resolve('#babel/plugin-syntax-import-assertions'),
[require.resolve('babel-plugin-direct-import'), {
modules: [
require.resolve('#mui/material'),
require.resolve('#mui/icons-material'),
require.resolve('#mui/lab'),
require.resolve('#mui/base'),
require.resolve('#mui/system'),
],
}],
isEnvDevelopment && shouldUseReactRefresh && require.resolve('react-refresh/babel'),
].filter(Boolean),
// ...
},
},
But NOT here:
// Process any JS outside of the app with Babel.
// Unlike the application JS, we only compile the standard ES features.
{
test: /\.(js|mjs)$/,
exclude: /#babel(?:\/|\\{1,2})runtime/,
// ...

Error when importing JSX files from another package in monorepo with React

I created a simple project to study monorepo. The example consists of three React applications, where one consists of a lib of components with the Storybook and the other two will import the components of this package.
However I am not able to import a component from another package.
When I import a common function, no error occurs.
Github repository
Error message
../components/src/button/Button.js
SyntaxError: /home/thiago/Documentos/Dev/projects/learn-monorepo/packages/components/src/button/Button.js: Support for the experimental syntax 'jsx' isn't currently enabled (7:5):
5 | function Button({label, ...rest}) {
6 | return (
> 7 | <div>
| ^
8 | <button {...rest}>
9 | {label}
10 | </button>
Add #babel/preset-react (https://git.io/JfeDR) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add #babel/plugin-syntax-jsx (https://git.io/vb4yA) to the 'plugins' section to enable parsing.
Button component
function Button({label, ...rest}) {
return (
<div>
<button {...rest}>
{label}
</button>
</div>
);
}
export { Button };
App.js
import React from 'react';
import { Button } from '#project/components';
function App() {
return (
<div>
<h1>Hello World - App 01</h1>
<Button label="Button"/>
</div>
);
}
export default App;
Package.json
{
"name": "project",
"version": "1.0.0",
"main": "build/index.js",
"author": "thiago <thenrique2012#gmail.com>",
"license": "MIT",
"private": true,
"workspaces": {
"packages": ["packages/*"]
},
"babel": {
"presets": [
"#babel/preset-react"
],
"plugins": [
"#babel/plugin-syntax-jsx"
]
},
"scripts": {
"bootstrap": "lerna bootstrap",
"start:app-01": "lerna run --scope #project/app-01 start",
"start:app-02": "lerna run --scope #project/app-02 start",
"start:storybook": "lerna run --scope #project/components storybook",
"start:web": "yarn start:app-01 & yarn start:app-02 & yarn start:storybook"
},
"devDependencies": {
"#babel/plugin-syntax-jsx": "^7.12.1",
"#babel/preset-react": "^7.12.10",
"babel-loader": "8.1.0",
"lerna": "^3.22.1"
}
}
You get error because you have not transpiled your shared react components. There are some (not official) solutions for this with create-react-app. One of them is to use react-app-rewired and customize-cra. Instal lthem as dev-dependency and add a config-overrides.js file to the root of your create-react-app:
var path = require('path')
const { override, babelInclude } = require('customize-cra')
module.exports = function (config, env) {
return Object.assign(
config,
override(
babelInclude([
/* transpile (converting to es5) code in src/ and shared component library */
path.resolve('src'),
path.resolve('../components'),
])
)(config, env)
)
}
Then use react-app-rewired instead of react-scripts in your scripts for start and build of the project:
"scripts": {
"start": "react-app-rewired start",
"build-prod": "react-app-rewired build",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
},

Dynamic import images from folders

Using webpack I can import images from a folder
let i = 0;
const imgs =[];
let img;
for(i=0;i<3;i++){
img = require( '../Resources/OtherProjects/'+props.type+'/'+i+'.jpg' );
imgs.push(img);
}
where props.type gives me a different folder to create an array for each folder in my dynamic component
my question is : there is a better way?
also , there is a way to get how many images are in my folder?
require.context only works for an specific folder , but not for each folder
You can use alias to provide aliases for your srcor any directory in your root folder.
Sample tsconfig.json file
const {alias, configPaths} = require('react-app-rewire-alias')
module.exports = function override(config) {
alias({
...configPaths('tsconfig.paths.json')
})(config)
return config
}
tsconfig.paths.json
/* tsconfig.paths.json */
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"example/*": ["example/src/*"],
"#library/*": ["library/src/*"]
}
}
}
and modify your package.json to this:
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-scripts eject"
}
You may also refer to this for more information.

How do I change `src` folder to something else in create-react-app

I'd like to know if it's possible using react-script to rename src to something else like app folder
You can use react-app-rewired to override react paths configuration.
In my case, I can change the paths in config-overrides.js file
const path = require('path');
module.exports = {
paths: function (paths, env) {
paths.appIndexJs = path.resolve(__dirname, 'mysrc/client.js');
paths.appSrc = path.resolve(__dirname, 'mysrc');
return paths;
},
}
Not sure if this answers your question but I'll give it a shot. My directory structure looks like this:
/root
--/app
----/build
----/public
------index.html
----/src
------index.js
app.js
package.js
My /root/package.json has this in it:
"scripts": {
"build": "cd app && npm run build",
"start": "node app.js",
"serve": "cd app && npm run start"
},
"dependencies": {
"express": "^4.8.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-scripts": "^1.0.17"
},
and my /root/app/package.json looks like this:
"scripts": {
"build": "react-scripts build",
"start": "set PORT=3000 && react-scripts start"
},
"dependencies": {
"react-scripts": "^1.0.17"
}
To run the development version of Reactjs, in the /root I can just npm run serve to serve up the dev version.
I am using node and express, so to run the production version of Reactjs,
in the /root I can just npm run build to create the /root/app/build directory. I have a router that looks like this:
var options = {root : config.siteRoot + '/app/build'};
mainRte.all('/', function(req, res) {
console.log('In mainRte.all Root');
res.sendFile('index.html', options);
});
so when I run /root/app.js in node and surf to "/" it opens up /root/app/public/index.html and then /root/app/index.js.
Hopefully that helps.
react-app-rewired allows for this exact customization.
1
Install react-app-rewired as a dev dependency:
npm install --save-dev react-app-rewired
2
In package.json, change these lines
"scripts": {
"react-start": "react-scripts start",
"react-build": "react-scripts build",
"react-test": "react-scripts test",
...
}
to
"scripts": {
"react-start": "react-app-rewired start",
"react-build": "react-app-rewired build",
"react-test": "react-app-rewired test",
...
}
3
Create a config-overrides.json file in your project directory root with the following contents:
const paths = require('react-scripts/config/paths')
const path = require('path')
// Make the "app" folder be treated as the "src" folder
paths.appSrc = path.resolve(__dirname, 'app')
// Tell the app that "src/index.js" has moved to "app/index.js"
paths.appIndexJs = path.resolve(__dirname, 'app/index.js')
Now your app folder is the new src!
You can also customize many other things, such as the name of the "public" folder:
paths.appPublic = path.resolve(__dirname, 'subfolder/public')
paths.appHtml = path.resolve(__dirname, 'subfolder/public/index.html')
And you can also change the location of package.json and node_modules. See here for the full list.
I know this is an old question but I'm still gonna post my solution since it might help someone.
I got it working by doing the following:
Run npm run eject. This exposes some internal configuration stuff from create-react-app
Open your package.json and edit the respective regexes under jest.collectCoverageFrom and jest.testMatch to match your test path
Alter the paths for appSrc, appIndexJs and testsSetup in the config/paths.js file
T0astBread's answer is nearly perfect, but there's an additional reference to "src" that he missed inside modules.js.
Specifically:
return {
src: paths.appSrc,
};
needs to be changed to
return {
newSrcName: paths.appSrc,
};
This is a great question and a valid scenario for changing this folder name is when migrating old react projects to CRA.
Here's another approach I found that breaks less things:
Create a symlink with:
ls -s ./app src
Then add this in config-overrides.js, to allow webpack to process the symlink:
module.exports = (config, ...rest) => {
return { ...config, resolve: { ...config.resolve, symlinks: false } };
};
Then install react-app-rewired and add this to your package.json:
"start": "react-app-rewired start",
While Cong Dan Luong's answer is correct as far as renaming the folder goes, it will break testing with jest. You need to expand the config-overrides.js module.exports part with the following:
module.exports = {
jest: function(config) {
config.collectCoverageFrom = ['client/**/*.{js,jsx,ts,tsx}', '!client/**/*.d.ts'];
config.testMatch = [
'<rootDir>/client/**/__tests__/**/*.{js,jsx,ts,tsx}',
'<rootDir>/client/**/*.{spec,test}.{js,jsx,ts,tsx}',
];
config.roots = ['<rootDir>/client'];
return config;
},
// The paths config
paths: function(paths, env) {
paths.appIndexJs = path.resolve(__dirname, 'client/index.js');
paths.appSrc = path.resolve(__dirname, 'client');
return paths;
},
};
In my above example I am using 'client' instead of 'src'. npm test now works.
Perhaps a symbolic link might address your reasons for wanting to do this:
ln -s ./src/ ./app
The src folder will remain but you can work with it as if it was the app folder.
If, like me you're using vscode you can also do:
Cmd-shift-p search workspace settings, and add the following:
{
"files.exclude": {
"src/": true
}
}
You could do similarly with other editors
Create file in root of your project, insert this code and run.
const fs = require('fs');
const path = './node_modules/react-scripts/config/paths.js';
const folder = 'app';
fs.readFile(path, 'utf8', (err, data) => {
if (err) throw err;
data = data.replace(/src/g, folder);
fs.writeFile(path, data, 'utf8');
});

Resources