├── .gitignore
├── LICENSE
├── README.md
├── config
├── .env
├── paths.js
└── webpack
│ ├── analyze.js
│ ├── common.js
│ ├── dev.js
│ ├── prod.js
│ └── pwa.js
├── package.json
├── public
├── assets
│ ├── browserconfig.xml
│ ├── icons
│ │ ├── 128.png
│ │ ├── 256.png
│ │ ├── 512.png
│ │ ├── 64.png
│ │ └── logo.png
│ ├── manifest.json
│ ├── netlify.toml
│ ├── robots.txt
│ └── sitemap.xml
├── index.html
└── sw-reg.js
├── src
├── App.js
├── assets
│ └── logo.png
├── index.js
└── modules
│ ├── components
│ ├── TodoControls.js
│ ├── TodoFilters.js
│ ├── TodoForm.js
│ ├── TodoList
│ │ ├── TodoListItemEdit.js
│ │ ├── TodoListItemRegular.js
│ │ └── index.js
│ └── index.js
│ ├── context
│ ├── TodoContext.js
│ └── package.json
│ └── reducer
│ ├── package.json
│ └── todoProducer.js
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | build
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright 2021 Igor Agapov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Webpack 5 Max (JS/React/TS)
2 |
3 | [](https://opensource.org/licenses/MIT)
4 |
5 | 
6 |
7 | ### :zap: `Webpack 5 Boilerplate for JS/React/TS apps.`
8 |
9 | :link: [Demo on CodeSandbox](https://codesandbox.io/s/webpack5-max-jsreactts-j2o2u)
10 |
11 | ---
12 |
13 | ## Includes
14 |
15 | - 5 config files with all possible settings
16 | - common
17 | - common + development
18 | - common + production
19 | - common + production + pwa
20 | - common + production + analyze
21 | - React Todo App example
22 | - actions: add, complete, remove, edit, update
23 | - filters: all, active, completed
24 | - controls: mark all todos as completed, clear all completed todos
25 | - state management: useContext/useReducer
26 | - styling - react-bootstrap
27 | - type checking - prop-types
28 | - HTML template with all meta & link tags for SEO
29 | - don't forget to change values in HtmlWebpackPlugin templateParameters object
30 | - browserconfig.xml, robots.txt, sitemap.xml
31 | - service-worker & manifest.json
32 | - don't forget to change values in manifest.json
33 | - netlify.toml with all security headers
34 | ---
35 | You can easily add settings for Vue or Angular components.
36 |
37 | *Vue*
38 |
39 | - install deps
40 |
41 | ```bash
42 | yarn add -D vue-loader vue-template-compiler
43 | # or
44 | npm i -D yarn vue-loader vue-template-compiler
45 | ```
46 |
47 | - add following to config/webpack/common.js
48 |
49 | ```js
50 | const VueLoaderPlugin = require('vue-loader/lib/plugin')
51 |
52 | module.exports = {
53 | module: {
54 | rules: [
55 | {
56 | test: /\.vue$/,
57 | loader: 'vue-loader'
58 | }
59 | ]
60 | },
61 | plugins: [
62 | new VueLoaderPlugin()
63 | ]
64 | }
65 | ```
66 |
67 | *Angular*
68 |
69 | - install dep
70 |
71 | ```bash
72 | yarn add -D angular2-template-loader
73 | # or
74 | npm i -D angular2-template-loader
75 | ```
76 |
77 | - change following in config/webpack/common.js
78 |
79 | ```js
80 | {
81 | test: /.tsx?$/i,
82 | exclude: /node_modules/,
83 | use: [babelLoader, 'ts-loader', 'angular2-template-loader?keepUrl=true']
84 | },
85 | ```
86 |
87 | ---
88 |
89 | ## Installation
90 |
91 | ```bash
92 | # clone repo
93 | git clone https://github.com/harryheman/Webpack5-Max.git
94 |
95 | # install deps
96 | yarn
97 | # or
98 | npm i
99 | ```
100 |
101 | ---
102 |
103 | ## Usage
104 |
105 | ### Development Server
106 |
107 | ```bash
108 | yarn start
109 | # or
110 | npm start
111 | ```
112 |
113 | ### Production Bundle
114 |
115 | ```bash
116 | yarn build
117 | # or
118 | npm run build
119 | ```
120 |
121 | ### Production Bundle PWA
122 |
123 | ```bash
124 | yarn pwa
125 | # or
126 | npm run pwa
127 | ```
128 |
129 | ### Production Bundle Analyzer
130 |
131 | ```bash
132 | yarn analyze
133 | # or
134 | npm run analyze
135 | ```
136 | ---
137 | ## Author
138 |
139 | [Igor Agapov](https://github.com/harryheman)
140 |
141 | ---
142 | ## License
143 |
144 | This project is open source and available under the [MIT License](LICENSE)
145 |
--------------------------------------------------------------------------------
/config/.env:
--------------------------------------------------------------------------------
1 | SECRET='Hello, world!'
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | public: path.resolve(__dirname, '../public'),
5 | src: path.resolve(__dirname, '../src'),
6 | build: path.resolve(__dirname, '../build')
7 | }
8 |
--------------------------------------------------------------------------------
/config/webpack/analyze.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge')
2 |
3 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
4 | .BundleAnalyzerPlugin
5 |
6 | const prod = require('./prod')
7 |
8 | module.exports = merge(prod, {
9 | plugins: [new BundleAnalyzerPlugin()]
10 | })
11 |
--------------------------------------------------------------------------------
/config/webpack/common.js:
--------------------------------------------------------------------------------
1 | const paths = require('../paths')
2 |
3 | const webpack = require('webpack')
4 |
5 | const CopyWebpackPlugin = require('copy-webpack-plugin')
6 | const HtmlWebpackPlugin = require('html-webpack-plugin')
7 |
8 | const Dotenv = require('dotenv-webpack')
9 |
10 | const babelLoader = {
11 | loader: 'babel-loader',
12 | options: {
13 | presets: ['@babel/preset-env', '@babel/preset-react'],
14 | plugins: [
15 | '@babel/plugin-proposal-class-properties',
16 | '@babel/plugin-syntax-dynamic-import',
17 | '@babel/plugin-transform-runtime'
18 | ]
19 | }
20 | }
21 |
22 | module.exports = {
23 | entry: `${paths.src}/index.js`,
24 | output: {
25 | path: paths.build,
26 | filename: 'js/[name].bundle.js',
27 | publicPath: '/',
28 | clean: true,
29 | crossOriginLoading: 'anonymous',
30 | module: true,
31 | environment: {
32 | arrowFunction: true,
33 | bigIntLiteral: false,
34 | const: true,
35 | destructuring: true,
36 | dynamicImport: false,
37 | forOf: true
38 | }
39 | },
40 | resolve: {
41 | alias: {
42 | '@': `${paths.src}/modules`
43 | },
44 | extensions: ['.mjs', '.js', '.jsx', '.ts', '.tsx', '.json']
45 | },
46 | experiments: {
47 | topLevelAwait: true,
48 | outputModule: true
49 | },
50 | module: {
51 | rules: [
52 | // JavaScript, React
53 | {
54 | test: /\.m?jsx?$/i,
55 | exclude: /node_modules/,
56 | use: babelLoader
57 | },
58 | // TypeScript
59 | {
60 | test: /.tsx?$/i,
61 | exclude: /node_modules/,
62 | use: [babelLoader, 'ts-loader']
63 | },
64 | // CSS, SASS
65 | {
66 | test: /\.(c|sa|sc)ss$/i,
67 | use: [
68 | 'style-loader',
69 | {
70 | loader: 'css-loader',
71 | options: { importLoaders: 1 }
72 | },
73 | 'sass-loader'
74 | ]
75 | },
76 | // MD
77 | {
78 | test: /\.md$/i,
79 | use: ['html-loader', 'markdown-loader']
80 | },
81 | // static files
82 | {
83 | test: /\.(jpe?g|png|gif|svg|eot|ttf|woff2?)$/i,
84 | type: 'asset'
85 | }
86 | ]
87 | },
88 | plugins: [
89 | new webpack.ProgressPlugin(),
90 |
91 | new CopyWebpackPlugin({
92 | patterns: [
93 | {
94 | from: `${paths.public}/assets`
95 | }
96 | ]
97 | }),
98 |
99 | new HtmlWebpackPlugin({
100 | template: `${paths.public}/index.html`,
101 | filename: 'index.html',
102 | templateParameters: {
103 | analytics: 'Google Analytics ID',
104 | author: 'Igor Agapov',
105 | publishedDate: '2021-02-27',
106 | description:
107 | 'Full Webpack 5 Boilerplate for JavaScript, React & TypeScript projects',
108 | keywords:
109 | 'webpack, webpack5, boilerplate, template, max, config, bundler, bundle, javascript, react, reactjs, react.js, typescript, project, app',
110 | title: 'Webpack5 Max',
111 | url: 'https://example.com'
112 | }
113 | }),
114 |
115 | new webpack.ProvidePlugin({
116 | React: 'react'
117 | }),
118 |
119 | new Dotenv({
120 | path: './config/.env'
121 | })
122 | ]
123 | }
124 |
--------------------------------------------------------------------------------
/config/webpack/dev.js:
--------------------------------------------------------------------------------
1 | const paths = require('../paths')
2 |
3 | const webpack = require('webpack')
4 | const { merge } = require('webpack-merge')
5 |
6 | const common = require('./common')
7 |
8 | module.exports = merge(common, {
9 | mode: 'development',
10 | devtool: 'eval-cheap-source-map',
11 | devServer: {
12 | compress: true,
13 | contentBase: paths.build,
14 | historyApiFallback: true,
15 | hot: true,
16 | open: true,
17 | port: 3000,
18 | clientLogLevel: 'silent'
19 | },
20 | plugins: [new webpack.HotModuleReplacementPlugin()]
21 | })
22 |
--------------------------------------------------------------------------------
/config/webpack/prod.js:
--------------------------------------------------------------------------------
1 | const paths = require('../paths')
2 | const { merge } = require('webpack-merge')
3 | const common = require('./common')
4 |
5 | const MiniCssExtractPlugin = require('mini-css-extract-plugin')
6 | const ImageminPlugin = require('imagemin-webpack-plugin').default
7 |
8 | module.exports = merge(common, {
9 | mode: 'production',
10 | entry: {
11 | index: {
12 | import: `${paths.src}/index.js`,
13 | dependOn: ['react', 'helpers']
14 | },
15 | react: ['react', 'react-dom', 'prop-types'],
16 | helpers: ['immer', 'nanoid']
17 | },
18 | devtool: false,
19 | output: {
20 | filename: 'js/[name].[contenthash].bundle.js',
21 | publicPath: './'
22 | },
23 | module: {
24 | rules: [
25 | {
26 | test: /\.(c|sa|sc)ss$/i,
27 | use: [
28 | MiniCssExtractPlugin.loader,
29 | {
30 | loader: 'css-loader',
31 | options: { importLoaders: 1 }
32 | },
33 | 'sass-loader'
34 | ]
35 | }
36 | ]
37 | },
38 | plugins: [
39 | new MiniCssExtractPlugin({
40 | filename: 'css/[name].[contenthash].css',
41 | chunkFilename: '[id].css'
42 | }),
43 |
44 | new ImageminPlugin({
45 | test: /\.(jpe?g|png|gif|svg)$/i
46 | })
47 | ],
48 | optimization: {
49 | runtimeChunk: 'single'
50 | },
51 | performance: {
52 | hints: 'warning',
53 | maxEntrypointSize: 512000,
54 | maxAssetSize: 512000
55 | }
56 | })
57 |
--------------------------------------------------------------------------------
/config/webpack/pwa.js:
--------------------------------------------------------------------------------
1 | const { merge } = require('webpack-merge')
2 | const paths = require('../paths.js')
3 |
4 | const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
5 | const { GenerateSW } = require('workbox-webpack-plugin')
6 |
7 | const prod = require('./prod')
8 |
9 | module.exports = merge(prod, {
10 | plugins: [
11 | new AddAssetHtmlPlugin({ filepath: `${paths.public}/sw-reg.js` }),
12 | new GenerateSW({
13 | clientsClaim: true,
14 | skipWaiting: true
15 | })
16 | ]
17 | })
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack5-max",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "private": true,
7 | "dependencies": {
8 | "bootstrap": "^4.6.0",
9 | "immer": "^8.0.1",
10 | "nanoid": "^3.1.20",
11 | "prop-types": "^15.7.2",
12 | "react": "^17.0.1",
13 | "react-bootstrap": "^1.5.0",
14 | "react-dom": "^17.0.1",
15 | "react-icons": "^4.2.0"
16 | },
17 | "devDependencies": {
18 | "@babel/core": "^7.12.13",
19 | "@babel/plugin-proposal-class-properties": "^7.12.13",
20 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
21 | "@babel/plugin-transform-runtime": "^7.13.15",
22 | "@babel/preset-env": "^7.12.13",
23 | "@babel/preset-react": "^7.12.13",
24 | "@tsconfig/recommended": "^1.0.1",
25 | "add-asset-html-webpack-plugin": "^3.2.0",
26 | "babel-loader": "^8.2.2",
27 | "copy-webpack-plugin": "^7.0.0",
28 | "css-loader": "^5.0.1",
29 | "dotenv-webpack": "^6.0.0",
30 | "html-loader": "^2.0.0",
31 | "html-webpack-plugin": "^5.0.0",
32 | "imagemin-webpack-plugin": "^2.4.2",
33 | "markdown-loader": "^6.0.0",
34 | "mini-css-extract-plugin": "^1.3.5",
35 | "sass": "^1.32.7",
36 | "sass-loader": "^11.0.1",
37 | "style-loader": "^2.0.0",
38 | "webpack": "^5.21.1",
39 | "webpack-bundle-analyzer": "^4.4.0",
40 | "webpack-cli": "^4.5.0",
41 | "webpack-dev-server": "^3.11.2",
42 | "webpack-merge": "^5.7.3",
43 | "workbox-webpack-plugin": "^6.1.0"
44 | },
45 | "scripts": {
46 | "start": "webpack serve -c config/webpack/dev.js",
47 | "build": "webpack -c config/webpack/prod.js",
48 | "pwa": "webpack -c config/webpack/pwa.js",
49 | "analyze": "webpack --analyze -c config/webpack/analyze.js"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/public/assets/browserconfig.xml:
--------------------------------------------------------------------------------
1 |
2 |