├── .editorconfig ├── .gitignore ├── LICENSE ├── README.md ├── babel-defines.js ├── babel.config.js ├── jsconfig.json ├── package.json ├── prettier.config.js ├── public ├── about.html └── index.html ├── src ├── app │ ├── index.scss │ ├── index.ts │ └── styles │ │ ├── body.scss │ │ └── index.scss ├── index.ts └── shared │ ├── img │ ├── .gitkeep │ └── webpack.svg │ └── misc │ ├── favicon.ico │ ├── robots.txt │ └── sitemap.xml ├── tsconfig.json ├── webpack ├── webpack-defines.js ├── webpack-pages.js ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # STANDART mode 4 | # From: https://github.com/airbnb/javascript/blob/master/.editorconfig 5 | 6 | root = true 7 | 8 | [*] 9 | indent_style = space 10 | indent_size = 2 11 | charset = utf-8 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | end_of_line = lf 15 | # editorconfig-tools is unable to ignore longs strings or urls 16 | max_line_length = off 17 | 18 | [CHANGELOG.md] 19 | indent_size = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Default 2 | .gulp 3 | .idea 4 | .vscode 5 | 6 | # Node 7 | node_modules 8 | 9 | # Dist & tests 10 | test 11 | dist 12 | 13 | # Logs: 14 | *.log 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | # Special 20 | Thumbs.db 21 | Desktop.ini 22 | .DS_Store* 23 | ehthumbs.db 24 | Icon? -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Evgenii Vedegis 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

Webpack work template

4 |

5 | Webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset. 6 |

7 |

Author: Vedees | Youtube guide (ru)

