Refactoring Webpack Series 6 - Configuration Files

Refactoring Webpack Series 6 - Configuration Files

I. Basic Usage

The configuration files for webpacks are different. This is because the configuration file for the webpack is a JavaScript file, in which an object for the webpack configuration is exported, and then the webpack is processed according to the properties defined by the configuration.

The conclusion of this article is that the configuration of a webpack can have many different styles and styles. The key is to maintain and understand these configurations easily and consistently within the team. This article is based on webpack@5 Version for instructions.

a. Basic configuration

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './foo.js',
  output: {
    path: path.resolve(__dirname,'dist'),
    filename: 'foo.bundle.js'
  }
}

b. Different profiles

In the course of a general development project, development and production environments are distinguished, so the corresponding configuration files for the webpack should also be separated:

// package.json

"scripts": {
  "build": "webpack --config webpack.prod.config.js",
  "dev": "webpack --config webpack.dev.config.js"
}

c. Other configurations

There are many options for a complete configuration file for webpack, so let's get to know it all:

const path = require('path');

module.exports = {
  mode: "production", //"production" | "development" | "none"
  // Choosing a different mode tells the webpack to use its built-in optimizations accordingly.
  entry: "./app/entry.js", // string | object | array
  // Default to. / src, where the application starts executing and the webpack begins packaging
  output: {
    // Options for webpack output
    path: path.resolve(__dirname, "dist"),
    // The destination path for all output files must be absolute (using the path module of nodejs)
    filename: "[name].js", // string(default)
    // File name template for entry chunk
    publicPath: "/assets/", // string
    // Output Parse File Directory, url relative to html page
    library: {
      type: "umd", // Common Module Definition
      // Type of export Library
      name: "MyLibrary", // string | string[]
      // Name of export Library
    },
    uniqueName: "my-application",
    // The unique name of this build to avoid conflicts with other builds in the same HTML
    name: "my-config",
    // The name of the configuration, shown in the output
  },
  module: {
    // Module configuration related
    rules: [
      // Module Rules (Configure loader, Parser Options)
      {
        // Matching Conditions:
        test: /\.jsx?$/,
        include: [
          path.resolve(__dirname, "app")
        ],
        exclude: [
          path.resolve(__dirname, "app/demo-files")
        ]
        // These are matching criteria, each accepting a regular expression or string
		//Testing and containment have the same behavior and must match
		//Exclusion does not match (prior to testing and inclusion)
		//Best practices:
		//- Use RegExp only in tests and file name matching
		//-Use absolute path arrays to match full paths in include and exclude
		//-Avoid exclusion as much as possible and prefer to include
		//Each condition can also receive objects with an "and", "or" or "not" attribute
		//This is a series of conditions.
        issuer: /\.css$/,
        issuer: path.resolve(__dirname, "app"),
        issuer: { and: [ /\.css$/, path.resolve(__dirname, "app") ] },
        issuer: { or: [ /\.css$/, path.resolve(__dirname, "app") ] },
        issuer: { not: [ /\.css$/ ] },
        issuer: [ /\.css$/, path.resolve(__dirname, "app") ], // like "or"
        // Execute Actions:
        // Single loader:
        loader: "babel-loader",
        // Applied loader, relative to context resolution
        // Configuration options for loader
        options: {
          presets: ["es2015"]
        },
        // Apply options corresponding to multiple composite loaders and loaders
        use: [
          "htmllint-loader",
          {
            loader: "htmllint-loader",
            options: {
             //...
            }
          }
        ],
        type: "javascript/auto",
        // Specify the type of module
      },
      {
        oneOf: [
          // ...(rules)
        ]
        // Use only one set of rules
      },
      {
        // ... (conditions)
        rules: [
          // ...(rules)
        ]
        // Use all these nested rules (combined with conditions of use)
      }
    ]
  },
  resolve: {
    // Options for parsing module requests
    // Module resolution not applicable to loader
    modules: ["node_modules",path.resolve(__dirname, "app")],
    // Find the directory of modules (in array order)
    extensions: [".js",".json",".jsx",".css"],
    // Extensions used
    alias: {
      // List of module name aliases
      // Aliases are imported relative to the current context
      "module": "new-module",
      // Alias:'module'- >'new-module' and'module/path/file'- >'new-module/path/file'
      "only-module$": "new-module",
      // Alias "only-module" -> "new-module", but does not match "only-module/path/file" -> "new-module/path/file"
      "module": path.resolve(__dirname, "app/third/module.js"),
      // Aliases "module" -> "/app/third/module.js" and "module/file" caused errors
      "module": path.resolve(__dirname, "app/third"),
      // Aliases are "module" ->"/ app/third"and "module/file" ->"/ app/third/file"
      [path.resolve(__dirname, "app/module.js")]: path.resolve(__dirname, "app/alternative-module.js"),
      // Alias "/app/module.js" ->"/app/alternative module.js"
    }
  },
  performance: {}, // Not available
  devtool: "source-map",
  // Enhance debugging capabilities by providing very detailed meta-information of source maps to browser debugging tools
  // But at the expense of build speed!
  context: __dirname, // string (absolute path!)
  // Home directory of webpack
  // entry and module.rules.loader options are resolved relative to this directory
  target: 'web',
  // Since JavaScript can write both server-side and browser-side code, the webpack provides a variety of deployment target s, with the web representing the browser-side and the default being browserlist
  devServer: {
    proxy: {}, // agent
    static: path.join(__dirname, 'public'), //boolean | string |  array | object, static file location
    compress: true, // Whether to turn on gzip compression
    historyApiFallback: true, // Don't know how to use it
    hot: true, // Hot Update
    https: false,
  },
  plugins: [], //Plug-in unit
  optimization: {
    // Optimize Options
    chunkIds: "size",
    // How to generate an id for a module, telling the webpack which algorithm to use when choosing a module id
    moduleIds: "size",
    // Module id generation method that tells webpack which algorithm to use when selecting a module id
    mangleExports: "size",
    // Rename the export name to a shorter name
    minimize: true,
    // Minimize Output Files
    minimizer: [new CssMinimizer(), "..."],
    // Minimum value for output file
    splitChunks: {
      cacheGroups: {
        "my-name": {
          // Define module groups with specific properties
          test: /\.sass$/,
          type: "css/mini-extract"
        }
      }
    }
  }
}

