Skip to content

Instantly share code, notes, and snippets.

@gusalbukrk
Last active July 17, 2023 06:59
Show Gist options
  • Save gusalbukrk/2a47934f45c4cd3d2c6392860a9cc986 to your computer and use it in GitHub Desktop.
Save gusalbukrk/2a47934f45c4cd3d2c6392860a9cc986 to your computer and use it in GitHub Desktop.
#webpack #step-by-step

1. EditorConfig

  • you may need to install a plugin, depending on the editor
  • .editorconfig at the root:
# https://EditorConfig.org

root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

2. Install Webpack

  • npm init -y
  • npm i -D webpack webpack-cli webpack-dev-server
  • npm i -D webpack-merge

3. Config Webpack

  • webpack.common.js:
module.exports = {
  entry: "./src/index.js",
};

// esm
export default {
  entry: "./src/index.js",
};
  • webpack.dev.js:
const path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common");

module.exports = merge(common, {
  mode: "development",
  devtool: "source-map",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
  devServer: {
    // contentBase: "./dist", // contentBase was deprecated in favor of the static option at webpack-dev-server v4
    static: './dist',
    open: true,

    // makes server accessible externally via `your-ip:8080`
    // tip: get ip with `ip address | grep 192.168.`
    // host: "0.0.0.0",
  }
});

// esm
import path, { dirname } from 'path';
import { fileURLToPath } from 'url';
import { merge } from 'webpack-merge';

import common from './webpack.common.js';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default merge(common, {
  mode: "development",
  devtool: "source-map",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  },
  devServer: {
    // contentBase: "./dist", // contentBase was deprecated in favor of the static option at webpack-dev-server v4
    static: './dist',
    open: true,
    // makes server accessible externally via `yourip:8080` (get ip with `ip address | grep 192.168.`)
    host: "0.0.0.0",
  },
});
  • webpack.prod.js:
const path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.common");

module.exports = merge(common, {
  mode: "production",
  output: {
    filename: "main.js",
    path: path.resolve(__dirname, "dist"),
  }
});

// esm
import path, { dirname } from "path";
import { fileURLToPath } from 'url';
import { merge } from "webpack-merge";

import common from "./webpack.common.js";

const __dirname = dirname(fileURLToPath(import.meta.url));

export default merge(common, {
  mode: "production",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "main.[contenthash].js",
  },
});

4. Scripts

  • at package.json:
  "scripts": {
    "start": "webpack serve --config webpack.dev.js",
    "build": "webpack --config webpack.prod.js"
  },

5. Create a css file and import it

  • create a css/sass file
  • in the js file: import "./style.css"

6. Load CSS & SASS

  • npm i -D style-loader css-loader sass-loader sass
  • in webpack.common.js:
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.s[ac]ss$/i,
        use: ["style-loader", "css-loader", "sass-loader"],
      },
    ],
  }

7. Extract CSS

  • move style-loader in webpack.common.js to webpack.dev.js
  • npm i -D mini-css-extract-plugin
  • webpack.prod.js:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

  module: {
    rules: [
      {
        test: /\.css$/,
        use: [{
          loader: MiniCssExtractPlugin.loader,
          options: {
            publicPath: '', // NOTE: publicPath is required
          },
        }, "css-loader"],
      },
      {
        test: /\.s[ac]ss$/,
        use: [{
          loader: MiniCssExtractPlugin.loader,
          options: {
            publicPath: '', // NOTE: publicPath is required
          },
        }, "css-loader", "sass-loader"],
      },
    ],
  },
  plugins: [new MiniCssExtractPlugin()],

8. Minimize CSS

  • npm i -D css-minimizer-webpack-plugin
  • at webpack.prod.js;
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

  optimization: {
    minimize: true,
    minimizer: [
      `...`, // // this is the minimizer overridden value that minimizes javascript 
      new CssMinimizerPlugin(),
    ],
  },

9. Don't output js comments

  • at webpack.prod.js;
const TerserPlugin = require('terser-webpack-plugin');

