Skip to content

whoisYeshua/webpack-simple-boilerplate

Repository files navigation

Simple Webpack 5 Boilerplate and more

I made a simple template for myself, but maybe it could be helpful for others.

✨ This template Features:

  • webpack - Module and asset bundler
  • webpack-cli - Command line interface for webpack
  • webpack-dev-server - Development server for webpack with live reloading
  • webpack-merge - Combines a common configuration with a specific one for development or production
  • babel-loader - This loader allows transpiling JavaScript files using Babel and webpack
  • sass-loader - Load Sass/SCSS and compile it to CSS
    • sass - is a pure JavaScript implementation of Sass
  • postcss-loader - Loader to process CSS with PostCSS
  • css-loader - Resolve CSS imports & CSS modules
  • style-loader - Inject CSS into the DOM
  • @svgr/webpack - Webpack loader for SVG
  • csv-loader - The loader will translate csv files into JSON
  • xml-loader - The loader will translate xml files into JSON
  • copy-webpack-plugin - Copies individual files or entire directories, which already exist, to the build directory
  • html-webpack-plugin - Generate HTML files from template
  • mini-css-extract-plugin - Extract CSS into separate files
  • ProgressPlugin - This plugin provides a way to customize how progress is reported during a compilation. (You do not need to install this plugin. Webpack comes with this plugin out of the box.). You can also replace this plugin with webpackbar which has more friendly UI.
  • EnvironmentPlugin - inject process.env variables in your output code. (You do not need to install this plugin. Webpack comes with this plugin out of the box.). You can also replace this plugin with webpack.DefinePlugin if you need more control over inserted values.
  • css-minimizer-webpack-plugin - Optimize and minimize CSS assets (Only in production mode)
  • terser-webpack-plugin - This plugin uses terser to minify your JavaScript (You do not need to install this plugin. Webpack v5 comes with the latest terser-webpack-plugin out of the box.) (Only in production mode)
  • webpack-bundle-analyzer - Visualize the size of webpack output files with an interactive zoomable treemap (Only in production mode)
  • eslint-webpack-plugin - is an ESLint plugin for webpack (Only in development mode)
  • fork-ts-checker-webpack-plugin - Webpack plugin that runs typescript type checker on a separate process (Only in development mode)
  • @pmmmwh/react-refresh-webpack-plugin - enable "Fast Refresh" (also previously known as Hot Reloading) for React components. Work closely together with react-refresh (Only in development mode)
  • @babel/preset-env - is a smart preset that allows you to use the latest JavaScript without needing to micromanage which syntax transforms (and optionally, browser polyfills) are needed by your target environment(s). Interacts with .browserslistrc:

    last 2 chrome versions
    last 2 firefox versions
    last 2 safari versions
    
  • @babel/preset-react - Preset for React

  • @babel/preset-typescript - Preset for TypeScript

  • postcss - is a tool for transforming styles with JS plugins
  • autoprefixer - PostCSS plugin to parse CSS and add vendor prefixes to CSS rules.If you need more you can replace it with postcss-preset-env which includes autoprefixer
  • .editorconfig - is helper for maintain consistent coding styles across various editors and IDEs.
  • stylelint-config-standard-scss - Turns on SCSS support & some SCSS rules. By default extends stylelint-config-standard and stylelint-config-recommended-scss.
  • stylelint-config-standard - Turns on additional rules to enforce the common stylistic conventions found within a handful of CSS styleguides.

    [!NOTE] Since the stylelint-config-standard-scss package extends the stylelint-config-standard package, using both may be redundant and problematic. It could result in having two versions of the same dependency in your project, which can cause issues if you update only one of them. However, it can still be useful to have access to new rules provided by this package. Make sure to pay attention to this in your project.

  • stylelint-config-recess-order - Config that sorts related property declarations by grouping together in the rational order