2. Export Method

In addition to exporting individual configurations, there are several ways to export configurations that meet more requirements.

A. Export as a function

The distinction between development and production environments can also be handled by exporting to a function that contains two parameters:

  • Parameter 1 is the environment
  • Parameter 2 is the configuration item passed to the webpack
module.exports = funtion(env, argv) {
  return {
    mode: env.production ? 'production':'development',
    devtool: env.production ? 'source-map' : 'eval',
    plugins: [
      new TerserPlugin({
        terserOptions: {
          compress: argv.mode === 'production'
        }
      })
    ]
  }
}

b. Export to Promise

When a configuration variable needs to be loaded asynchronously, the webpack executes the function, exports a configuration file, and returns a Promise. Webpack supports using Promise.all([your promise]) exports multiple promises:

module.exports = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        entry: './app.js',
        // ...
      })
    }, 5000)
  })
}

c. Exporting multiple configurations

In addition to exporting a single configuration object/function, you may also need to export multiple configurations ( webpack@3 Above). When the webpack runs, all configuration items are built, such as libraries built with multiple targets such as AMD and CommonJS:

module.exports = [
  {
    output: {
      filename: './dist-amd.js',
      libraryTarget: 'amd'
    },
    name: 'amd',
    entry: './app.js',
    mode: 'production'
  },
  {
    output: {
      filename: './dist-commonjs.js',
      libraryTarget: 'commonjs',
    },
    name: 'commonjs',
    entry: './app.js',
    mode: 'production'
  }
]

Note: If you only pass in a--config-name name name identity, the webpack will only build the specified configuration item.

d.dependencies (based on c)

In case one of your configurations depends on the output of another, you can specify a dependencies list using a dependencies list:

module.exports = [
  {
    name: 'client',
    target: 'web',
    // ...
  },
  {
    name: 'server',
    target: 'node',
    dependencies: ['client'],
  },
]

e.parallelism (based on c)

If you have exported multiple configurations, you can use the parallelism option in the configuration to specify the maximum number of concurrencies compiled. ( webpack@5.22 Above)

module.exports = [
  {
    //config-1
  },
  {
    //config-2
  },
];
module.exports.parallelism = 1;

3. Use other languages

Webpack supports writing configuration files in a variety of programming languages and data description formats. You can find a list of currently supported file types in node-interpret, which the webpack can handle.

a.TypeScript

npm install --save-dev typescript ts-node @types/node @types/webpack

// If you use webpack-dev-server, you also need to install the following dependencies

npm install --save-dev @types/webpack-dev-server

Once the installation is complete, you can start writing the configuration file, as shown in the following example:

// webpack.config.ts