optimization: {
  minimizer: [
    new TerserPlugin({
      extractComments: false, // don't extract comments to separate file
      terserOptions: {
        format: {
          comments: false, // avoid build with comments
        },
      },
    }),
    new CssMinimizerPlugin(),
  ],
}

10. Load HTML

  • npm i -D html-webpack-plugin
  • NOTE: delete any script/style tag in the template.html
  • webpack.common.js:
const HtmlWebpackPlugin = require("html-webpack-plugin");
  
  plugins: [
    new HtmlWebpackPlugin({
      template: "./src/index.html",
    }),
  ],

11. Cache busting

  • you need to be using HtmlWebpackPlugin, this will automatically insert the script tag in the html
  • in webpack.prod.js:
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "main-[contenthash].js",
  },

  // ...

    plugins: [new MiniCssExtractPlugin({
    filename: "style.[contenthash].css",
  })],

12. Delete previous build files

  • npm i -D clean-webpack-plugin
  • in webpack.prod.js:
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

  plugins: [new CleanWebpackPlugin()],

13. Load images & fonts

  • npm i -D html-loader
  • webpack.common.js:
{
  module: {
    rules: [
      {
        test: /\.html$/i,
        loader: 'html-loader', // if not included, webpack won't parse html and import images/fonts
      },
      {
        test: /\.(png|jpe?g|gif)$/,
        type: "asset/resource", // emits a separate file (replaced file-loader)
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf|svg)$/,
        type: "asset/inline", // inserts inline (replaced url-loader)
      },
    ],
  },
}

14. Minimize image

  • npm i -D image-minimizer-webpack-plugin
  • install format plugins
    • lossless probably won't compress much
    • lossy will compress much more
import ImageMinimizerPlugin from 'image-minimizer-webpack-plugin';

export default = {
  module: {
    rules: [
      {
        test: /\.(jpe?g|png|gif|svg)$/i,
        type: 'asset/resource',
      },
    ],
  },
  plugins: [
    new ImageMinimizerPlugin({
      minimizerOptions: {
        // Lossless plugins
        plugins: [
          ['gifsicle', { interlaced: true }],
          ['jpegtran', { progressive: true }],
          ['optipng', { optimizationLevel: 5 }],
          [
            'svgo',
            {
              plugins: [
                {
                  removeViewBox: false,
                },
              ],
            },
          ],
        ],
      },
    }),
  ],
};

14.1. hash bug

15. Custom output path

  • in webpack.prod.js, at the output object:
    • assetModuleFilename: "assets/img/[name].[hash][ext]",
  • likewise, in webpack.dev.js:
    • assetModuleFilename: '[name][ext]',

16. Add multiple entry points

  • in webpack.common.js:
  entry: {
    // name: path
    main: "./src/index.js",
    other: "./src/other.js"
  },
  • in webpack.prod.js:
  output: {
    filename: "[name].[contenthash].js",
    path: path.resolve(__dirname, "dist"),
  },
  • in webpack.dev.js:
  output: {
    filename: "[name].js",
    path: path.resolve(__dirname, "dist"),
  },

17. Separate main and vendor

  • in webpack.prod.js:
    optimization: {
      splitChunks: {
        cacheGroups: {
          commons: {
            test: /[\\/]node_modules[\\/]/,
            name: "vendor",
            chunks: "all",

            // force the creation of this chunk
            // by ignoring splitChunks default properties
            // (minSize, minChunks, maxAsyncRequests, maxInitialRequests)
            // enforce: true,
          },
        },
      },
    },

18. Bundle size

  • npm i -D webpack-bundle-analyzer
  • webpack.prod.js:
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin;

plugins: [
  new BundleAnalyzerPlugin()
]

19. DON'T Mangle properties' names

  optimization: {
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          mangle: {
            properties: true, // DON'T DO THIS
          }
        }
      }),
    ],
  },

20. EsLint & Webpack integration

  • npm i -D eslint eslint-webpack-plugin
  • npx eslint --init
  • webpack.common.js:
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
  // ...
  plugins: [
    new ESLintPlugin({
      fix: true,
    }),
  ],
  // ...
};
  • .eslintrc.cjs:
