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