react简单模版
This commit is contained in:
commit
a4d18ff768
|
@ -0,0 +1,17 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
// Allow importing core-js in entrypoint and use browserlist to select polyfills
|
||||
useBuiltIns: 'entry',
|
||||
// Set the corejs version we are using to avoid warnings in console
|
||||
corejs: 3,
|
||||
// Exclude transforms that make all code slower
|
||||
exclude: ['transform-typeof-symbol'],
|
||||
},
|
||||
],
|
||||
['@babel/preset-react', { useBuiltIns: true }],
|
||||
],
|
||||
plugins: [],
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
node: true,
|
||||
es6: true,
|
||||
},
|
||||
parser: '@babel/eslint-parser',
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecamVersion: 6,
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
extends: ['eslint:recommended', 'plugin:react/recommended', 'prettier'],
|
||||
plugins: ['@babel', 'prettier'],
|
||||
rules: {
|
||||
'prettier/prettier': 'error',
|
||||
},
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,21 @@
|
|||
module.exports = {
|
||||
printWidth: 80, //单行长度
|
||||
tabWidth: 2, //缩进长度
|
||||
useTabs: false, //使用空格代替tab缩进
|
||||
semi: false, //句末使用分号
|
||||
singleQuote: true, //使用单引号
|
||||
quoteProps: 'as-needed', //仅在必需时为对象的key添加引号
|
||||
jsxSingleQuote: true, // jsx中使用单引号
|
||||
trailingComma: 'es5', //多行时尽可能打印尾随逗号
|
||||
bracketSpacing: true, //在对象前后添加空格-eg: { foo: bar }
|
||||
jsxBracketSameLine: true, //多属性html标签的‘>’折行放置
|
||||
arrowParens: 'always', //单参数箭头函数参数周围使用圆括号-eg: (x) => x
|
||||
requirePragma: false, //无需顶部注释即可格式化
|
||||
insertPragma: false, //在已被preitter格式化的文件顶部加上标注
|
||||
proseWrap: 'preserve',
|
||||
htmlWhitespaceSensitivity: 'ignore', //对HTML全局空白不敏感
|
||||
vueIndentScriptAndStyle: false, //不对vue中的script及style标签缩进
|
||||
endOfLine: 'lf', //结束行形式
|
||||
embeddedLanguageFormatting: 'auto', //对引用代码进行格式化
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
module.exports = {
|
||||
prettier: false,
|
||||
svgo: false,
|
||||
svgoConfig: {
|
||||
plugins: [{ removeViewBox: false }],
|
||||
},
|
||||
titleProp: true,
|
||||
ref: true,
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,76 @@
|
|||
{
|
||||
"name": "react-template",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"serve": "webpack serve --open --config webpack.dev.js",
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"pre-commit": "lint-staged"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "zhouxhere",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"core-js": "^3.20.2",
|
||||
"moment": "^2.29.1",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-redux": "^7.2.6",
|
||||
"react-router-dom": "^6.2.1",
|
||||
"redux": "^4.1.2",
|
||||
"redux-thunk": "^2.4.1",
|
||||
"regenerator-runtime": "^0.13.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.7",
|
||||
"@babel/eslint-parser": "^7.16.5",
|
||||
"@babel/eslint-plugin": "^7.16.5",
|
||||
"@babel/preset-env": "^7.16.7",
|
||||
"@babel/preset-react": "^7.16.7",
|
||||
"@svgr/webpack": "^6.1.2",
|
||||
"babel-loader": "^8.2.3",
|
||||
"babel-preset-react-app": "^10.0.1",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.4.0",
|
||||
"css-loader": "^6.5.1",
|
||||
"css-minimizer-webpack-plugin": "^3.3.1",
|
||||
"dotenv": "^10.0.0",
|
||||
"dotenv-webpack": "^7.0.3",
|
||||
"eslint": "^8.6.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-webpack-plugin": "^3.1.1",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"husky": "^7.0.4",
|
||||
"less": "^4.1.2",
|
||||
"less-loader": "^10.2.0",
|
||||
"lint-staged": "^12.1.7",
|
||||
"postcss": "^8.4.5",
|
||||
"postcss-flexbugs-fixes": "^5.0.2",
|
||||
"postcss-loader": "^6.2.1",
|
||||
"postcss-normalize": "^10.0.1",
|
||||
"postcss-preset-env": "^7.1.0",
|
||||
"prettier": "^2.5.1",
|
||||
"react-dev-utils": "^12.0.0",
|
||||
"react-refresh": "^0.11.0",
|
||||
"resolve-url-loader": "^4.0.0",
|
||||
"source-map-loader": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"terser-webpack-plugin": "^5.3.0",
|
||||
"webpack": "^5.65.0",
|
||||
"webpack-cli": "^4.9.1",
|
||||
"webpack-dev-server": "^4.7.1",
|
||||
"webpack-manifest-plugin": "^4.0.2",
|
||||
"webpack-merge": "^5.8.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.{js,jsx,vue}": [
|
||||
"prettier --write",
|
||||
"eslint --fix",
|
||||
"git add"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
module.exports = {
|
||||
plugins: [
|
||||
'postcss-flexbugs-fixes',
|
||||
[
|
||||
'postcss-preset-env',
|
||||
{
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 3,
|
||||
},
|
||||
],
|
||||
'postcss-normalize',
|
||||
],
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<link rel="manifest" href="manifest.json" />
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
Binary file not shown.
After Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
|
@ -0,0 +1,16 @@
|
|||
import 'core-js'
|
||||
import 'regenerator-runtime'
|
||||
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import Router from './router'
|
||||
import { Provider } from 'react-redux'
|
||||
import store from './store'
|
||||
|
||||
const element = (
|
||||
<Provider store={store}>
|
||||
<Router />
|
||||
</Provider>
|
||||
)
|
||||
|
||||
ReactDOM.render(element, document.getElementById('root'))
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { Outlet } from 'react-router-dom'
|
||||
|
||||
const BasicLayout = () => {
|
||||
const count = useSelector((state) => state.count)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>basic layout</h2>
|
||||
<p>count:{count}</p>
|
||||
<Outlet />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default BasicLayout
|
|
@ -0,0 +1,24 @@
|
|||
import React, { useCallback } from 'react'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
const Home = () => {
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const addCount = useCallback(() => {
|
||||
dispatch({ type: 'ADD_COUNT' })
|
||||
}, [dispatch])
|
||||
|
||||
const delCount = useCallback(() => {
|
||||
dispatch({ type: 'DEL_COUNT' })
|
||||
}, [dispatch])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>home page</h3>
|
||||
<button onClick={addCount}>add</button>
|
||||
<button onClick={delCount}>del</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react'
|
||||
import { BrowserRouter, Route, Routes } from 'react-router-dom'
|
||||
import BasicLayout from '../layout/basic'
|
||||
import Home from '../page/home'
|
||||
|
||||
const Router = () => {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
<Route path='/' element={<BasicLayout />}>
|
||||
<Route element={<Home />} index />
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
|
||||
export default Router
|
|
@ -0,0 +1,39 @@
|
|||
// 将 redux 数据 持久化 至 localstorage
|
||||
const storageEnhancer = (next) => (reducer) => {
|
||||
let store = next(reducer)
|
||||
|
||||
let persistedState
|
||||
let initialState = store.getState()
|
||||
|
||||
try {
|
||||
persistedState = JSON.parse(localStorage.getItem('redux'))
|
||||
if (persistedState && initialState) {
|
||||
Object.keys(initialState).forEach((key) => {
|
||||
if (persistedState[key]) {
|
||||
initialState[key] = persistedState[key]
|
||||
}
|
||||
})
|
||||
store = next(reducer, initialState)
|
||||
}
|
||||
if (initialState) {
|
||||
localStorage.setItem('redux', JSON.stringify(initialState))
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to retrieve initialize state from localStorage:', e)
|
||||
}
|
||||
|
||||
// 主要代码
|
||||
store.subscribe(async function () {
|
||||
const state = await store.getState()
|
||||
|
||||
try {
|
||||
localStorage.setItem('redux', JSON.stringify(state))
|
||||
} catch (e) {
|
||||
console.warn('Unable to persist state to localStorage:', e)
|
||||
}
|
||||
})
|
||||
|
||||
return store
|
||||
}
|
||||
|
||||
export default storageEnhancer
|
|
@ -0,0 +1,17 @@
|
|||
import { createStore, combineReducers, applyMiddleware, compose } from 'redux'
|
||||
import thunk from 'redux-thunk'
|
||||
import storageEnhancer from './enhancer/storage'
|
||||
import loggerMiddleware from './middleware/logger'
|
||||
import countReducer from './reducer/count'
|
||||
import todoReducer from './reducer/todo'
|
||||
|
||||
const reducer = combineReducers({
|
||||
todo: todoReducer,
|
||||
count: countReducer,
|
||||
})
|
||||
const middlewareEnhancer = applyMiddleware(loggerMiddleware, thunk)
|
||||
const composedEnhancers = compose(middlewareEnhancer, storageEnhancer)
|
||||
|
||||
const store = createStore(reducer, composedEnhancers)
|
||||
|
||||
export default store
|
|
@ -0,0 +1,10 @@
|
|||
const loggerMiddleware = (store) => (next) => (action) => {
|
||||
console.group(action.type)
|
||||
console.info('dispatching', action)
|
||||
let result = next(action)
|
||||
console.log('next state', store.getState())
|
||||
console.groupEnd()
|
||||
return result
|
||||
}
|
||||
|
||||
export default loggerMiddleware
|
|
@ -0,0 +1,22 @@
|
|||
import { storeTool } from '../../util/store'
|
||||
|
||||
const addCount = (countState) => {
|
||||
return countState + 1
|
||||
}
|
||||
|
||||
const setCount = (countState, param) => {
|
||||
return param
|
||||
}
|
||||
|
||||
const delCount = (countState) => {
|
||||
return countState <= 0 ? 0 : countState - 1
|
||||
}
|
||||
|
||||
const countReducer = storeTool.createReducer(0, {
|
||||
ADD_COUNT: addCount,
|
||||
SET_COUNT: setCount,
|
||||
DEL_COUNT: delCount,
|
||||
})
|
||||
|
||||
export const countActions = { addCount, setCount, delCount }
|
||||
export default countReducer
|
|
@ -0,0 +1,30 @@
|
|||
import { storeTool } from '../../util/store'
|
||||
|
||||
const addTodo = (todoState, param) => {
|
||||
const newTodo = todoState.concat(param)
|
||||
return newTodo
|
||||
}
|
||||
|
||||
const editTodo = (todoState, param) => {
|
||||
const _index = todoState.findIndex((item) => item.id === param.id)
|
||||
if (_index < 0) return newTodo
|
||||
const newTodo = storeTool.updateItemInArray(todoState, _index, (item) => {
|
||||
return storeTool.updateObject(item, param)
|
||||
})
|
||||
return newTodo
|
||||
}
|
||||
|
||||
const delTodo = (todoState, param) => {
|
||||
const _index = todoState.findIndex((item) => item.id === param.id)
|
||||
if (_index < 0) return newTodo
|
||||
const newTodo = todoState.splice(_index, 1)
|
||||
return newTodo
|
||||
}
|
||||
|
||||
const todoReducer = storeTool.createReducer([{ id: 1, name: 'hello' }], {
|
||||
ADD_TODO: addTodo,
|
||||
EDIT_TODO: editTodo,
|
||||
DEL_TODO: delTodo,
|
||||
})
|
||||
|
||||
export default todoReducer
|
|
@ -0,0 +1,25 @@
|
|||
import axios from 'axios'
|
||||
|
||||
const defaultConfig = {
|
||||
baseURL: process.env.BASE_URL,
|
||||
timeout: 5 * 1000,
|
||||
}
|
||||
|
||||
const requestHandler = (config) => {
|
||||
return config
|
||||
}
|
||||
|
||||
const responseHandler = (response) => {
|
||||
return response
|
||||
}
|
||||
|
||||
const errorHandler = (error) => {
|
||||
return Promise.reject(error)
|
||||
}
|
||||
|
||||
const request = axios.create(defaultConfig)
|
||||
|
||||
request.interceptors.request.use(requestHandler, errorHandler)
|
||||
request.interceptors.response.use(responseHandler, errorHandler)
|
||||
|
||||
export default request
|
|
@ -0,0 +1,28 @@
|
|||
const updateObject = (oldVal, newVal) => {
|
||||
return Object.assign({}, oldVal, newVal)
|
||||
}
|
||||
|
||||
const updateItemInArray = (array, index, updateCallback) => {
|
||||
const updatedArray = array.map((_item, _index) => {
|
||||
if (_index !== index) {
|
||||
return _item
|
||||
}
|
||||
|
||||
const updatedItem = updateCallback(_item)
|
||||
return updatedItem
|
||||
})
|
||||
|
||||
return updatedArray
|
||||
}
|
||||
|
||||
const createReducer = (initialState, handlers) => {
|
||||
return function reducer(state = initialState, action) {
|
||||
if (Object.prototype.hasOwnProperty.call(handlers, action.type)) {
|
||||
return handlers[action.type](state, action)
|
||||
} else {
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const storeTool = { createReducer, updateObject, updateItemInArray }
|
|
@ -0,0 +1,179 @@
|
|||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin')
|
||||
const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
|
||||
const ESLintPlugin = require('eslint-webpack-plugin')
|
||||
const Dotenv = require('dotenv-webpack')
|
||||
|
||||
module.exports = {
|
||||
entry: './src/index.js',
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
assetModuleFilename: 'static/media/[name].[hash][ext]',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: [/\.avif$/],
|
||||
type: 'asset',
|
||||
mimetype: 'image/avif',
|
||||
parser: {
|
||||
dataUrlCondition: {
|
||||
maxSize: 1024 * 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
type: 'asset',
|
||||
parser: {
|
||||
dataUrlCondition: {
|
||||
maxSize: 1024 * 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'asset',
|
||||
resourceQuery: /url/,
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
issuer: {
|
||||
and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
|
||||
},
|
||||
use: [require.resolve('@svgr/webpack')],
|
||||
},
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
exclude: /(node_modules|bower_components)/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
cacheDirectory: true,
|
||||
cacheCompression: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: /\.module\.css$/,
|
||||
use: [
|
||||
require.resolve('style-loader'),
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: {
|
||||
mode: 'icss',
|
||||
},
|
||||
},
|
||||
},
|
||||
require.resolve('postcss-loader'),
|
||||
],
|
||||
sideEffects: true,
|
||||
},
|
||||
{
|
||||
test: /\.module\.css$/,
|
||||
use: [
|
||||
require.resolve('style-loader'),
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 1,
|
||||
modules: true,
|
||||
},
|
||||
},
|
||||
require.resolve('postcss-loader'),
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
exclude: /\.module\.less$/,
|
||||
use: [
|
||||
require.resolve('style-loader'),
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 3,
|
||||
modules: {
|
||||
mode: 'icss',
|
||||
},
|
||||
},
|
||||
},
|
||||
require.resolve('postcss-loader'),
|
||||
require.resolve('less-loader'),
|
||||
],
|
||||
sideEffects: true,
|
||||
},
|
||||
{
|
||||
test: /\.module\.less$/,
|
||||
use: [
|
||||
require.resolve('style-loader'),
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
importLoaders: 3,
|
||||
modules: true,
|
||||
},
|
||||
},
|
||||
require.resolve('postcss-loader'),
|
||||
require.resolve('less-loader'),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules'],
|
||||
extensions: [
|
||||
'web.mjs',
|
||||
'mjs',
|
||||
'web.js',
|
||||
'js',
|
||||
'web.ts',
|
||||
'ts',
|
||||
'web.tsx',
|
||||
'tsx',
|
||||
'json',
|
||||
'web.jsx',
|
||||
'jsx',
|
||||
'...',
|
||||
],
|
||||
alias: {
|
||||
'react-native': 'react-native-web',
|
||||
src: path.resolve(__dirname, 'src'),
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
inject: true,
|
||||
template: path.resolve(__dirname, 'public/index.html'),
|
||||
}),
|
||||
new ESLintPlugin({
|
||||
extensions: ['js', 'jsx', 'ts', 'tsx'],
|
||||
}),
|
||||
new WebpackManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
// publicPath:
|
||||
generate: (seed, files, entrypoints) => {
|
||||
const manifestFiles = files.reduce((manifest, file) => {
|
||||
manifest[file.name] = file.path
|
||||
return manifest
|
||||
}, seed)
|
||||
const entrypointFiles = entrypoints.main.filter(
|
||||
(fileName) => !fileName.endsWith('.map')
|
||||
)
|
||||
|
||||
return {
|
||||
files: manifestFiles,
|
||||
entrypoints: entrypointFiles,
|
||||
}
|
||||
},
|
||||
}),
|
||||
new webpack.IgnorePlugin({
|
||||
resourceRegExp: /^\.\/locale$/,
|
||||
contextRegExp: /moment$/,
|
||||
}),
|
||||
new Dotenv(),
|
||||
],
|
||||
cache: {
|
||||
type: 'memory',
|
||||
},
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
const CaseSensitivePathsWebpackPlugin = require('case-sensitive-paths-webpack-plugin')
|
||||
const { merge } = require('webpack-merge')
|
||||
const common = require('./webpack.common')
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'development',
|
||||
devtool: 'inline-source-map',
|
||||
output: {
|
||||
filename: 'static/js/bundle.js',
|
||||
chunkFilename: 'static/js/[name].chunk.js',
|
||||
},
|
||||
plugins: [new CaseSensitivePathsWebpackPlugin()],
|
||||
devServer: {
|
||||
static: './public',
|
||||
hot: true,
|
||||
proxy: {},
|
||||
},
|
||||
})
|
|
@ -0,0 +1,65 @@
|
|||
const { merge } = require('webpack-merge')
|
||||
const common = require('./webpack.common')
|
||||
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
|
||||
const TerserPlugin = require('terser-webpack-plugin')
|
||||
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
|
||||
|
||||
module.exports = merge(common, {
|
||||
mode: 'production',
|
||||
output: {
|
||||
fileName: 'static/js/[name].[contenthash:8].js',
|
||||
chunkFilename: 'static/js/[name].[contenthash:8].chunk.js',
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
parse: {
|
||||
// We want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minification steps that turns valid ecma 5 code
|
||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||
// sections only apply transformations that are ecma 5 safe
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
// Disabled because of an issue with Terser breaking valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/5250
|
||||
// Pending further investigation:
|
||||
// https://github.com/terser-js/terser/issues/120
|
||||
inline: 2,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
},
|
||||
// Added for profiling in devtools
|
||||
keep_classnames: false,
|
||||
keep_fnames: false,
|
||||
output: {
|
||||
ecma: 5,
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
new CssMinimizerPlugin(),
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'static/css/[name].[contenthash:8].css',
|
||||
chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
|
||||
}),
|
||||
],
|
||||
})
|
Reference in New Issue