// https://eslint.org/docs/user-guide/configuring/configuration-files#configuration-file-formats
// use .eslintrc.cjs when running ESLint in JavaScript packages that specify `"type": "module "`
// Note that ESLint does not support ESM configuration at this time

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'airbnb-base',
  ],
  plugins: [
  ],
  parserOptions: {
    ecmaVersion: 2021, // equivalent to 12
    sourceType: 'module',
  },
  rules: {
    // // this rule enforce or disallow the use of certain file extensions
    // // when `"type": "module"` in `package.json`, must always contain extension
    // 'import/extensions': [
    //   'error',
    //   'always',
    // ],
    // // otherwise, if `commonjs`, extensions can be omitted
    // 'import/extensions': [
    //   'error',
    //   'ignorePackages',
    //   {
    //     js: 'never',
    //     jsx: 'never',
    //     ts: 'never',
    //     tsx: 'never',
    //   },
    // ],
  },
  settings: {
    // fix 'import/no-unresolved' error
    'import/resolver': {
      node: {
        extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
      },
    },
  },
  overrides: [
    {
      files: ['webpack.*.js'],
      rules: {
        'import/no-extraneous-dependencies': [
          'error',
          { devDependencies: true },
        ],
      },
    },
  ],
};

21. eslint-plugin-compat

  • not really necessary when babel's useBuiltIns is enabled
    • because polyfills are added automatically
  • emit error if target environment (at browserslistrc) doesn't support specific feature
  • only lints high level built-in API (e.g. Promise, Set)
  • doesn't lint syntactical features (e.g. class) or methods (e.g. Array.prototype.includes)

21.1. Setup

  • npm i -D eslint-plugin-compat
  • eslintrc.cjs:
{
  // ...
  "extends": ["plugin:compat/recommended"],
}

22. Prettier install

  • npm i -D prettier
  • .prettierrc.cjs
module.exports = {
  singleQuote: true,
};
  • .prettierignore: dist/

23. Prettier & EsLint integration

  • npm i -D eslint-config-prettier eslint-plugin-prettier
    • eslint-config-prettier turns off all rules that conflict with Prettier
    • eslint-plugin-prettier reports Prettier as Eslint issues
  • .eslintrc.js:
{
  extends: [
    "some-other-config-you-use",
    "prettier"
  ],
  // ...
  plugins: ["prettier"],
  rules: {
    'prettier/prettier': 'error',
  },
}

24. StyleLint install

  • npm i -D stylelint stylelint-config-standard
  • .stylelintrc.cjs:
{
  "extends": "stylelint-config-standard"
}

25. Stylelint & Webpack integration

  • npm i -D stylelint-webpack-plugin
  • webpack.common.js:
const StylelintPlugin = require('stylelint-webpack-plugin');

module.exports = {
  // ...
  plugins: [new StylelintPlugin({
    context: "./src", // directory to search for sass/css files
    fix: true,
  })],
};

26. Solving StyleLint & Prettier conflicts

  • npm i -D stylelint-prettier stylelint-config-prettier
    • stylelint-prettier = reports prettier as a stylelint issues
    • stylelint-config-prettier = disable rules that conflict with prettier
  • NOTE: "stylelint-config-prettier" must be the last at extends array
  • .stylelintrc.cjs:
{
  "extends": [
    "stylelint-config-standard",
    "stylelint-config-prettier"
  ],
  "plugins": [
    "stylelint-prettier"
  ],
  "rules": {
    "prettier/prettier": true
  }
}

27. Install Stylelint plugin

  • npm i -D stylelint-order
  • .stylelintrc.cjs:
module.exports = {
  plugins: [
    "stylelint-order".
  ],
  rules: {
    "order/properties-alphabetical-order": true,
  },
};

28. stylelint-scss

  • npm i -D stylelint-scss
  • .stylelintrc.js:
{
  "plugins": [
    "stylelint-scss"
  ],
  "rules": {
    // recommended rules
    "at-rule-no-unknown": null,
    "scss/at-rule-no-unknown": true,
  }
}