CI

  • husky - Enable Git hooks, like pre-commit, commit-msg etc

  • lint-staged - Run linters on git staged files. Work closely together with husky pre-commit hook. It has its own config, which is located in .lintstagedrc.json

  • commitlint - Lint commit messages. Work closely together with husky commit-msg hook. It has its own config, which is located in .commitlintrc.json

    Expanded by @commitlint/config-conventional - Shareable commitlint config enforcing conventional commits

  • commitizen - is a tool designed to define a standard way of committing rules. It has its own config, which is located in .czrc. You can use it with npm run cm

    Expanded by cz-conventional-changelog - A commitizen propmpts adapter for the Angular version of conventional commits

  • Automates the whole package release workflow including: determining the next version number, generating the release notes, and upgrading the package version. It has its own config, which is located in .releaserc.json - config description. This package is primarily used in a CI environment

    Plugins
    • @semantic-release/commit-analyzer - plugin to analyze commits with conventional-changelog. (You do not need to install this plugin. semantic-release comes with this plugin out of the box). Expanded by conventional-changelog-conventionalcommits

      [!NOTE] Since this is project-specific, I've decided to label the "chore" type as a patch release ({ "type": "chore", "release": "patch" }), which is typically used for updating packages To allow for the possibility of "chore" breaking releases, add the following rule above ({ "type": "chore", "release": "patch" }) for correct rule applies. If you want to use deafult preset settings, you could delete this two lines.

    • @semantic-release/release-notes-generator - plugin to generate changelog content with conventional-changelog (You do not need to install this plugin. semantic-release comes with this plugin out of the box). Expanded by conventional-changelog-conventionalcommits

    • @semantic-release/changelog - plugin to create or update a changelog file

    • @semantic-release/npm - plugin to publish a npm package and bump version in package.json

      [!NOTE] Since this project is not a package for publication. This plugin is only needed to update the version in package.json (this is regulated by private: true in package.json)

    • @semantic-release/github - plugin to publish a GitHub release and comment on released Pull Requests/Issues. (You do not need to install this plugin. semantic-release comes with this plugin out of the box)

    • @semantic-release/git - plugin to commit release assets \ tag version to the project's git repository

    Preset
    • conventional-changelog-conventionalcommits - same preset that used for commitizen (cz-conventional-changelog) and commitlint (@commitlint/config-conventional). Since I have overridden the angular preset used by semantic-release by default, it must be declared in direct dependencies.
  • plop - micro-generator framework that makes it easy to create files

⚠️ Requirements ⚠️

Since some Webpack, ESLint plugins or other dev tools like (semantic-release) require Node.js version ≥ 18.17. you need Node.js 18.17 and above to prepare for future changes. Check recommended version in .nvmrc. You also may need Docker, if you want run production build

⏬ Installation

  1. Clone or download the repo
  2. Browse the downloaded directory
  3. Install dependencies through npm
     npm i

⏩ Commands

Development environment

One-time development build:

  npm run dev

Watcher (will update the build after each change):

  npm run watch

⭐ Dev Server (provides you with a simple web server and the ability to use HMR):

  npm start

Production environment

⭐ One-time production build with Bundle Analazer stats:

  npm run build

Run production build insdide docker on http://localhost/ (you might need start Docker before run this command):

  docker compose up -d --build

Formatting & Linting commands

Format & fix the code by ESLint, Prettier, and Stylelint:

  npm run format

Only checks the code for compliance with rules by ESLint, Prettier, and Stylelint:

  npm run lint

You can also use a specific formatter

  npm run lint:prettier # check for compliance with Prettier rules
  # or
  npm run lint:stylelint # check for compliance with Stylelint rules
  # or
  npm run format:eslint # format & fix the code with ESLint rules

Check out more commands at package.json scripts section.

Misc

Debug result ESLint config (output eslint-output-config.json):

  npm run debug:eslint

Debug result Stylelint config (output stylelint-output-config.json):

  npm run debug:stylelint

Debug result Prettier config (cli output):

  npm run debug:prettier

Generate React component:

npm run generate

Localy check your next release:

npm run semantic-release:local-check

It may be necessary to temporarily remove @semantic-release/github plugin from .releaserc.json

If you want to get a more relevant check (with sync to your GitHub repository state), you could run this:

GH_TOKEN=<YOUR_TOKEN> npx semantic-release --dry-run --no-ci

Getting GH_TOKEN instruction

Create conventional commit:

  npm run cm

Because husky is used in the project, this creates a certain restriction on the naming of scripts, described here.

I personally prefer use this VS Code Extension for creating conventional commits.

Environment comparison

Both environments use webpack.config.js, but each environment has its own features:

Features Development Production
Devtool ✅ - eval-source-map*
devServer
ESLint
TS checks
CSS implementation** ✅ - style-loader ✅ - MiniCssExtractPlugin
Proxy backend requests ✅ - Webpack devServer.proxy ✅ - NGINX proxy_pass
ReactRefreshWebpackPlugin
TerserPlugin
CssMinimizerPlugin
HtmlWebpackPlugin minify
BundleAnalyzerPlugin
Output files name Default Contenthash
Favicon*** 🤔

