As a command-line tool officially maintained by React, create-react-app (CRA for short) makes it extremely easy to create fully-configured React applications, helping users quickly enter React development. Its biggest shortcoming is that the application created cannot be customized as desired. If you want to customize, you can only eject . And eject means that all the configuration of the application is maintained by the user, which is tedious and prohibitive.
Now, with wuzzle , we can arbitrarily customize the React app created by CRA without ejecting.
View CRA webpack configuration without eject
First, use CRA to create a demo application that supports TypeScript (if you are not used to using TS to remove parameters --template typescript
):
$ npx create-react-app --template typescript demo
# ...
$ cd demo
Install wuzzle:
$ npm i -D wuzzle
Open package.json
edit scripts
mount wuzzle:
"scripts": {
- "start": "react-scripts start",
+ "start": "wuzzle react-scripts start",
- "build": "react-scripts build",
+ "build": "wuzzle react-scripts build",
},
Now, run start
or build
script with the parameters --dry-run
to directly view the webpack configuration used inside the CRA:
$ npm run build -- --dry-run
# ...
@wuzzle/cli:applyConfig Webpack config with difference: {
# ...
devtool: # ...
entry: # ...
output: #...
cache: #...
resolve: # ...
module: # ...
plugins: # ...
# ...
}
Introduce less and use antd without eject
On style files, CRA applications support css, scss/sass, but not less. If you want to fully use antd and make theme modifications, you need to introduce less in the webpack configuration. Go back to --dry-run
run the result and take a closer look at the module field:
$ npm run build -- --dry-run
# ...
@wuzzle/cli:applyConfig Webpack config with difference: {
# ...
module: {
# ...
rules: [
# ...
{
oneOf: [
# ...
{
test: /\.(scss|sass)$/,
exclude: /\.module\.(scss|sass)$/,
use: [
{
loader: '.../mini-css-extract-plugin/dist/loader.js',
options: {}
},
{
loader: '.../css-loader/...',
options: # ...
},
{
loader: '.../postcss-loader/...',
options: # ...
},
{
loader: '.../resolve-url-loader/...',
options: # ...
},
{
loader: '.../sass-loader/...',
options: # ...
}
],
},
{
test: /\.module\.(scss|sass)$/,
use: [
{
loader: '.../mini-css-extract-plugin/dist/loader.js',
options: # ...
},
{
loader: '.../css-loader/...',
options: # ...
},
{
loader: '.../postcss-loader/...',
options: # ...
},
{
loader: '.../resolve-url-loader/...',
options: # ...
},
{
loader: '.../sass-loader/...',
options: # ...
}
]
},
# ...
]
}
]
},
# ...
}
It is not difficult to find that the configuration method of sass is very close to that of less. With a little modification, replace sass-loader with less-loader and remove resolve-url-loader to achieve the goal.
Install the dependencies required to configure less:
npm i -D less less-loader
Then, create a file next to package.json
wuzzle.config.js
to modify the webpack configuration used inside the CRA. Here you can use the modification help method provided by wuzzle to reduce the workload:
const appPaths = require('react-scripts/config/paths');
const { deleteUseItem, firstRule, firstUseItem, replaceUseItem } = require('wuzzle');
module.exports = (webpackConfig, webpack, wuzzleContext) => {
const { commandArgs } = wuzzleContext;
if (commandArgs[0] === 'start' || commandArgs[0] === 'build') {
// Replace sass-loader with less-loader to support .less files
const lessOptions = { javascriptEnabled: true };
const scssRuleQuery = { file: { dir: appPaths.appSrc, base: 'index.scss' } };
const lessRule = firstRule(webpackConfig, scssRuleQuery);
Object.assign(lessRule, { test: /\.(less)$/, exclude: /\.module\.less$/ });
deleteUseItem(lessRule, { loader: 'resolve-url-loader' });
replaceUseItem(
lessRule,
{ loader: 'sass-loader' },
{ loader: 'less-loader', options: { sourceMap: true, lessOptions } }
);
firstUseItem(lessRule, { loader: 'css-loader' }).options.importLoaders = 2;
const scssModuleRuleQuery = { file: { dir: appPaths.appSrc, base: 'index.module.scss' } };
const lessModuleRule = firstRule(webpackConfig, scssModuleRuleQuery);
Object.assign(lessModuleRule, { test: /\.module\.less$/ });
deleteUseItem(lessModuleRule, { loader: 'resolve-url-loader' });
replaceUseItem(
lessModuleRule,
{ loader: 'sass-loader' },
{ loader: 'less-loader', options: { sourceMap: true, lessOptions } }
);
firstUseItem(lessModuleRule, { loader: 'css-loader' }).options.importLoaders = 2;
}
};
After that, change all .css
file suffixes to .less
:
- 把
index.css
重命名index.less
,index.tsx
把import './index.css';
改为import './index.less';
。 - 把
App.css
重命名App.less
,App.tsx
把import './App.css';
改为import './App.less';
。
Install antd:
$ npm i -S antd
In index.less
introduce the antd style file:
-body {
- ...
-}
-code {
- ...
-}
+@import '~antd/dist/antd.less';
If you want to modify the antd theme, you can go back to wuzzle.config.js
lessOptions
and add the modifyVars
field in ---6e4beec2179e09b104827bbec4156cad---, for example:
- const lessOptions = { javascriptEnabled: true };
+ const lessOptions = {
+ javascriptEnabled: true,
+ modifyVars: { '@primary-color': '#1da57a' },
+ };
Now, run the start
or build
script to see the effect of introducing less and using antd in the CRA application:
$ npm start
Starting the development server...
Compiled successfully!
You can now view demo in the browser.
Local: http://localhost:3000
On Your Network: http://192.168.100.24:3000
Note that the development build is not optimized.
To create a production build, use npm run build.
webpack compiled successfully
No issues found.
Adjust CRA test configuration without eject
Internally, CRA test is packaged based on jest, not webpack. For jest, wuzzle provides two ways to customize the configuration:
- Replacing the compilation of jest itself with the compatible wepback compilation, and the user modifies the webpack compilation configuration.
- Continue to use jest's own compilation, and users modify the jest compilation configuration.
Let's see how to introduce less to maintain test
script compatibility through these two methods.
method 1
Go back to package.json
, edit scripts
to test
and mount the script on wuzzle:
"scripts": {
- "test": "react-scripts test",
+ "test": "wuzzle react-scripts test",
},
Then, run the test
--dry-run
to view the configuration of the webpack compiled instead of jest itself:
$ npm test -- --dry-run
# ...
@wuzzle/cli:applyConfig Webpack config with difference: {
# ...
module: {
rules: [
{
test: /\.(js|jsx|mjs|cjs|ts|tsx)$/,
exclude: /node_modules/,
use: # ...
},
{
test: /\.css$/,
exclude: /node_modules/,
use: [
{
loader: '.../null-loader/...'
}
]
},
{
test: /\.svg$/,
exclude: /node_modules/,
use: # ...
},
{
exclude: [ /\.(js|jsx|mjs|cjs|ts|tsx|json|css|svg)$/, /node_modules/ ],
use: # ...
}
]
},
# ...
}
It can be found that as long as the matching of the .less
file is added to the css configuration, and the matching of the .less
configuration.
Go back to wuzzle.config.js
and modify the configuration of webpack compilation that replaces jest's own compilation:
module.exports = (webpackConfig, webpack, wuzzleContext) => {
const { commandArgs } = wuzzleContext;
// ...
+ if (commandArgs[0] === 'test') {
+ const cssRule = firstRule(webpackConfig, { file: 'index.css' });
+ cssRule.test = [cssRule.test, /\.less$/];
+
+ const fallbackRule = firstRule(webpackConfig, { file: 'index.fallback' });
+ fallbackRule.exclude.push(/\.less$/);
+ }
};
Now, run the test
script to see the effect of introducing less in CRA test:
$ npm test
# ...
File 'src/setupTests.ts' compiled.
File 'src/App.test.tsx' compiled.
File 'src/App.tsx' compiled.
File 'src/App.less' compiled.
File 'src/logo.svg' compiled.
PASS src/App.test.tsx (10.183 s)
✓ renders learn react link (40 ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.005 s
Ran all test suites related to changed files.
Method 2
Go back to package.json
, edit again scripts
test
script add parameters --no-webpack
close webpack
"scripts": {
- "test": "wuzzle react-scripts test",
+ "test": "wuzzle react-scripts test --no-webpack",
},
Then, run the test
script through the parameter --dry-run
to view the configuration compiled by jest itself:
$ npm test -- --dry-run
# ...
@wuzzle/cli:applyConfig Jest config with difference: {
# ...
transform: [
[
'^.+\\.(js|jsx|mjs|cjs|ts|tsx)$',
'.../node_modules/react-scripts/config/jest/babelTransform.js',
{}
],
[
'^.+\\.css$',
'.../node_modules/react-scripts/config/jest/cssTransform.js',
{}
],
[
'^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)',
'.../node_modules/react-scripts/config/jest/fileTransform.js',
{}
]
],
# ...
}
This configuration is slightly different from the user configuration of jest. It is the configuration used internally by jest. The structure refers toProjectConfig . Similar to method 1, just add the matching pair .less
file in the css configuration, and remove the matching pair .less
file in the bottom configuration.
Go back to wuzzle.config.js
, first create a new object and put the original direct export method into the modify
field export:
module.exports = {
modify(webpackConfig, webpack, wuzzleContext) {
// Place the directly exported top-level function here.
},
};
After that, continue to wuzzle.config.js
add jest
field to modify the jest compilation configuration:
module.exports = {
modify(webpackConfig, webpack, wuzzleContext) { // ...
},
+ jest(jestConfig, jestInfo, wuzzleContext) {
+ for (const transformItem of jestConfig.transform) {
+ const fileRegExp = new RegExp(transformItem[0]);
+ if (fileRegExp.test('index.css')) {
+ transformItem[0] = '^.+\\.(css|less)$';
+ }
+ if (fileRegExp.test('index.fallback')) {
+ transformItem[0] = '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|less|json)$)';
+ }
+ }
+ },
};
Now, run the test
script to see the same effect of introducing less in CRA test. The difference is that performance may be better because webpack compilation is turned off.
Further customization
Up to this point, all the scripts of the CRA application have been customized, and the eject
script is no longer needed, and can be edited package.json
:
"scripts": {
- "eject": "react-scripts eject",
}
In real projects, SSR (server-side rendering), E2E (end-to-end testing), and in-depth configuration of eslint may also be used. For how to use CRA and wuzzle to further build a real application, you can refer to the official examplee2e/.../react-scripts .
write at the end
At present, the example project in the article has been included in wuzzle-blog/.../demo , readers and friends can open the reference as needed. If you have any questions or ideas, please leave a message. In addition, if you have any questions or ideas about wuzzle, you are welcome to create an issue in wuzzle/issues , both in Chinese and English. If you are interested and have time to contribute code, please submit PR. For details, please refer to the development guide . Finally, if you think the gadget is helpful, you can order a small ⭐️ in the GitHub repo wuzzle to compare your heart.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。