29. husky & lint-staged

  • npx mrm lint-staged = install husky & lint-staged; set to always run linters before commit
  • npx husky add .husky/pre-commit "npm test -- --passWithNoTests --selectProjects jest" = always run tests before commit
  • erase lint-staged property created at package.json
  • lint-staged.config.cjs:
// lint-staged will automatically add any fix to the commit
// that's why the linting of test files is done here

module.exports = {
  '*.(js|jsx|cjs|ts|tsx)': `eslint --fix`, // including test files
  '*.(css|scss|sass)': 'stylelint --fix',
  '*.(html|md)': 'prettier --write',
};
Manual install
  • npm i -D husky lint-staged
  • npm set-script prepare "husky install" && npm run prepare
  • npx husky add .husky/pre-commit "npx lint-staged"
  • npx husky add .husky/pre-commit "npm test -- --passWithNoTests --selectProjects jest"
  • add same lint-staged config as above

30. Install babel

  • npm install -D babel-loader @babel/core @babel/preset-env
  • in webpack.prod.js:
module: {
  rules: [
    {
      test: /\.js$/,
      exclude: /(node_modules|bower_components)/,
      use: {
        loader: 'babel-loader',
      }
    }
  ]
}

31. babel config files

  • NOTE: when no browserslist configs are specified: preset-env will transform all ES2015-ES2020 code to be ES5 compatible
  • babel.config.json:
{
  "presets": ["@babel/preset-env"]
}

32. Babel's useBuiltIns

  • babel by itself will only transpile syntax (e.g. arrow functions, spread operator)
  • new built-in APIs like objects (e.g. Promise) and methods (e.g. Array.prototype.includes) require polyfill

32.1. Setup

  • npm i core-js
  • at babel.config.json:
{
  "presets": [
    [
      "@babel/preset-env",
      {
        // 'usage' = add the polyfills needed automatically
        // 'entry' = requires explicit import of core-js
        "useBuiltIns": "usage", 
        "corejs": {
          "version": "3.8", // change it to the last version
          "proposals": true
        }
      }
    ]
  ]
}

33. browserslist & babel

  • babel-loader only at webpack.prod.js (for project without react)
  • file: .browserslistrc
defaults
not IE 11

33.1. babel-loader in both dev and production

  • babel-loader must be moved to webpack.common.js to browserslist settings be applied in both production and dev
    • in practice, there's usually no need to apply babel in development;
    • unless you going to use react that requires babel at development
  • package.json, at scripts:
    • "start": "webpack serve --node-env development --config webpack.dev.js",
    • "build": "webpack --node-env production --config webpack.prod.js"
  • .browserslistrc
[development]
last 2 chrome versions

# browserslist's "defaults" is different from
# default behavior of @babel/preset-env in babel v7
#
# if you want to specify targets only for development
# and use babel's default in production,
# you must delete this file and add to package.json:
#
# "browserslist": {
#   "development": [
#     "last 2 chrome versions"
#   ]
# }

[production]
defaults
not IE 11