8 |
9 | 10 | ## Features: 11 | 12 | - separated configs for `dev` and `prod` 13 | - `typescript / javascript` full support 14 | - `sass / css` full support 15 | - full babel & postcss setup 16 | - 0 dependencies 17 | - the best optimization for your production 18 | - easy webpack and babel customization 19 | 20 | Everybody knows that developing runs on coffee! Thanks for your support! 21 | 22 | [![Buy me a coffee][buymeacoffee-shield]][buymeacoffee] 23 | 24 | ## Build Setup: 25 | 26 | ```bash 27 | # Download repository: 28 | git clone https://github.com/vedees/webpack-template webpack-template 29 | 30 | # Go to the app: 31 | cd webpack-template 32 | 33 | # Install dependencies: 34 | # npm install 35 | # or: 36 | yarn 37 | 38 | # Server with hot reload at http://localhost:8084/ 39 | # npm run start 40 | # or: 41 | yarn start 42 | 43 | # Output will be at dist/ folder 44 | # npm run build 45 | # or: 46 | yarn build 47 | ``` 48 | 49 | ## Project Structure: 50 | 51 | - `public/*.html` - HTML files 52 | - `src/app` - core app 53 | - `src/shared` - shared files 54 | - `src/shared/img` - images folder (! for html calls use correct path: `static/img/some.jpg`) 55 | - `src/shared/misc` - misc files (i.g. favicon, sitemap, etc.) 56 | - `src/index.ts` - main app entity 57 | 58 | Configs: 59 | 60 | - `/babel-defines.js` - config for babel 61 | - `/webpack/webpack-pages.js` - config for html pages 62 | - `/webpack/webpack-defines.js` - config for entire webpack 63 | 64 | Main entry point: 65 | 66 | - `src/app/index.ts` - core entry point 67 | 68 | ## Defines: 69 | 70 | Core webpack config from `/webpack/webpack-defines.js`: 71 | 72 | ```js 73 | const PATHS = { 74 | // path to the src dir 75 | src: path.join(__dirname, '../src'), 76 | // path to the output dir 77 | dist: path.join(__dirname, '../dist'), 78 | // path to the public files (html files) 79 | public: path.join(__dirname, '../public'), 80 | 81 | // path to output sub dir (js, css, fonts, etc.) 82 | assets: 'assets/', 83 | // path to output sub dir (img, icons, etc.) 84 | static: 'static/' 85 | } 86 | ``` 87 | 88 | ## Pages config: 89 | 90 | Pages config from `/webpack/webpack-pages.js`: 91 | 92 | ```js 93 | const pages = [ 94 | { 95 | // page title 96 | title: 'Home page', 97 | // template name `public/index.html` 98 | template: 'index.html', 99 | // output filename `dist/index.html` 100 | filename: 'index.html', 101 | 102 | // other options can be here 103 | }, 104 | { 105 | title: 'About page', 106 | template: 'about.html', 107 | filename: 'about.html', 108 | } 109 | ] 110 | ``` 111 | 112 | You can pass a hash of configuration options to html-webpack-plugin. 113 | 114 | Allowed values are as follows: https://github.com/jantimon/html-webpack-plugin#options 115 | 116 | ## Manual pages setup: 117 | 118 | In case if you don't want to use Pages config: 119 | 120 | 1. Create another html file in `./public` 121 | 2. Go to `./webpack/webpack.common.js` 122 | 3. Add new page to the config: 123 | 124 | ```js 125 | // index page: 126 | new HtmlWebpackPlugin({ 127 | title: 'Home page', 128 | favicon: defines.src + '/shared/misc/favicon.ico', 129 | template: defines.public + '/index.html', // public/index.html page 130 | filename: 'index.html' // output file 131 | }), 132 | // about page: 133 | new HtmlWebpackPlugin({ 134 | title: 'About page', 135 | favicon: defines.src + '/shared/misc/favicon.ico', 136 | template: defines.public + '/about.html', // public/about.html page 137 | filename: 'about.html' // output file 138 | }), 139 | ``` 140 | 141 | ## Import libs example: 142 | 143 | Install it: 144 | 145 | ```bash 146 | yarn add bootstrap react react-dom 147 | ``` 148 | 149 | Import libs to `src/app/index.ts`: 150 | 151 | ```js 152 | // React example 153 | import React from 'react' 154 | 155 | // Bootstrap example (with custom js imports) 156 | import Bootstrap from 'bootstrap/dist/js/bootstrap.min.js' 157 | import 'bootstrap/dist/js/bootstrap.min.js' 158 | ``` 159 | 160 | ## Import SASS / CSS libs example: 161 | 162 | Import libs to `src/app/index.scss`: 163 | 164 | ```scss 165 | // sass lib import example: 166 | @import '../../node_modules/spinners/stylesheets/spinners'; 167 | // css lib import example: 168 | @import '../../node_modules/flickity/dist/flickity.css'; 169 | ``` 170 | 171 | ## React example: 172 | 173 | Here's an example with React + i18n Provider. 174 | 175 | Install react: 176 | 177 | ```bash 178 | yarn add react react-dom 179 | ``` 180 | 181 | Create div with id `app` in `public/index.html`: 182 | 183 | ```html 184 |
185 | ``` 186 | 187 | Init the app in `src/app/index.ts`: 188 | 189 | ```tsx 190 | import React from 'react' 191 | import { createRoot } from 'react-dom/client' 192 | 193 | // app styles 194 | import './index.scss' 195 | 196 | // local providers: 197 | import { I18nProvider } from './providers/I18nProvider' 198 | 199 | const container = document.getElementById('app') as HTMLElement 200 | const root = createRoot(container) 201 | 202 | root.render( 203 | 204 | ... 205 | 206 | ) 207 | ``` 208 | 209 | File `src/app/providers/I18nProvider.tsx`: 210 | 211 | ```tsx 212 | import React, { FC, PropsWithChildren } from 'react' 213 | 214 | export const I18nProvider: FC = ({ children }) => { 215 | // ... 216 | 217 | return {children} 218 | } 219 | ``` 220 | 221 | ## Vue example: 222 | 223 | Install vue: 224 | 225 | ```bash 226 | yarn add vue 227 | ``` 228 | 229 | Init the app in `src/app/index.ts`: 230 | 231 | ```js 232 | // vue example (react one is above): 233 | const app = new Vue({ 234 | el: '#app' 235 | }) 236 | ``` 237 | 238 | Create div with id `app` in `public/index.html`: 239 | 240 | ```html 241 |
242 | ``` 243 | 244 | ### Adding Vue Components: 245 | 246 | Create your component in `src/app/components/`. 247 | 248 | **HTML Usage (in `*.html` files):** 249 | 250 | Init component in `src/app/index.ts`: 251 | 252 | ```js 253 | Vue.component('example-component', require('./components/Example.vue').default) 254 | ``` 255 | 256 | In any html files: 257 | 258 | ```html 259 | 260 | ``` 261 | 262 | **VUE Usage (in `*.vue` files):** 263 | 264 | Import component: 265 | 266 | ```js 267 | import ExampleComponent from '@/components/Example.vue' 268 | ``` 269 | 270 | Init component (template): 271 | 272 | ```js 273 | 274 | ``` 275 | 276 | Register component (script): 277 | 278 | ```js 279 | components: { 280 | Example: ExampleComponent 281 | } 282 | ``` 283 | 284 | ## Adding Google Fonts: 285 | 286 | Connect fonts to `public/index.html`: 287 | 288 | ```html 289 | 290 | 291 | 292 | ``` 293 | 294 | Change the font in `src/app/styles/body.scss`: 295 | 296 | ```scss 297 | html { 298 | font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, 'Apple Color Emoji', Arial, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol' !important; 299 | } 300 | ``` 301 | 302 | ## Adding local fonts: 303 | 304 | In case if you don't want to use Google Fonts: 305 | 306 | - Download fonts 307 | - Add fonts to the (i.g. `/src/shared/fonts/OpenSans/...`). 308 | 309 | Then add `@font-face` in some `.scss` file (i.g. `/src/app/styles/font.scss`): 310 | 311 | ```scss 312 | // Open Sans example: 313 | @font-face { 314 | font-family: 'Open Sans'; 315 | font-style: normal; 316 | font-weight: 400; 317 | font-stretch: 100%; 318 | font-display: swap; 319 | src: url('/static/fonts/OpenSans/Open-Sans.woff2') format('woff2'); 320 | unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; 321 | } 322 | ``` 323 | 324 | The last step is to copy these fonts into the `/dist` folder every time you build the project. 325 | 326 | Add another config for `CopyWebpackPlugin` to `/webpack/webpack.common.js`: 327 | 328 | ```js 329 | new CopyWebpackPlugin({ 330 | // ... 331 | 332 | // `shared/fonts` to `dist/static/fonts` 333 | { 334 | from: `${defines.src}/shared/fonts`, 335 | to: `${defines.dist}/${defines.static}/fonts` 336 | }, 337 | }) 338 | ``` 339 | 340 | Change the font in `src/app/styles/body.scss`: 341 | 342 | ```scss 343 | html { 344 | font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, 'Apple Color Emoji', Arial, sans-serif, 'Segoe UI Emoji', 'Segoe UI Symbol' !important; 345 | } 346 | ``` 347 | 348 | ## License: 349 | 350 | [MIT](./LICENSE) 351 | 352 | Copyright (c) 2018-present, [Evgenii Vedegis](https://github.com/vedees) 353 | 354 | [buymeacoffee-shield]: https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-2.svg 355 | [buymeacoffee]: https://www.buymeacoffee.com/vedegis 356 | -------------------------------------------------------------------------------- /babel-defines.js: -------------------------------------------------------------------------------- 1 | const shared = { 2 | __DEV__: "process.env.NODE_ENV !== 'production'", 3 | } 4 | 5 | module.exports = { 6 | development: shared, 7 | test: shared, 8 | production: { 9 | ...shared, 10 | __DEV__: 'false', 11 | 'process.env.NODE_ENV': "'production'", 12 | }, 13 | } 14 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const defines = require('./babel-defines') 2 | 3 | function replacementPlugin(env) { 4 | return ['babel-plugin-transform-replace-expressions', { replace: defines[env] }] 5 | } 6 | 7 | const sharedPlugins = [ 8 | 'preval', 9 | 10 | // install others if you need: 11 | // 'add-react-displayname', 12 | // 'babel-plugin-styled-components', 13 | // '@babel/plugin-proposal-nullish-coalescing-operator', 14 | // '@babel/plugin-proposal-optional-chaining', 15 | 16 | [ 17 | 'module-resolver', 18 | { 19 | root: ['./'], 20 | extensions: ['ts', 'tsx', '.js', '.jsx', '.json', '.svg', '.png', '.jpg', '.jpeg'], 21 | alias: { 22 | '@': './src/' 23 | } 24 | } 25 | ] 26 | ] 27 | 28 | function makePresets(moduleValue) { 29 | return ['@babel/preset-env', '@babel/preset-typescript', ['@babel/preset-react', { modules: moduleValue }]] 30 | } 31 | 32 | module.exports = { 33 | env: { 34 | development: { 35 | presets: makePresets(process.env.BABEL_MODULE || false), 36 | plugins: [ 37 | ...(process.env.BABEL_MODULE === 'commonjs' 38 | ? ['@babel/plugin-transform-modules-commonjs'] 39 | : process.env.STORYBOOK 40 | ? [] 41 | : [ 42 | 'transform-commonjs' // theme-preval is commonjs and needs to be transformed to esm 43 | ]), 44 | ...sharedPlugins, 45 | replacementPlugin('development') 46 | ] 47 | }, 48 | production: { 49 | presets: makePresets(false), 50 | plugins: [...sharedPlugins, replacementPlugin('production')] 51 | }, 52 | test: { 53 | presets: makePresets('commonjs'), 54 | plugins: [...sharedPlugins, ['@babel/plugin-transform-modules-commonjs'], replacementPlugin('test')] 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./", 4 | "paths": { 5 | "@/*": ["./src/*"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpacktemplate", 3 | "version": "1.0.0", 4 | "description": "Webpack template", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack serve --config webpack/webpack.dev.js", 8 | "build": "npm run clean & NODE_ENV=production webpack --config webpack/webpack.prod.js", 9 | "clean": "rimraf dist", 10 | "format": "prettier --cache --write '**/*.{js,css,md,mdx,ts,tsx,yml}'", 11 | "format:diff": "prettier --cache --list-different '**/*.{js,css,md,mdx,ts,tsx,yml}'", 12 | "lint": "eslint '**/*.{js,ts,tsx,md,mdx}' --max-warnings=0", 13 | "lint:fix": "npm run lint -- --fix", 14 | "test": "jest", 15 | "tscheck": "tsc", 16 | "tscheck:watch": "tsc -w" 17 | }, 18 | "browserslist": { 19 | "production": [ 20 | ">0.5%", 21 | "last 1 year", 22 | "not dead", 23 | "not op_mini all" 24 | ], 25 | "development": [ 26 | "last 1 chrome version", 27 | "last 1 firefox version", 28 | "last 1 safari version" 29 | ] 30 | }, 31 | "license": "MIT", 32 | "dependencies": {}, 33 | "devDependencies": { 34 | "@babel/core": "^7.23.5", 35 | "@babel/plugin-transform-modules-commonjs": "^7.23.3", 36 | "@babel/preset-env": "^7.23.5", 37 | "@babel/preset-react": "^7.23.3", 38 | "@babel/preset-typescript": "^7.23.3", 39 | "@types/node": "^20.10.2", 40 | "babel-core": "^6.26.3", 41 | "babel-loader": "^9.1.3", 42 | "babel-plugin-module-resolver": "^5.0.0", 43 | "babel-plugin-preval": "^5.1.0", 44 | "babel-plugin-transform-commonjs": "^1.1.6", 45 | "babel-plugin-transform-remove-console": "^6.9.4", 46 | "babel-plugin-transform-replace-expressions": "^0.2.0", 47 | "copy-webpack-plugin": "latest", 48 | "css-loader": "^6.8.1", 49 | "css-minimizer-webpack-plugin": "^5.0.1", 50 | "file-loader": "^6.2.0", 51 | "html-webpack-plugin": "^5.5.3", 52 | "json-minimizer-webpack-plugin": "^4.0.0", 53 | "mini-css-extract-plugin": "^2.7.6", 54 | "postcss-loader": "^7.3.3", 55 | "postcss-preset-env": "^9.3.0", 56 | "sass": "^1.69.5", 57 | "sass-loader": "^13.3.2", 58 | "terser-webpack-plugin": "^5.3.9", 59 | "webpack": "^5.89.0", 60 | "webpack-cli": "^5.1.4", 61 | "webpack-dev-server": "^4.15.1", 62 | "webpack-merge": "^5.10.0" 63 | }, 64 | "author": "https://github.com/vedees", 65 | "repository": { 66 | "type": "git", 67 | "url": "git+https://github.com/vedees/webpack-template.git" 68 | }, 69 | "bugs": { 70 | "url": "https://github.com/vedees/webpack-template/issues" 71 | }, 72 | "keywords": [ 73 | "webpack", 74 | "babel", 75 | "typescript", 76 | "scss" 77 | ] 78 | } 79 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: false, 3 | singleQuote: true, 4 | arrowParens: 'avoid', 5 | trailingComma: 'none', 6 | endOfLine: 'auto' 7 | } 8 | -------------------------------------------------------------------------------- /public/about.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Webpack Template 8 | 9 | 10 |
11 | 12 |
It works!
13 | 14 | 15 | 16 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Webpack Template 8 | 9 | 10 |
11 | 12 |
It works!
13 | 14 | 15 | 16 | 27 |
28 | 29 | 30 | -------------------------------------------------------------------------------- /src/app/index.scss: -------------------------------------------------------------------------------- 1 | // scss entry point 2 | 3 | // here can be imports for global libs 4 | 5 | // local styles: 6 | @import './styles/index.scss'; 7 | -------------------------------------------------------------------------------- /src/app/index.ts: -------------------------------------------------------------------------------- 1 | console.log('it works!') 2 | 3 | // core app styles: 4 | import './index.scss' 5 | 6 | // here you can init react or vue 7 | -------------------------------------------------------------------------------- /src/app/styles/body.scss: -------------------------------------------------------------------------------- 1 | *, 2 | *:before, 3 | *:after { 4 | box-sizing: border-box; 5 | } 6 | 7 | body, 8 | html { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | html { 14 | scroll-behaviour: smooth; 15 | } 16 | 17 | body { 18 | position: relative; 19 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Roboto, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; 20 | font-size: 18px; 21 | fill: currentcolor; 22 | -webkit-font-smoothing: antialiased; 23 | -moz-osx-font-smoothing: grayscale; 24 | } 25 | -------------------------------------------------------------------------------- /src/app/styles/index.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | // scss imports can be here (local files only): 4 | @import './body.scss'; 5 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // entry point 2 | // DO NOT mess this file! 3 | 4 | import './app' 5 | -------------------------------------------------------------------------------- /src/shared/img/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vedees/webpack-template/a4b7333328726f141d09acf1e76d5b865debe113/src/shared/img/.gitkeep -------------------------------------------------------------------------------- /src/shared/img/webpack.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/shared/misc/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vedees/webpack-template/a4b7333328726f141d09acf1e76d5b865debe113/src/shared/misc/favicon.ico -------------------------------------------------------------------------------- /src/shared/misc/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /*?* 3 | Sitemap: http://localhost:8081/sitemap.xml 4 | 5 | User-agent: Goolebot 6 | Disallow: /*?* 7 | -------------------------------------------------------------------------------- /src/shared/misc/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | http://localhost:8081/ 5 | 6 | 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* 2 | TypeScript compiler is NOT using for build! 3 | 4 | so here's only type-checking options 5 | use: `yarn tscheck` or `yarn tscheck:watch` 6 | 7 | you can set up eslint in vs-code to see live time errors without `tscheck` 8 | */ 9 | 10 | { 11 | "compilerOptions": { 12 | "target": "ESNext", 13 | "module": "CommonJS", 14 | "lib": ["ESNext", "ES6", "DOM", "DOM.Iterable"], 15 | "jsx": "react", 16 | "noEmit": true, 17 | "sourceMap": true, 18 | "resolveJsonModule": true, 19 | 20 | // strict options 21 | "strict": true, 22 | "noImplicitAny": true, 23 | "strictNullChecks": true, 24 | 25 | // js 26 | "allowJs": true, 27 | "checkJs": false, 28 | "allowSyntheticDefaultImports": true, 29 | 30 | // module resolution options 31 | "moduleResolution": "node", 32 | "forceConsistentCasingInFileNames": true, 33 | "esModuleInterop": true, 34 | 35 | // paths: 36 | "baseUrl": "./", 37 | "paths": { 38 | "@/*": ["./src/*"] 39 | } 40 | }, 41 | 42 | "exclude": ["scripts", "webpack", "dist", "node_modules", "**/*.spec.ts"], 43 | 44 | // ? .jsx seems useless 45 | "include": ["src/*.d.ts", "src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"] 46 | } 47 | -------------------------------------------------------------------------------- /webpack/webpack-defines.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | const dirs = { 4 | // path to the Src dir 5 | src: path.join(__dirname, '../src'), 6 | // path to the Output dir 7 | dist: path.join(__dirname, '../dist'), 8 | // path to your html files 9 | public: path.join(__dirname, '../public') 10 | } 11 | 12 | const subDirs = { 13 | // path to Output sub dir (js, css, fonts, etc.) 14 | // i.g. `dist/assets/css/` & dist/assets/js/ 15 | assets: 'assets/', 16 | 17 | // path to Output sub dir (img, icons, etc.) 18 | // i.g. `dist/static/img/` & `dist/static/fonts/` 19 | static: 'static/' 20 | } 21 | 22 | module.exports = { 23 | ...dirs, 24 | ...subDirs 25 | } 26 | -------------------------------------------------------------------------------- /webpack/webpack-pages.js: -------------------------------------------------------------------------------- 1 | // automation for `HtmlWebpackPlugin` 2 | 3 | // Notes: 4 | // - remember to restart server after new page added 5 | // - link on the html must be with .html suffix (i.g. About) 6 | 7 | const pages = [ 8 | { 9 | // page title 10 | title: 'Home page', 11 | // template name `public/index.html` 12 | template: 'index.html', 13 | // output filename `dist/index.html` 14 | filename: 'index.html' 15 | 16 | // you can pass a hash of configuration options to html-webpack-plugin. 17 | // Allowed values are as follows: 18 | // read more: https://github.com/jantimon/html-webpack-plugin#options 19 | }, 20 | { 21 | title: 'About page', 22 | template: 'about.html', 23 | filename: 'about.html' 24 | } 25 | ] 26 | 27 | module.exports = pages 28 | -------------------------------------------------------------------------------- /webpack/webpack.common.js: -------------------------------------------------------------------------------- 1 | /* Base config: 2 | ========================================================================== */ 3 | 4 | // 5 | const defines = require('./webpack-defines') 6 | const pages = require('./webpack-pages') 7 | 8 | // copy files from dev (i.g. `assets/img/*`) to dist (i.g `static/img/*`) 9 | const CopyWebpackPlugin = require('copy-webpack-plugin') 10 | // extract css from js to another files 11 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 12 | // html support 13 | const HtmlWebpackPlugin = require('html-webpack-plugin') 14 | 15 | // helpers: 16 | // I want one rule for development and production, so I use `isDev` to check the process 17 | const isDev = process.env.NODE_ENV !== 'production' 18 | 19 | module.exports = { 20 | entry: { 21 | app: `${defines.src}/index.ts` 22 | // another app example: 23 | // auth: `${defines.src}/_auth/index.ts` 24 | }, 25 | output: { 26 | path: defines.dist, 27 | // if you need hash: 28 | // filename: `${defines.assets}js/[name].[contenthash].js` 29 | // if you don't need hash: 30 | filename: `${defines.assets}js/[name].js` 31 | }, 32 | 33 | // optimization (chunks) 34 | optimization: { 35 | chunkIds: 'named', 36 | mergeDuplicateChunks: true, 37 | 38 | splitChunks: { 39 | chunks: 'async', 40 | minSize: 20000, 41 | minRemainingSize: 0, 42 | minChunks: 1, 43 | maxAsyncRequests: 30, 44 | maxInitialRequests: 30, 45 | enforceSizeThreshold: 50000, 46 | cacheGroups: { 47 | defaultVendors: { 48 | name: 'vendors', // or comment name to make chunks works 49 | chunks: 'all', 50 | // the way to keep kit in the vendors 51 | test: /[\\/]node_modules[\\/]|[\\/]ui-kit[\\/]/, 52 | priority: -10, 53 | reuseExistingChunk: true 54 | }, 55 | default: { 56 | minChunks: 2, 57 | priority: -20, 58 | reuseExistingChunk: true 59 | } 60 | } 61 | } 62 | }, 63 | 64 | module: { 65 | rules: [ 66 | // js(x) & ts(x) 67 | { 68 | test: /\.(ts|js)x?$/, 69 | exclude: /node_modules/, 70 | use: { 71 | loader: 'babel-loader', 72 | 73 | options: { 74 | // react-refresh example: 75 | // plugins: [isDev && require.resolve('react-refresh/babel')].filter(Boolean) 76 | } 77 | } 78 | }, 79 | 80 | // sass & css 81 | { 82 | test: /\.s(a|c)ss$/, 83 | use: [ 84 | MiniCssExtractPlugin.loader, 85 | 'css-loader', 86 | { 87 | loader: 'postcss-loader', 88 | options: { 89 | postcssOptions: { 90 | plugins: [ 91 | [ 92 | // add more postcss plugins here 93 | // ... 94 | 95 | // https://www.npmjs.com/package/postcss-preset-env 96 | // it's including autoprefixer by default (config is in `package.json`) 97 | // pass `autoprefixer: false` to disable autoprefixer 98 | 'postcss-preset-env' 99 | ] 100 | ], 101 | postcssOptions: { 102 | parser: 'postcss-js' 103 | }, 104 | execute: true 105 | } 106 | } 107 | }, 108 | { 109 | loader: 'sass-loader', 110 | options: { 111 | sourceMap: isDev 112 | } 113 | } 114 | ] 115 | }, 116 | 117 | // svg in js(x) & ts(x) 118 | { 119 | test: /\.svg$/i, 120 | issuer: /\.[jt]sx?$/, 121 | use: [ 122 | // https://react-svgr.com/docs/webpack/ 123 | '@svgr/webpack' 124 | ] 125 | }, 126 | 127 | // fonts 128 | { 129 | test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/, 130 | loader: 'file-loader', 131 | options: { 132 | name: '[name].[ext]' 133 | } 134 | }, 135 | 136 | // images 137 | { 138 | test: /\.(?:ico|gif|png|jpg|jpeg)$/i, 139 | type: 'asset/resource' 140 | } 141 | ] 142 | }, 143 | plugins: [ 144 | // html pages: 145 | 146 | // can be manually (one by one): 147 | // new HtmlWebpackPlugin({ 148 | // title: 'Home page', 149 | // favicon: defines.src + '/shared/misc/favicon.ico', 150 | // template: defines.public + '/index.html', 151 | // filename: 'index.html' // output file 152 | // }), 153 | // new HtmlWebpackPlugin({ 154 | // title: 'About page', 155 | // favicon: defines.src + '/shared/misc/favicon.ico', 156 | // template: defines.public + '/about.html', 157 | // filename: 'about.html' // output file 158 | // }), 159 | 160 | // or by config (from `webpack-pages.js`): 161 | ...pages.map( 162 | page => 163 | new HtmlWebpackPlugin({ 164 | title: page.title, 165 | template: defines.public + '/' + page.template, 166 | filename: page.filename, 167 | // default: 168 | favicon: defines.src + '/shared/misc/favicon.ico' 169 | }) 170 | ), 171 | 172 | // extract css from js / ts files (it's a basic setup to keep css in `css` folder) 173 | // https://webpack.js.org/plugins/mini-css-extract-plugin/ 174 | new MiniCssExtractPlugin({ 175 | // if you need hash: 176 | // filename: `${defines.assets}css/[name].[contenthash].css`, 177 | // if you don't need hash: 178 | filename: `${defines.assets}css/[name].css`, 179 | chunkFilename: '[id].css' 180 | }), 181 | 182 | // copy files from target to destination folder 183 | new CopyWebpackPlugin({ 184 | patterns: [ 185 | // `shared/img` to `dist/static/img` 186 | { 187 | from: `${defines.src}/shared/img`, 188 | to: `${defines.dist}/${defines.static}/img` 189 | }, 190 | 191 | // others: 192 | // `shared/fonts` to `dist/static/fonts` 193 | // { 194 | // from: `${defines.src}/shared/fonts`, 195 | // to: `${defines.dist}/${defines.static}/fonts` 196 | // }, 197 | 198 | // misc 199 | // `shared/misc` to `dist/` 200 | { 201 | from: `${defines.src}/shared/misc`, 202 | to: `${defines.dist}` 203 | } 204 | ] 205 | }) 206 | ], 207 | 208 | resolve: { 209 | alias: { 210 | // no need since I use `tsconfig` & `jsconfig` 211 | // '@': defines.src 212 | }, 213 | extensions: ['.ts', '.tsx', '.js', '.jsx'] 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /webpack/webpack.dev.js: -------------------------------------------------------------------------------- 1 | /* Development build: 2 | ========================================================================== */ 3 | const { merge } = require('webpack-merge') 4 | 5 | // default config 6 | const commonConfig = require('./webpack.common.js') 7 | 8 | module.exports = merge(commonConfig, { 9 | mode: 'development', 10 | devtool: 'inline-source-map', 11 | 12 | // spin up a server for quick development 13 | devServer: { 14 | compress: true, 15 | open: false, 16 | hot: true, 17 | port: 8084, 18 | client: { 19 | progress: false, 20 | overlay: { 21 | errors: false, 22 | warnings: false 23 | } 24 | }, 25 | // to test in other devices: 26 | // allowedHosts: 'all', 27 | 28 | // fix CORS: 29 | historyApiFallback: true 30 | }, 31 | plugins: [ 32 | // react refresh example: 33 | // https://github.com/pmmmwh/react-refresh-webpack-plugin 34 | // new ReactRefreshWebpackPlugin() 35 | ], 36 | resolve: { 37 | extensions: ['.ts', '.tsx', '.js', '.jsx'] 38 | }, 39 | watchOptions: { 40 | // for some systems, watching many files can result in a lot of CPU or memory usage 41 | // https://webpack.js.org/configuration/watch/#watchoptionsignored 42 | // ! don't use this pattern, if you have a monorepo with linked packages 43 | ignored: /node_modules/ 44 | } 45 | }) 46 | -------------------------------------------------------------------------------- /webpack/webpack.prod.js: -------------------------------------------------------------------------------- 1 | /* Production build: 2 | ========================================================================== */ 3 | const { merge } = require('webpack-merge') 4 | const defines = require('./webpack-defines') 5 | 6 | // plugins for production build only: 7 | const JsonMinimizerPlugin = require('json-minimizer-webpack-plugin') 8 | const TerserPlugin = require('terser-webpack-plugin') 9 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') 10 | 11 | // default config 12 | const commonConfig = require('./webpack.common.js') 13 | 14 | module.exports = merge(commonConfig, { 15 | mode: 'production', 16 | devtool: false, 17 | output: { 18 | path: defines.dist 19 | }, 20 | plugins: [ 21 | // compress example: 22 | // new CompressionPlugin({ 23 | // exclude: /\/static/, 24 | // }), 25 | ], 26 | module: { 27 | rules: [] 28 | }, 29 | performance: { 30 | hints: false, 31 | maxEntrypointSize: 512000, 32 | maxAssetSize: 512000 33 | }, 34 | optimization: { 35 | minimize: true, 36 | minimizer: [ 37 | new JsonMinimizerPlugin(), 38 | new TerserPlugin(), 39 | new CssMinimizerPlugin({ 40 | minimizerOptions: { 41 | // no ie please! 42 | // targets: { ie: 11 }, 43 | preset: [ 44 | 'default', 45 | { 46 | discardComments: { removeAll: true } 47 | } 48 | ] 49 | } 50 | }) 51 | ] 52 | } 53 | }) 54 | --------------------------------------------------------------------------------