@shark-pepper/create-app 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -9
- package/bin/create-app-cli.js +45 -23
- package/package.json +1 -1
- package/templates/react-app/.babelrc +17 -0
- package/templates/react-app/package.base.json +17 -12
- package/templates/react-app/pnpm-lock.yaml +4367 -2272
- package/templates/react-app/src/App.tsx +17 -2
- package/templates/react-app/src/index.tsx +1 -1
- package/templates/react-app/src/routes/index.tsx +3 -16
- package/templates/react-app/src/styles/App.module.scss +16 -0
- package/templates/react-app/webpack/webpack.common.js +53 -0
- package/templates/react-app/webpack/webpack.dev.js +81 -0
- package/templates/react-app/webpack/webpack.prod.js +84 -0
- package/templates/react-app/src/components/ProtectedRoute.tsx +0 -17
- package/templates/react-app/src/pages/Home.tsx +0 -15
- package/templates/react-app/src/pages/Login.tsx +0 -29
- package/templates/react-app/src/styles/Home.scss +0 -9
- package/templates/react-app/src/styles/Login.scss +0 -16
- package/templates/react-app/webpack.config.js +0 -123
|
@@ -1,5 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import styles from '@/styles/App.module.scss';
|
|
2
2
|
|
|
3
3
|
export default function App() {
|
|
4
|
-
return
|
|
4
|
+
return (
|
|
5
|
+
<div className={styles.app}>
|
|
6
|
+
<svg
|
|
7
|
+
width="112"
|
|
8
|
+
role="img"
|
|
9
|
+
viewBox="0 0 24 24"
|
|
10
|
+
fill="#087ea4"
|
|
11
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
12
|
+
>
|
|
13
|
+
<title>React</title>
|
|
14
|
+
<path d="M14.23 12.004a2.236 2.236 0 0 1-2.235 2.236 2.236 2.236 0 0 1-2.236-2.236 2.236 2.236 0 0 1 2.235-2.236 2.236 2.236 0 0 1 2.236 2.236zm2.648-10.69c-1.346 0-3.107.96-4.888 2.622-1.78-1.653-3.542-2.602-4.887-2.602-.41 0-.783.093-1.106.278-1.375.793-1.683 3.264-.973 6.365C1.98 8.917 0 10.42 0 12.004c0 1.59 1.99 3.097 5.043 4.03-.704 3.113-.39 5.588.988 6.38.32.187.69.275 1.102.275 1.345 0 3.107-.96 4.888-2.624 1.78 1.654 3.542 2.603 4.887 2.603.41 0 .783-.09 1.106-.275 1.374-.792 1.683-3.263.973-6.365C22.02 15.096 24 13.59 24 12.004c0-1.59-1.99-3.097-5.043-4.032.704-3.11.39-5.587-.988-6.38-.318-.184-.688-.277-1.092-.278zm-.005 1.09v.006c.225 0 .406.044.558.127.666.382.955 1.835.73 3.704-.054.46-.142.945-.25 1.44-.96-.236-2.006-.417-3.107-.534-.66-.905-1.345-1.727-2.035-2.447 1.592-1.48 3.087-2.292 4.105-2.295zm-9.77.02c1.012 0 2.514.808 4.11 2.28-.686.72-1.37 1.537-2.02 2.442-1.107.117-2.154.298-3.113.538-.112-.49-.195-.964-.254-1.42-.23-1.868.054-3.32.714-3.707.19-.09.4-.127.563-.132zm4.882 3.05c.455.468.91.992 1.36 1.564-.44-.02-.89-.034-1.345-.034-.46 0-.915.01-1.36.034.44-.572.895-1.096 1.345-1.565zM12 8.1c.74 0 1.477.034 2.202.093.406.582.802 1.203 1.183 1.86.372.64.71 1.29 1.018 1.946-.308.655-.646 1.31-1.013 1.95-.38.66-.773 1.288-1.18 1.87-.728.063-1.466.098-2.21.098-.74 0-1.477-.035-2.202-.093-.406-.582-.802-1.204-1.183-1.86-.372-.64-.71-1.29-1.018-1.946.303-.657.646-1.313 1.013-1.954.38-.66.773-1.286 1.18-1.868.728-.064 1.466-.098 2.21-.098zm-3.635.254c-.24.377-.48.763-.704 1.16-.225.39-.435.782-.635 1.174-.265-.656-.49-1.31-.676-1.947.64-.15 1.315-.283 2.015-.386zm7.26 0c.695.103 1.365.23 2.006.387-.18.632-.405 1.282-.66 1.933-.2-.39-.41-.783-.64-1.174-.225-.392-.465-.774-.705-1.146zm3.063.675c.484.15.944.317 1.375.498 1.732.74 2.852 1.708 2.852 2.476-.005.768-1.125 1.74-2.857 2.475-.42.18-.88.342-1.355.493-.28-.958-.646-1.956-1.1-2.98.45-1.017.81-2.01 1.085-2.964zm-13.395.004c.278.96.645 1.957 1.1 2.98-.45 1.017-.812 2.01-1.086 2.964-.484-.15-.944-.318-1.37-.5-1.732-.737-2.852-1.706-2.852-2.474 0-.768 1.12-1.742 2.852-2.476.42-.18.88-.342 1.356-.494zm11.678 4.28c.265.657.49 1.312.676 1.948-.64.157-1.316.29-2.016.39.24-.375.48-.762.705-1.158.225-.39.435-.788.636-1.18zm-9.945.02c.2.392.41.783.64 1.175.23.39.465.772.705 1.143-.695-.102-1.365-.23-2.006-.386.18-.63.406-1.282.66-1.933zM17.92 16.32c.112.493.2.968.254 1.423.23 1.868-.054 3.32-.714 3.708-.147.09-.338.128-.563.128-1.012 0-2.514-.807-4.11-2.28.686-.72 1.37-1.536 2.02-2.44 1.107-.118 2.154-.3 3.113-.54zm-11.83.01c.96.234 2.006.415 3.107.532.66.905 1.345 1.727 2.035 2.446-1.595 1.483-3.092 2.295-4.11 2.295-.22-.005-.406-.05-.553-.132-.666-.38-.955-1.834-.73-3.703.054-.46.142-.944.25-1.438zm4.56.64c.44.02.89.034 1.345.034.46 0 .915-.01 1.36-.034-.44.572-.895 1.095-1.345 1.565-.455-.47-.91-.993-1.36-1.565z" />
|
|
15
|
+
</svg>
|
|
16
|
+
<p className={styles.thanks}>Thanks for using @shark-pepper/create-app</p>
|
|
17
|
+
<p className={styles.start}>Let's start coding!</p>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
5
20
|
}
|
|
@@ -2,12 +2,12 @@ import React from 'react';
|
|
|
2
2
|
import { createRoot } from 'react-dom/client';
|
|
3
3
|
import { BrowserRouter, useRoutes } from 'react-router-dom';
|
|
4
4
|
import routes from './routes';
|
|
5
|
-
import 'normalize.css';
|
|
6
5
|
|
|
7
6
|
const AppRouter: React.FC = () => useRoutes(routes);
|
|
8
7
|
|
|
9
8
|
const container = document.getElementById('root')!;
|
|
10
9
|
const root = createRoot(container);
|
|
10
|
+
|
|
11
11
|
root.render(
|
|
12
12
|
<React.StrictMode>
|
|
13
13
|
<BrowserRouter>
|
|
@@ -1,24 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import Home from '@/pages/Home';
|
|
4
|
-
import Login from '@/pages/Login';
|
|
5
|
-
import App from '../App';
|
|
1
|
+
import { RouteObject } from 'react-router-dom';
|
|
2
|
+
import App from '@/App';
|
|
6
3
|
|
|
7
4
|
const routes: RouteObject[] = [
|
|
8
5
|
{
|
|
9
6
|
path: '/',
|
|
10
|
-
element:
|
|
11
|
-
<ProtectedRoute>
|
|
12
|
-
<App />
|
|
13
|
-
</ProtectedRoute>
|
|
14
|
-
),
|
|
15
|
-
children: [
|
|
16
|
-
{ index: true, element: <Home /> },
|
|
17
|
-
{ path: 'home', element: <Navigate to="/" replace /> },
|
|
18
|
-
{ path: '*', element: <div>404 Not Found</div> },
|
|
19
|
-
],
|
|
7
|
+
element: <App />,
|
|
20
8
|
},
|
|
21
|
-
{ path: 'login', element: <Login /> },
|
|
22
9
|
];
|
|
23
10
|
|
|
24
11
|
export default routes;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
const __dirname = path.resolve();
|
|
4
|
+
|
|
5
|
+
export default {
|
|
6
|
+
entry: path.resolve(__dirname, './src/index.tsx'),
|
|
7
|
+
output: {
|
|
8
|
+
path: path.resolve(__dirname, './dist'),
|
|
9
|
+
publicPath: '/',
|
|
10
|
+
clean: true,
|
|
11
|
+
},
|
|
12
|
+
resolve: {
|
|
13
|
+
extensions: ['.ts', '.tsx', '.js', '.jsx'],
|
|
14
|
+
alias: {
|
|
15
|
+
'@': path.resolve(__dirname, './src'),
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
module: {
|
|
19
|
+
rules: [
|
|
20
|
+
{
|
|
21
|
+
test: /\.(png|jpe?g|gif|svg|webp)$/i,
|
|
22
|
+
type: 'asset',
|
|
23
|
+
parser: { dataUrlCondition: { maxSize: 10 * 1024 } },
|
|
24
|
+
generator: { filename: 'images/[name].[hash:8][ext]' },
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
test: /\.(woff2?|eot|ttf|otf)$/i,
|
|
28
|
+
type: 'asset/resource',
|
|
29
|
+
generator: { filename: 'fonts/[name].[hash:8][ext]' },
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
optimization: {
|
|
35
|
+
splitChunks: {
|
|
36
|
+
chunks: 'all',
|
|
37
|
+
cacheGroups: {
|
|
38
|
+
reactVendor: {
|
|
39
|
+
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
|
|
40
|
+
name: 'react-vendor',
|
|
41
|
+
chunks: 'all',
|
|
42
|
+
},
|
|
43
|
+
vendor: {
|
|
44
|
+
test: /[\\/]node_modules[\\/]/,
|
|
45
|
+
name: 'vendor',
|
|
46
|
+
chunks: 'all',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
runtimeChunk: 'single',
|
|
51
|
+
moduleIds: 'deterministic',
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { merge } from 'webpack-merge';
|
|
3
|
+
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
4
|
+
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
|
5
|
+
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
|
6
|
+
import ESLintPlugin from 'eslint-webpack-plugin';
|
|
7
|
+
import common from './webpack.common.js';
|
|
8
|
+
|
|
9
|
+
const __dirname = path.resolve();
|
|
10
|
+
|
|
11
|
+
export default merge(common, {
|
|
12
|
+
mode: 'development',
|
|
13
|
+
|
|
14
|
+
output: {
|
|
15
|
+
filename: 'js/[name].js',
|
|
16
|
+
chunkFilename: 'js/[name].chunk.js',
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
module: {
|
|
20
|
+
rules: [
|
|
21
|
+
{
|
|
22
|
+
test: /\.tsx?$/,
|
|
23
|
+
use: [
|
|
24
|
+
{
|
|
25
|
+
loader: 'esbuild-loader',
|
|
26
|
+
options: { loader: 'tsx', target: 'es2017' },
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
exclude: /node_modules/,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
test: /\.module\.scss$/,
|
|
33
|
+
use: [
|
|
34
|
+
'style-loader',
|
|
35
|
+
{
|
|
36
|
+
loader: 'css-loader',
|
|
37
|
+
options: {
|
|
38
|
+
modules: {
|
|
39
|
+
localIdentName: '[name]__[local]___[hash:base64:5]',
|
|
40
|
+
exportLocalsConvention: 'asIs',
|
|
41
|
+
},
|
|
42
|
+
esModule: false,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
'postcss-loader',
|
|
46
|
+
'sass-loader',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
test: /\.scss$/,
|
|
51
|
+
use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
|
|
52
|
+
exclude: /\.module\.scss$/,
|
|
53
|
+
},
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
plugins: [
|
|
58
|
+
new HtmlWebpackPlugin({
|
|
59
|
+
template: path.resolve(__dirname, './public/index.html'),
|
|
60
|
+
inject: 'body',
|
|
61
|
+
minify: false,
|
|
62
|
+
}),
|
|
63
|
+
new ReactRefreshWebpackPlugin(),
|
|
64
|
+
new ForkTsCheckerWebpackPlugin({
|
|
65
|
+
async: true,
|
|
66
|
+
typescript: { configFile: path.resolve(__dirname, './tsconfig.json') },
|
|
67
|
+
}),
|
|
68
|
+
new ESLintPlugin({ extensions: ['ts', 'tsx', 'js', 'jsx'], fix: true, emitWarning: true }),
|
|
69
|
+
].filter(Boolean),
|
|
70
|
+
|
|
71
|
+
devtool: 'cheap-module-source-map',
|
|
72
|
+
|
|
73
|
+
devServer: {
|
|
74
|
+
static: path.resolve(__dirname, './public'),
|
|
75
|
+
compress: true,
|
|
76
|
+
port: 3000,
|
|
77
|
+
hot: true,
|
|
78
|
+
historyApiFallback: true,
|
|
79
|
+
open: true,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { merge } from 'webpack-merge';
|
|
3
|
+
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
4
|
+
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
5
|
+
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
|
6
|
+
import ESLintPlugin from 'eslint-webpack-plugin';
|
|
7
|
+
import common from './webpack.common.js';
|
|
8
|
+
|
|
9
|
+
const __dirname = path.resolve();
|
|
10
|
+
|
|
11
|
+
export default merge(common, {
|
|
12
|
+
mode: 'production',
|
|
13
|
+
|
|
14
|
+
output: {
|
|
15
|
+
filename: 'js/[name].[contenthash:8].js',
|
|
16
|
+
chunkFilename: 'js/[name].[contenthash:8].chunk.js',
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
module: {
|
|
20
|
+
rules: [
|
|
21
|
+
{
|
|
22
|
+
test: /\.tsx?$/,
|
|
23
|
+
use: [
|
|
24
|
+
{
|
|
25
|
+
loader: 'babel-loader',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
exclude: /node_modules/,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
test: /\.module\.scss$/,
|
|
32
|
+
use: [
|
|
33
|
+
MiniCssExtractPlugin.loader,
|
|
34
|
+
{
|
|
35
|
+
loader: 'css-loader',
|
|
36
|
+
options: {
|
|
37
|
+
modules: {
|
|
38
|
+
localIdentName: '[hash:base64:8]',
|
|
39
|
+
exportLocalsConvention: 'asIs',
|
|
40
|
+
},
|
|
41
|
+
esModule: false,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
'postcss-loader',
|
|
45
|
+
'sass-loader',
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
test: /\.scss$/,
|
|
50
|
+
use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'],
|
|
51
|
+
exclude: /\.module\.scss$/,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
plugins: [
|
|
57
|
+
new HtmlWebpackPlugin({
|
|
58
|
+
template: path.resolve(__dirname, './public/index.html'),
|
|
59
|
+
inject: true,
|
|
60
|
+
minify: {
|
|
61
|
+
removeComments: true,
|
|
62
|
+
collapseWhitespace: true,
|
|
63
|
+
removeRedundantAttributes: true,
|
|
64
|
+
useShortDoctype: true,
|
|
65
|
+
removeEmptyAttributes: true,
|
|
66
|
+
removeStyleLinkTypeAttributes: true,
|
|
67
|
+
keepClosingSlash: true,
|
|
68
|
+
minifyJS: true,
|
|
69
|
+
minifyCSS: true,
|
|
70
|
+
minifyURLs: true,
|
|
71
|
+
},
|
|
72
|
+
}),
|
|
73
|
+
new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:8].css' }),
|
|
74
|
+
new ForkTsCheckerWebpackPlugin({
|
|
75
|
+
async: false,
|
|
76
|
+
typescript: {
|
|
77
|
+
configFile: path.resolve(__dirname, './tsconfig.json'),
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
new ESLintPlugin({ extensions: ['ts', 'tsx', 'js', 'jsx'], fix: true, emitWarning: false }),
|
|
81
|
+
].filter(Boolean),
|
|
82
|
+
|
|
83
|
+
devtool: 'source-map',
|
|
84
|
+
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
import { Navigate, useLocation } from 'react-router-dom';
|
|
3
|
-
|
|
4
|
-
interface ProtectedRouteProps {
|
|
5
|
-
children: ReactNode;
|
|
6
|
-
redirectPath?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export default function ProtectedRoute({ children, redirectPath = '/login' }: ProtectedRouteProps) {
|
|
10
|
-
const location = useLocation();
|
|
11
|
-
const isAuth = localStorage.getItem('isAuth') === 'true';
|
|
12
|
-
|
|
13
|
-
if (!isAuth) {
|
|
14
|
-
return <Navigate to={redirectPath} state={{ from: location }} replace />;
|
|
15
|
-
}
|
|
16
|
-
return children;
|
|
17
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import '../styles/Home.scss';
|
|
2
|
-
|
|
3
|
-
export default function Home() {
|
|
4
|
-
const handleClick = () => {
|
|
5
|
-
localStorage.setItem('isAuth', 'false');
|
|
6
|
-
window.location.reload();
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
return (
|
|
10
|
-
<div className="home">
|
|
11
|
-
<h1>Home Page</h1>
|
|
12
|
-
<button onClick={handleClick}>Logout</button>
|
|
13
|
-
</div>
|
|
14
|
-
);
|
|
15
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { Navigate, useLocation, useNavigate } from 'react-router-dom';
|
|
2
|
-
import '../styles/Login.scss';
|
|
3
|
-
|
|
4
|
-
export default function Login() {
|
|
5
|
-
const navigate = useNavigate();
|
|
6
|
-
const location = useLocation();
|
|
7
|
-
|
|
8
|
-
const from = (location.state as any)?.from?.pathname || '/';
|
|
9
|
-
const isAuth = localStorage.getItem('isAuth') === 'true';
|
|
10
|
-
|
|
11
|
-
if (isAuth) {
|
|
12
|
-
return <Navigate to="/" replace />;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const handleLogin = () => {
|
|
16
|
-
localStorage.setItem('isAuth', 'true');
|
|
17
|
-
navigate(from, { replace: true });
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
return (
|
|
21
|
-
<div className="container">
|
|
22
|
-
<div className="welcome">
|
|
23
|
-
<h1>Thanks for using @shark-pepper/create-app</h1>
|
|
24
|
-
<h2>Let's start coding!</h2>
|
|
25
|
-
</div>
|
|
26
|
-
<button onClick={handleLogin}>Login Now</button>
|
|
27
|
-
</div>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
@mixin flex-center($direction: row) {
|
|
2
|
-
display: flex;
|
|
3
|
-
flex-direction: $direction;
|
|
4
|
-
justify-content: center;
|
|
5
|
-
align-items: center;
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
.container {
|
|
9
|
-
height: 100vh;
|
|
10
|
-
@include flex-center(column);
|
|
11
|
-
.welcome {
|
|
12
|
-
width: 750px;
|
|
13
|
-
margin-bottom: 40px;
|
|
14
|
-
@include flex-center(column);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import HtmlWebpackPlugin from 'html-webpack-plugin';
|
|
3
|
-
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|
4
|
-
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
|
|
5
|
-
import ESLintPlugin from 'eslint-webpack-plugin';
|
|
6
|
-
|
|
7
|
-
const __dirname = path.resolve(); // ESModule 下获取 __dirname
|
|
8
|
-
|
|
9
|
-
const isProd = process.env.NODE_ENV === 'production';
|
|
10
|
-
|
|
11
|
-
export default {
|
|
12
|
-
mode: isProd ? 'production' : 'development',
|
|
13
|
-
|
|
14
|
-
entry: path.resolve(__dirname, 'src/index.tsx'),
|
|
15
|
-
|
|
16
|
-
output: {
|
|
17
|
-
path: path.resolve(__dirname, 'dist'),
|
|
18
|
-
filename: isProd ? 'js/[name].[contenthash:8].js' : 'js/[name].js',
|
|
19
|
-
chunkFilename: isProd ? 'js/[name].[contenthash:8].chunk.js' : 'js/[name].chunk.js',
|
|
20
|
-
publicPath: '/',
|
|
21
|
-
clean: true, // 自动清理 dist 目录
|
|
22
|
-
},
|
|
23
|
-
|
|
24
|
-
resolve: {
|
|
25
|
-
extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
|
|
26
|
-
alias: {
|
|
27
|
-
'@': path.resolve(__dirname, 'src'),
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
module: {
|
|
32
|
-
rules: [
|
|
33
|
-
// TS / TSX
|
|
34
|
-
{
|
|
35
|
-
test: /\.tsx?$/,
|
|
36
|
-
use: [
|
|
37
|
-
{
|
|
38
|
-
loader: 'ts-loader',
|
|
39
|
-
options: { transpileOnly: true }, // 类型检查交给 ForkTsCheckerWebpackPlugin
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
exclude: /node_modules/,
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
// CSS / SCSS
|
|
46
|
-
{
|
|
47
|
-
test: /\.css$/i,
|
|
48
|
-
use: [
|
|
49
|
-
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
|
|
50
|
-
'css-loader',
|
|
51
|
-
'postcss-loader',
|
|
52
|
-
],
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
test: /\.s[ac]ss$/i,
|
|
56
|
-
use: [
|
|
57
|
-
isProd ? MiniCssExtractPlugin.loader : 'style-loader',
|
|
58
|
-
'css-loader',
|
|
59
|
-
'postcss-loader',
|
|
60
|
-
'sass-loader',
|
|
61
|
-
],
|
|
62
|
-
},
|
|
63
|
-
|
|
64
|
-
// 静态资源(图片 / 字体)
|
|
65
|
-
{
|
|
66
|
-
test: /\.(png|jpe?g|gif|svg|webp)$/i,
|
|
67
|
-
type: 'asset',
|
|
68
|
-
parser: { dataUrlCondition: { maxSize: 10 * 1024 } },
|
|
69
|
-
generator: { filename: 'images/[name].[hash:8][ext]' },
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
test: /\.(woff2?|eot|ttf|otf)$/i,
|
|
73
|
-
type: 'asset/resource',
|
|
74
|
-
generator: { filename: 'fonts/[name].[hash:8][ext]' },
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
plugins: [
|
|
80
|
-
new HtmlWebpackPlugin({
|
|
81
|
-
template: path.resolve(__dirname, 'public/index.html'),
|
|
82
|
-
inject: 'body',
|
|
83
|
-
minify: isProd ? { removeComments: true, collapseWhitespace: true } : false,
|
|
84
|
-
}),
|
|
85
|
-
isProd && new MiniCssExtractPlugin({ filename: 'css/[name].[contenthash:8].css' }),
|
|
86
|
-
new ForkTsCheckerWebpackPlugin({
|
|
87
|
-
async: !isProd,
|
|
88
|
-
typescript: { configFile: path.resolve(__dirname, 'tsconfig.json') },
|
|
89
|
-
}),
|
|
90
|
-
new ESLintPlugin({ extensions: ['ts', 'tsx', 'js', 'jsx'], fix: true, emitWarning: !isProd }),
|
|
91
|
-
].filter(Boolean),
|
|
92
|
-
|
|
93
|
-
devtool: isProd ? 'source-map' : 'eval-cheap-module-source-map',
|
|
94
|
-
|
|
95
|
-
devServer: {
|
|
96
|
-
static: path.resolve(__dirname, 'public'),
|
|
97
|
-
compress: true,
|
|
98
|
-
port: 3000,
|
|
99
|
-
hot: true,
|
|
100
|
-
historyApiFallback: true,
|
|
101
|
-
open: true,
|
|
102
|
-
},
|
|
103
|
-
|
|
104
|
-
optimization: {
|
|
105
|
-
splitChunks: {
|
|
106
|
-
chunks: 'all',
|
|
107
|
-
cacheGroups: {
|
|
108
|
-
reactVendor: {
|
|
109
|
-
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
|
|
110
|
-
name: 'react-vendor',
|
|
111
|
-
chunks: 'all',
|
|
112
|
-
},
|
|
113
|
-
vendor: {
|
|
114
|
-
test: /[\\/]node_modules[\\/]/,
|
|
115
|
-
name: 'vendor',
|
|
116
|
-
chunks: 'all',
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
},
|
|
120
|
-
runtimeChunk: 'single',
|
|
121
|
-
moduleIds: 'deterministic',
|
|
122
|
-
},
|
|
123
|
-
};
|