import * as path from 'path';
import * as webpack from 'webpack';
// If you need to configure devServer, you need to introduce the following
import 'webpack-dev-server'

const config: webpack.Configuration = {
  mode: 'production',
  entry: './foo.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'foo.bundle.js'
  }
}

export default config;

This example requires typescript version 2.7 and above, and in tsconfig. Two configuration items, esModuleInterop and allowSyntheticDefaultImports, are added to the compilerOptions of the JSON file.

It is important to note that you need to ensure tsconfig. The value of the module option in json's compilerOptions is commonjs, otherwise the webpack will fail and error because ts-node does not support module specifications other than commonjs.

There are three ways you can set up a module:

  • Direct tsconfig.json file
  • Modify tsconfig.json and add ts-node settings
  • tsconfig-paths

The first way is to open your tsconfig.json file, find the configuration for compilerOptions, and then set the target and module options to "ES5" and "CommonJs" (you can also not display the configuration for writing modules when target is set to ES5).

// tsconfig.json

{
  "compilerOptions": {
    "target": "ES5",
    "module": "ESNext"
  }
}

The second method is to add the ts-node settings:

You can keep the "module" configuration for tsc: "ESNext" and set an override for ts-node if you are using a webpack or other build tool:

// tsconfig.json

{
  "compilerOptions": {
    "module": "ESNext",
  },
  "ts-node": {
    "compilerOptions": {
      "module": "CommonJS"
    }
  }
}

The third way is to install the npm package tsconfig-paths first, as follows:

npm install --save-dev tsconfig-paths

After installation, you can create a separate TypeScript configuration file for the webpack configuration as follows:

// tsconfig-for-webpack-config.json

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "esModuleInterop": true
  }
}

ts-node can use the environment variable process provided by tsconfig-paths. Env. TS_ NODE_ PROJECT to find tsconfig.json file path.

Process. Env. TS_ NODE_ The PROJECT variable is set as follows:

// package:json

{
  "scripts": {
    "build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack-config.json\"webpack"
  }
}

The cross-env was added because we are using TS_directly NODE_ PROJECT encountered feedback from unrecognized command error "TS_NODE_PROJECT", which was resolved after adding cross-env.

b.CoffeeScript

Similar to Typescript, you need to install its dependencies before using CoffeeScript, as follows:

npm install --save-dev coffeescript

Then write a configuration file:

// webpack.config.coffee

HtmlWebpackPlugin = require('html-webpack-plugin')
webpack = require('webpack')
path = require('path')

config = 
  mode: 'production'
  entry: './path/to/my/entry/file.js'
  output: 
    path: path.resolve(__dirname, 'dist')
    filename: 'my-first-bundle.js'
  module: rules: [{
    test: /\.(js|jsx)$/
    use: 'babel-loader'
  }]
  plugins: [
    new HtmlWebpackPlugin(template: './src/index.html')
  ]
  
module.exports = config

c.Babel and JSX

First, as with the above two approaches, you need to install some necessary dependencies, as shown below:

npm install --save-dev babel-register jsxobj babel-preset-es2015

To write. babelrc profile:

{
  "presets": ["es2015"]
}

Write a webpack configuration file:

// webpack.config.babel.js

import jsxobj from 'jsxobj'

// Example of plug-in introduction
const CustomPlugin = (config) => ({
  ...config,
  name: 'custom-plugin'
})

export default (
  <webpack target="web" watch mode="production">
    <entry path="src/index.js" />
    <resolve>
      <alias
       {...{
         react: 'preact-compat',
         'react-dom': 'preact-compat'
       }}
      />
    </resolve>
    <plugins>
      <CustomPlugin foo="bar" />
    </plugins>
  </webpack>
)

If you use Babel elsewhere and the modules value is set to false, you must maintain two copies. babelrc files, or you can replace the import jsxobj from'jsxobj'in the example above with const jsxobj = require('jsxobj'). And replace the new export syntax with module.exports, because node does not yet support module syntax for ES6.

4. Notes

  1. The webpack fully follows the CommonJS module specification
  2. You can import other files through require()
  3. Tool functions that can be downloaded using npm through require()
  4. You can write with ternary operators
  5. You can assign value s using constants or variables
  6. Write and execute functions to generate partial configurations
  7. Configuration files should not be too long and need to be split up if necessary, such as distinguishing environments

This is the end of the article. Interested partners appreciate every point of interest.

Keywords: Javascript Front-end Webpack

Added by Bee on Thu, 06 Jan 2022 19:27:30 +0200