33.2. webpack-dev-server bug

  • NOTE:
  • webpack-dev-server is not yet 100% ready for webpack 5
  • a bug (automatic reload doesn't happen) when having a .browserslistrc or an array at target in the webpack config
  • the workaround is to set target: web in the development environment (which overrides default, i.e. .browserslistrc)

33.3. test query

  • current project's browserslist = npx browserslist
  • browsers list from query = npx browserslist "last 2 versions, not dead"
  • coverage = npx browserslist --coverage "defaults"
  • coverage by countries = npx browserslist --coverage=global,US,BR "defaults, not ie 11, not op_mini all"

33.4. 'defaults' query

  • preset-env's behavior is different than browserslist's "defaults"
  • defaults is the same as npx browserslist "> 0.5%, last 2 versions, Firefox ESR, not dead"

34. @babel/eslint-parser

  • npm i -D @babel/eslint-parser = enables eslint support for experimental features (e.g. class properties)
  • .eslintrc.js: parser: '@babel/eslint-parser'
  • .babelrc.cjs: plugins: [ '@babel/plugin-proposal-class-properties' ],

35. React

  • npm i react react-dom

36. Config React in Babel

  • npm i -D @babel/preset-react
  • at babel.config.json add "@babel/preset-react" as the last element in the presets array
  • move the babel loader from webpack.prod.js to webpack.common.js

37. Config React in EsLint

  • npm i -D eslint-plugin-react
  • at .eslintrcjs:
  extends: [
    'airbnb-base',
    "prettier",
    "plugin:react/recommended", // must always be the last
  ],
  settings: {
    "react": {
      "version": "detect"
    }
  },

38. Add styled-components

  • npm i styled-components
  • npm i -D babel-plugin-styled-components = for minification and a better debugging experience
  • add to babel.config.json: "plugins": ["babel-plugin-styled-components"]

39. CSS modules

  • css modules are enabled with a option (modules) passed to the css-loader
  • however it defaults to true if file matches /\.module\.\w+$/
  • this means that *.module.css and *.module.scss modules are load by default

40. Jest

  • npm i -D jest
  • npx jest --init or create jest.config.js

41. Jest Babel

  • babel-jest is automatically installed when installing Jest
  • will automatically transform files if a babel configuration exists in your project

42. Jest & eslint runner

  • npx jest will run eslint as a jest test suite

42.1. jest rules

  • npm i -D eslint-plugin-jest
  • .eslintrc.cjs: "plugins": ["jest"], "extends": ["plugin:jest/all"]

42.2. Setup

  • npm i -D jest-runner-eslint
  • jest.config.js:
{
  projects: [
    {
      displayName: 'jest',
    },
    {
      runner: 'jest-runner-eslint',
      displayName: 'eslint',
    },
  ],
  watchPlugins: ['jest-runner-eslint/watch-fix'],
}
  • .jest-runner-eslintrc.json:
{
  "cliOptions": {
    "fix": true
  }
}

43. Jest handling assets

  • you'll get a error if you import anything that imports any other thing other than a js file (jest can only parse js)

43.1. Handling css, css module & other files

  • at root, create __mocks__/fileMock.js
    • content: module.exports = 'test-file-stub';
  • npm i -D identity-obj-proxy
  • jest.config.js:
// NOTE: if not using jest-runner-eslint
// `moduleNameMapper` goes instead inside the root of the module.exports
  projects: [
    {
      displayName: 'jest',
      "moduleNameMapper": {
        "\\.(jpg|jpeg|png|gif)$": "<rootDir>/__mocks__/fileMock.js",
        "\\.(css|s[ac]ss)$": "identity-obj-proxy", // handles both regular css and css modules
      },
    },
  ]

44. Typescript

  • npm i -D typescript

  • npx tsc --init

    • open the file and edit: "module": "es2015"
  • npm i -D @types/react @types/jest

  • from here, you have 2 options:

    • ts-loader: to compile typescript with ts-loader, not with babel
    • babel typescript preset: to compile typescript with babel

44.1. ts-loader

  • npm i -D ts-loader
  • npx tsc --init, edit:
{
  "compilerOptions": {
    "target": "es2015",
    "module": "es2015",
    "jsx": "react",
    "strict": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}
  • webpack.common.js, add to module.rules:
{
  test: /\.tsx?$/,
  loader: "ts-loader",
},

44.1.1. fork-ts-checker-webpack-plugin

  • npm i -D fork-ts-checker-webpack-plugin = speed up compilation by moving type checking and (optionally) EsLint to a separate process
  • webpack.config.js:
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');

// module.rules
{
  test: /\.tsx?$/,
  loader: 'ts-loader',
  exclude: /node_modules/,
  options: {
    transpileOnly: true
  }
}

plugins: [
  new ForkTsCheckerWebpackPlugin({
    // // optional - move lint to a separate process
    // eslint: {
    //   files: './src/**/*.{ts,tsx,js,jsx}',
    //   formatter: 'basic', // smaller output
    //   // formatter: { type: 'codeframe', options: { linesAbove: 0, linesBelow: 0 }},
    // },
  }),
];

44.2. preset-typescript

  • @babel/preset-typescript can't do type checking, its only job is to compile from ts to js (even if there're errors)
  • you can type check by:
    • run tsc -w in a separate terminal tab
    • or use an editor which supports typescript and will highlight errors (e.g. vscode)

44.2.1. setup

  • npm i -D @babel/preset-typescript
  • at babel.config.js add to presets array: @babel/preset-typescript
  • at webpack.common.js, add to module.rules:
{
  test: /\.(js|ts)x?$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
  },
},
  • tsconfig.json:
{
  "compilerOptions": {
    "target": "es2020", // babel will transpile
    "module": "es2015",
    "allowJs": true,
    "jsx": "react",
    "noEmit": true,
    "strict": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,

    // make `tsc` throw an error if an unsupported feature is used
    // if set true all implementation files must be modules (import or export something)
    "isolatedModules": true
  }
}