* You can set eval or false options to increase build speed, but in this case, you should manually set sourceMap to true in css-loader, scss-loader, and postcss-loader.

** mini-css-extract-plugin is more often used in production mode to get separate css files. For development mode (including webpack-dev-server) we use style-loader, because it injects CSS into the DOM using multiple style tags and works faster.

*** In previous commits, I refused to use the clean-webpack-output plugin because I noticed the presence of a native function that appeared in Webpack 5.20+ output.clean. Unfortunately, it has certain problems with the favicon. Therefore, if this bug is not fixed soon, I will return to the previous version (especially since this plugin was updated recently).

Source

HTML - public/index.html

JS/TS - src/

Global CSS - src/styles

Assets - src/assets - all assets that used directly in target code

Fonts - src/assets/fonts

Images/SVG - src/assets/images

Other Assets - src/assets/*

Static files - public/static - files in this folder will be copy to dist root as it is (without any processing). Eg robots.txt.

Useful tips 💡

  • SVG import

    To import SVG as a React component, use deafult svg extension:

    import SvgComponent from '@assets/images/example.svg' // props => React.createElement("svg",...

    to import SVG as an asset (url), add resource query (?url) to svg extension:

    in TSX/JSX/JS/TS files:

    import svgSrc from '@assets/images/example.svg?url' // data:image/svg+xml;base64,PHN2ZyB4bW...

    in CSS/SCSS files:

    .svg {
      background-image: url('@assets/images/example.svg?url'); /* data:image/svg+xml;base64,PHN2ZyB4bW...*/
    }
  • babel.config.cjs

    I set useBuiltIns: 'usage' which automatically detects the polyfills needed to be based on the language features used in your source code. This ensures only the minimum amount of polyfills are included in your final bundle. Additionaly i set proposals: true, for Object.groupBy, since not all major browsers currently support it (31.10.2023).

    presets: [
      [
        '@babel/preset-env',
        {
          useBuiltIns: 'usage',
          corejs: { version: '3.33', proposals: true }, // The version string can be any supported core-js versions
        },
      ],
    ]
  • webpack.production.js

    Set these options in TerserPlugin to remove comments from code and prevent LICENSE.txt files creation.

    optimization: {
      minimizer: [
        new TerserPlugin({
          terserOptions: {
            format: {
              comments: false,
            },
          },
          extractComments: false,
        })
      ],
    },

    In production mode eg HTMLWebpackPlugin minify your HTML code by default

    I'm decide to remove context field from config, since @pmmmwh/react-refresh-webpack-plugin & fork-ts-checker-webpack-plugin incorrectly infer root directory, while set context to src folder.

    • Formatting & Linting commands

    ESLint & Prettier & Stylelint formatting works separately. So, when you run npm run format:eslint it will only fix ESLint rules, but not apply Prettier rules formatting. If you want both run npm run format:eslint && npm run format:prettier or npm run format, which also includes Stylelint formatting.

    To understand which extensions are used in each command, check out package.json scripts section.

    • CI

    If you wish to make a prerelease, you can begin working in the alpha or beta branches.

    If you need to bypass the git commit hooks, use the -n or --no-verify option. For example:

    git commit -n -m "message" # or git commit --no-verify -m "message"

    If you need to bypass husky checks, pass the environment variable HUSKY=0. For example (macOS/Linux):

    HUSKY=0 git commit -m "message" # or for Windows: cross-env HUSKY=0 git commit -m "message"

    If you want to skip semantic-release checks for commit, you could add scope - no-release. For example:

    git commit -m "chore(no-release): message"

    If you want to skip release job for commit, you could add [skip ci] text in your commit message. For example:

    git commit -m "chore: message [skip ci]"

    If you want ti skip all above:

    git commit -n -m "chore(no-release): message [skip ci]"

📝 To Do

In addition

This template is based on the migration of this example to Webpack 5 and also from these sources:

  • webpack Boilerplate - Sensible webpack 5 boilerplate using Babel, PostCSS, and Sass with a hot dev server and an optimized production build.
  • Webpack 5 Boilerplate Template - Simple starter webpack 5 project template supporting SASS/PostCSS, Babel ES7, browser syncing, code linting. Easy project setup having multiple features and developer-friendly tools.
  • Create App - Frontend build config generator