├── .eslintignore
├── src
├── index.css
├── pages
│ ├── help
│ │ └── index.js
│ ├── about
│ │ └── index.js
│ ├── not-found
│ │ └── index.js
│ └── home
│ │ └── index.js
├── components
│ └── loading
│ │ └── index.js
├── index.ejs
├── routes
│ └── index.js
├── history
│ └── index.js
├── index.js
└── app.js
├── .gitignore
├── prettier.config.js
├── .editorconfig
├── postcss.config.js
├── stylelint.config.js
├── README.md
├── babel.config.js
├── .eslintrc.js
├── LICENSE
├── package.json
└── webpack.config.js
/.eslintignore:
--------------------------------------------------------------------------------
1 | dist
2 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #f7f7f7;
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_STORE
2 | node_modules
3 | dist
4 | *.log*
5 | .vscode
6 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = {
4 | endOfLine: 'lf',
5 | singleQuote: true
6 | };
7 |
--------------------------------------------------------------------------------
/src/pages/help/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function Help() {
4 | return
Help
;
5 | }
6 |
7 | export default Help;
8 |
--------------------------------------------------------------------------------
/src/pages/about/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function About() {
4 | return About
;
5 | }
6 |
7 | export default About;
8 |
--------------------------------------------------------------------------------
/src/pages/not-found/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function NotFound() {
4 | return 404
;
5 | }
6 |
7 | export default NotFound;
8 |
--------------------------------------------------------------------------------
/src/components/loading/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function Loading() {
4 | return Loading...
;
5 | }
6 |
7 | export default Loading;
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // https://github.com/michael-ciniawsky/postcss-load-config
4 | module.exports = {
5 | plugins: {
6 | // To edit target browsers: use "browserlist" field in package.json
7 | autoprefixer: {}
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/stylelint.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const isProd = process.env.NODE_ENV === 'production';
4 |
5 | module.exports = {
6 | extends: [
7 | 'stylelint-config-xo',
8 | 'stylelint-config-css-modules',
9 | isProd ? 'stylelint-prettier/recommended' : 'stylelint-config-prettier'
10 | ]
11 | };
12 |
--------------------------------------------------------------------------------
/src/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Title
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/routes/index.js:
--------------------------------------------------------------------------------
1 | import { lazy } from 'react';
2 |
3 | export default [
4 | {
5 | path: '/',
6 | exact: true,
7 | component: lazy(() => import('../pages/home'))
8 | },
9 | {
10 | path: '/about',
11 | component: lazy(() => import('../pages/about'))
12 | },
13 | {
14 | path: '/help',
15 | component: lazy(() => import('../pages/help'))
16 | }
17 | ];
18 |
--------------------------------------------------------------------------------
/src/pages/home/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Link } from 'react-router-dom';
3 |
4 | function Home() {
5 | return (
6 |
7 |
Home
8 |
9 | About
10 |
11 |
12 | Help
13 |
14 |
15 | );
16 | }
17 |
18 | export default Home;
19 |
--------------------------------------------------------------------------------
/src/history/index.js:
--------------------------------------------------------------------------------
1 | import qs from 'qs';
2 | // eslint-disable-next-line import/no-extraneous-dependencies
3 | import { createBrowserHistory } from 'history';
4 |
5 | function addQuery(history) {
6 | const { location } = history;
7 | history.location = {
8 | ...location,
9 | query: qs.parse(location.search, { ignoreQueryPrefix: true })
10 | };
11 | }
12 |
13 | const history = createBrowserHistory();
14 |
15 | addQuery(history);
16 |
17 | export const unlisten = history.listen(() => {
18 | addQuery(history);
19 | });
20 |
21 | export default history;
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-demo
2 |
3 | > A full feature React.js application demo or template
4 |
5 | Clone or download for use, new projects can be used as application templates to replace [create-react-app](https://github.com/facebookincubator/create-react-app) (haha).
6 |
7 | ## Usage
8 |
9 | ``` bash
10 | # install dependencies
11 | yarn
12 |
13 | # code lint
14 | yarn lint:script
15 | yarn lint:style
16 |
17 | # code lint & fix
18 | yarn lint:script --fix
19 | yarn lint:style --fix
20 |
21 | # develop
22 | yarn dev
23 |
24 | # build for production
25 | yarn build
26 |
27 | # code analyze
28 | yarn analyze
29 | ```
30 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import 'normalize.css';
2 | import './index.css';
3 |
4 | // Polyfills
5 | // https://reactjs.org/docs/javascript-environment-requirements.html
6 | import 'raf/polyfill';
7 | import 'core-js/es/map';
8 | import 'core-js/es/set';
9 | import 'core-js/es/promise';
10 |
11 | import 'react-hot-loader';
12 | import React from 'react';
13 | import ReactDOM from 'react-dom';
14 | import { Router } from 'react-router';
15 | import history from './history';
16 | import App from './app';
17 |
18 | ReactDOM.render(
19 |
20 |
21 | ,
22 | document.querySelector('#app')
23 | );
24 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | import React, { Suspense } from 'react';
2 | import { Switch, Route } from 'react-router';
3 | import { hot } from 'react-hot-loader/root';
4 | import routes from './routes';
5 | import NotFound from './pages/not-found';
6 | import Loading from './components/loading';
7 |
8 | function App() {
9 | return (
10 | }>
11 |
12 | {routes.map(route => (
13 |
19 | ))}
20 |
21 |
22 |
23 | );
24 | }
25 |
26 | export default hot(App);
27 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = ({ env }) => ({
4 | presets: [
5 | [
6 | '@babel/preset-env',
7 | {
8 | loose: true,
9 | modules: false,
10 | useBuiltIns: 'usage',
11 | corejs: 3,
12 | targets: env('development') ? 'last 1 Chrome versions' : undefined
13 | }
14 | ],
15 | '@babel/preset-react'
16 | ],
17 | plugins: [
18 | 'react-hot-loader/babel',
19 | [
20 | '@babel/plugin-transform-runtime',
21 | {
22 | regenerator: false,
23 | useESModules: true
24 | }
25 | ],
26 | [
27 | '@babel/plugin-proposal-optional-chaining',
28 | {
29 | loose: true
30 | }
31 | ],
32 | [
33 | '@babel/plugin-proposal-nullish-coalescing-operator',
34 | {
35 | loose: true
36 | }
37 | ]
38 | ]
39 | });
40 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const confusingBrowserGlobals = require('confusing-browser-globals');
4 |
5 | const isProd = process.env.NODE_ENV === 'production';
6 |
7 | const config = {
8 | root: true,
9 | parser: 'babel-eslint',
10 | env: {
11 | browser: true
12 | },
13 | extends: [
14 | 'xo/esnext',
15 | require.resolve('xo/config/plugins'),
16 | 'xo-react',
17 | 'plugin:prettier/recommended',
18 | 'prettier/unicorn',
19 | 'prettier/react'
20 | ],
21 | rules: {
22 | 'no-console': isProd ? 2 : 0,
23 | 'no-debugger': isProd ? 2 : 0,
24 | 'no-restricted-globals': [2, ...confusingBrowserGlobals],
25 | 'import/no-unassigned-import': 0,
26 | 'react/prop-types': 0,
27 | },
28 | settings: {
29 | react: {
30 | version: 'detect'
31 | }
32 | }
33 | };
34 |
35 | if (!isProd) {
36 | config.extends = [
37 | ...config.extends,
38 | 'silent',
39 | 'silent/import',
40 | 'silent/prettier',
41 | 'silent/react',
42 | 'silent/unicorn'
43 | ];
44 | }
45 |
46 | module.exports = config;
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 杨明山
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-demo",
3 | "version": "1.0.0",
4 | "description": "A full feature React.js application demo or template",
5 | "scripts": {
6 | "lint:script": "cross-env NODE_ENV=production eslint .",
7 | "lint:style": "cross-env NODE_ENV=production stylelint \"src/**/*.css\"",
8 | "dev": "cross-env NODE_ENV=development webpack-dev-server --hot",
9 | "build": "cross-env NODE_ENV=production MODE=production webpack",
10 | "analyze": "cross-env NODE_ENV=production MODE=analysis webpack"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/yangmingshan/react-demo.git"
15 | },
16 | "author": "Yang Mingshan ",
17 | "license": "MIT",
18 | "bugs": {
19 | "url": "https://github.com/yangmingshan/react-demo/issues"
20 | },
21 | "homepage": "https://github.com/yangmingshan/react-demo#readme",
22 | "dependencies": {
23 | "@babel/runtime": "^7.4.5",
24 | "@hot-loader/react-dom": "~16.11.0",
25 | "core-js": "^3.1.4",
26 | "normalize.css": "^8.0.1",
27 | "qs": "^6.7.0",
28 | "raf": "^3.4.1",
29 | "react": "~16.11.0",
30 | "react-hot-loader": "^4.11.1",
31 | "react-router": "^5.0.1",
32 | "react-router-dom": "^5.0.1",
33 | "regenerator-runtime": "^0.13.2"
34 | },
35 | "devDependencies": {
36 | "@babel/core": "^7.4.5",
37 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4",
38 | "@babel/plugin-proposal-optional-chaining": "^7.2.0",
39 | "@babel/plugin-transform-runtime": "^7.4.4",
40 | "@babel/preset-env": "^7.4.5",
41 | "@babel/preset-react": "^7.0.0",
42 | "autoprefixer": "^9.6.0",
43 | "babel-eslint": "^10.0.1",
44 | "babel-loader": "^8.0.6",
45 | "clean-webpack-plugin": "^3.0.0",
46 | "confusing-browser-globals": "^1.0.9",
47 | "cross-env": "^6.0.3",
48 | "css-loader": "^3.0.0",
49 | "eslint": "^6.0.1",
50 | "eslint-config-silent": "^0.6.0",
51 | "eslint-config-xo-react": "^0.20.0",
52 | "eslint-plugin-react": "^7.13.0",
53 | "eslint-plugin-react-hooks": "^2.0.1",
54 | "file-loader": "^5.0.2",
55 | "html-webpack-plugin": "^3.2.0",
56 | "husky": "^3.0.1",
57 | "lint-staged": "^9.2.0",
58 | "mini-css-extract-plugin": "^0.8.0",
59 | "optimize-css-assets-webpack-plugin": "^5.0.1",
60 | "postcss-loader": "^3.0.0",
61 | "prettier": "^1.18.2",
62 | "script-ext-html-webpack-plugin": "^2.1.3",
63 | "style-loader": "^1.0.0",
64 | "stylelint": "^12.0.0",
65 | "stylelint-config-css-modules": "^2.1.0",
66 | "stylelint-config-prettier": "^8.0.0",
67 | "stylelint-config-xo": "^0.16.0",
68 | "stylelint-prettier": "^1.1.1",
69 | "url-loader": "^3.0.0",
70 | "webpack": "^4.35.0",
71 | "webpack-bundle-analyzer": "^3.3.2",
72 | "webpack-cli": "^3.3.5",
73 | "webpack-dev-server": "^3.7.2",
74 | "webpack-stylish": "^0.1.8",
75 | "webpackbar": "^4.0.0",
76 | "xo": "^0.25.3"
77 | },
78 | "husky": {
79 | "hooks": {
80 | "pre-commit": "lint-staged"
81 | }
82 | },
83 | "lint-staged": {
84 | "*.json": [
85 | "prettier --write",
86 | "git add"
87 | ],
88 | "*.js": [
89 | "cross-env NODE_ENV=production eslint --fix",
90 | "git add"
91 | ],
92 | "*.css": [
93 | "cross-env NODE_ENV=production stylelint --fix",
94 | "git add"
95 | ]
96 | },
97 | "browserslist": [
98 | "> 1%",
99 | "last 2 versions",
100 | "not op_mini all",
101 | "not dead"
102 | ]
103 | }
104 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const webpack = require('webpack');
5 | const WebpackBar = require('webpackbar');
6 | const WebpackStylish = require('webpack-stylish');
7 | const HtmlWebpackPlugin = require('html-webpack-plugin');
8 | const { CleanWebpackPlugin } = require('clean-webpack-plugin');
9 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
10 | const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
11 | const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
12 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
13 |
14 | const NODE_ENV = process.env.NODE_ENV || 'production';
15 | const _DEV_ = NODE_ENV === 'development';
16 |
17 | const config = {
18 | mode: NODE_ENV,
19 | entry: './src/index.js',
20 | output: {
21 | publicPath: '/',
22 | path: path.resolve(__dirname, './dist'),
23 | filename: _DEV_ ? '[name].js' : 'js/[name].[contenthash:8].js',
24 | chunkFilename: _DEV_ ? '[name].js' : 'js/[name].[contenthash:8].js'
25 | },
26 | resolve: {
27 | alias: {
28 | 'react-dom': '@hot-loader/react-dom'
29 | }
30 | },
31 | module: {
32 | rules: [
33 | {
34 | test: /\.css/,
35 | use: [
36 | {
37 | loader: _DEV_ ? 'style-loader' : MiniCssExtractPlugin.loader
38 | },
39 | {
40 | loader: 'css-loader',
41 | options: { modules: true }
42 | },
43 | {
44 | loader: 'postcss-loader'
45 | }
46 | ]
47 | },
48 | {
49 | test: /\.jsx?$/,
50 | exclude: /node_modules/,
51 | use: ['babel-loader']
52 | },
53 | {
54 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
55 | use: [
56 | {
57 | loader: 'url-loader',
58 | options: {
59 | limit: 4096,
60 | name: 'images/[name].[hash:8].[ext]'
61 | }
62 | }
63 | ]
64 | },
65 | {
66 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
67 | use: [
68 | {
69 | loader: 'url-loader',
70 | options: {
71 | limit: 4096,
72 | name: 'fonts/[name].[hash:8].[ext]'
73 | }
74 | }
75 | ]
76 | }
77 | ]
78 | },
79 | plugins: [
80 | new WebpackBar(),
81 | new HtmlWebpackPlugin({
82 | filename: 'index.html',
83 | template: 'src/index.ejs'
84 | })
85 | ]
86 | };
87 |
88 | if (_DEV_) {
89 | config.devtool = 'cheap-module-eval-source-map';
90 | config.devServer = {
91 | port: 8080,
92 | stats: 'errors-only',
93 | overlay: true,
94 | host: '0.0.0.0',
95 | historyApiFallback: true,
96 | disableHostCheck: true,
97 | proxy: {
98 | '/api': {
99 | target: 'http://api.development.com',
100 | pathRewrite: { '^/api': '' },
101 | changeOrigin: true
102 | }
103 | }
104 | };
105 | } else {
106 | config.stats = 'none';
107 | config.plugins.push(
108 | new CleanWebpackPlugin(),
109 | new webpack.HashedModuleIdsPlugin(),
110 | new ScriptExtHtmlWebpackPlugin({
111 | inline: [/runtime\.[a-z0-9]{8}\.js$/],
112 | preload: {
113 | chunks: 'initial',
114 | test: [/vendors\.[a-z0-9]{8}\.js$/, /main\.[a-z0-9]{8}\.js$/]
115 | }
116 | }),
117 | new MiniCssExtractPlugin({
118 | filename: 'css/[name].[contenthash:8].css',
119 | chunkFilename: 'css/[name].[contenthash:8].css'
120 | }),
121 | new OptimizeCssAssetsPlugin(),
122 | new WebpackStylish()
123 | );
124 | config.optimization = {
125 | runtimeChunk: { name: 'runtime' },
126 | splitChunks: {
127 | cacheGroups: {
128 | vendors: {
129 | name: 'vendors',
130 | chunks: 'initial',
131 | test: /[\\/]node_modules[\\/](?!.*normalize\.css)/
132 | }
133 | }
134 | }
135 | };
136 | }
137 |
138 | if (process.env.MODE === 'analysis') {
139 | config.plugins.push(new BundleAnalyzerPlugin());
140 | }
141 |
142 | module.exports = config;
143 |
--------------------------------------------------------------------------------