44.3. Bug in webpack

  • while using webpack 5 and the last versions of webpack-dev-server
  • warnings related to using features not available in the set target & lib doesn't appear
  • while using ts-loader & @babel/preset-typescript this behavior may be expected, but this also occurs when using tsc

45. Typescript with css modules

  • src/global.d.ts:
declare module '*.module.css' {
  const styles: { [key: string]: string };
  export default styles;
}

declare module '*.module.sass' {
  const styles: { [key: string]: string };
  export default styles;
}

declare module '*.module.scss' {
  const styles: { [key: string]: string };
  export default styles;
}

45.1. Add intellisense

  • npm install -D typescript-plugin-css-modules
  • in vscode, when a js or ts file is opened:
    • ctrl + shift + p, select select typescript version
    • select use workspace version
  • tsconfig.json:
{
  "compilerOptions": {
    "plugins": [{ "name": "typescript-plugin-css-modules" }]
  }
}

46. Eslint & Typescript

  • npm i -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
  • at webpack.common.js, ESLintPlugin options: add extensions: ['js', 'jsx', 'ts', 'tsx'],
  • .eslintrc.js:
  overrides: [
    {
      files: ['*.ts', '*.tsx'],
      extends: [
        'plugin:@typescript-eslint/recommended-requiring-type-checking',
        'plugin:@typescript-eslint/recommended'
      ],
      parser: '@typescript-eslint/parser',
      parserOptions: { project: './tsconfig.json' },
      plugins: [ '@typescript-eslint' ],
      rules: {
        // disable some base rules and enable their typescript-eslint equivalents (Extension Rules) to prevent incorrect errors
        // https://github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#i-am-using-a-rule-from-eslint-core-and-it-doesnt-work-correctly-with-typescript-code
        'no-use-before-define': 'off',
        '@typescript-eslint/no-use-before-define': ['error'],
        'no-shadow': 'off',
        '@typescript-eslint/no-shadow': ['error'],
        'no-unused-vars': 'off',
        '@typescript-eslint/no-unused-vars': ['error'],

        // fix 'missing file extension' error
        'import/extensions': [ 'error', 'never' ],

        // fix 'no-extraneous-dependencies' error in test files
        'import/no-extraneous-dependencies': [
          'error',
          {
            devDependencies: [
              '**/__tests__/**',
              '**/*{.,_}{test,spec}.{ts,tsx}',
            ],
          },
        ],
      },
    },
  ],

47. Jest & Typescript

  • when using ts-loader, you need ts-jest to run tests written in Typescript
  • otherwise, if using @babel/preset-typescript:

47.1. ts-loader, ts-jest

  • jest.config.js:
module.exports = {
  projects: [
    {
      displayName: 'jest';
      // ...
      preset: 'ts-jest', // write tests in typescript
      globals: {
        'ts-jest': {
          tsconfig: 'tsconfig.jest.json',
        },
      },
      transform: {
        "^.+\\.jsx?$": "babel-jest", // to also be able to write tests in javascript
        "^.+\\.tsx?$": "ts-jest",
      },
    }
    // ...
  ],
  // ...
};
  • tsconfig.jest.json:
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    // 'allowSyntheticDefaultImports' is recommended when using webpack
    // but 'esModuleInterop' is best suited for Node (e.g. jest)
    // https://github.com/typescript-cheatsheets/react#import-react
    "esModuleInterop": true
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment