npm package: @siteone/webpack-config
Description: SiteOne webpack config for server generated sites
Author: Jiří Cerhan
Latest version: 4.2.1

# SiteOne Webpack 5 config

This package contains basic webpack configuration for server-generated websites (it is not meant to be used for SPAs). It works best with Craft CMS and [twigpack](https://nystudio107.com/docs/twigpack/) plugin, but it could be used with almost any type of BE frameworks/CMS like wordpress or nette. We don't provide a way how to load your assets in your templates. For Craft CMS we are using [twigpack](https://nystudio107.com/docs/twigpack/) plugin, other than that it's up to you. You can simply use script/style tag or create some automatic solution that parses your manifest.json file and inserts your tags for you.

The goal is to provides webpack configuration that can be updated and maintained from one place and used on multiple projects. It provide basic config that can be used only partially or/and extended to fit your project needs.

It is inspired by [nystudio107/annotated-webpack-config](https://github.com/nystudio107/annotated-webpack-config) and it can be used with [Twigpack plugin for Craft CMS](https://nystudio107.com/docs/twigpack/)

## Installation

```bash
yarn add webpack webpack-cli @babel/core @siteone/webpack-config --dev
```

OR

```bash
npm install webpack webpack-cli @babel/core @siteone/webpack-config --save-dev
```

## Basic Example

Create webpack.common.js

```javascript
const {
  buildWebpackConfig,
  createConfigFunctions,
} = require("@siteone/webpack-config");

const settings = {
  js: {
    entries: {
      test: "./templates/_modules/test/test.js",
    },
    output: {
      path: path.resolve(__dirname, "./web/dist/"),
    },
  },
};

const configFunctions = [
  // Common
  createConfigFunctions.createCommonConfig,
  createConfigFunctions.createManifestConfig,
  // Scss
  createConfigFunctions.createMiniCssExtractConfig,
  createConfigFunctions.createCssLoaderConfig,
  createConfigFunctions.createPostCssLoaderConfig,
  createConfigFunctions.createSassLoaderConfig,
  // JS
  createConfigFunctions.createBabelLoaderConfig,
];

const webpackModernProdConfig = buildWebpackConfig(
  settings,
  configFunctions,
  "production"
);
```

## Features

### Composition

This configuration is split into small blocks that contain configuration only for a certain plugin, loader, etc. With this approach, you can decide to use or omit any part.

For example, there are separate configurations for:

- cssLoader
- sassLoader
- postCss

So you can decide if you would like to use postCss, Sass, or just simple CSS with no further processing. Every part of this configuration export createConfig function. It accepts settings object and returns partial of webpack configuration object.

#### Example:

```javascript
createSassLoaderConfig = (settings) => {
  return {
    module: {
      rules: [
        {
          test: /\.scss$/,
          use: ["sass-loader"],
        },
      ],
    },
  };
};
```

These partials are then merged with webpack-merge plugin.

```javascript
const webpackConfig = merge(
  // other createConfig functions
  createMiniCssExtractConfig(settings),
  createCssLoaderConfig(settings),
  createPostCssLoaderConfig(settings),
  createBabelLoaderConfig(settings)
);
```

This allows you to add your createConfig function and extend your configuration. Or omit any function you don't want to use.

### Legacy and modern mode

Inspired by [nystudio107/annotated-webpack-config](https://github.com/nystudio107/annotated-webpack-config) you can exploit the configuration object to create more different webpack configs.

This means we provide two default configurations. Other than that it's up to you which features you would like to use in each mode.

The difference is that legacy config contains other browserslist than modern and this way you can build modern and legacy assets then distribute these assets to different browsers. So old browsers should still work but bundle for modern browsers doesn't have to be compiled to old js. This way you can save some bundle size.

## API

### `buildWebpackConfig(userSettings, createConfigFunctions, webpackMode, [type])`

Creates a webpack config based on the given configuration and createConfig functions.

#### Arguments

1. `userSettings` (_Object_): Partial of _settings_ object

Settings:

| key                       | type                     | Default (modern/legacy)                                                                                                                                                                                                                                                       | Description                                                                                             |
| ------------------------- | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| assets                    | object                   | see below                                                                                                                                                                                                                                                                     |                                                                                                         |
| assets.filename           | string                   | "assets/[hash][ext][query]"                                                                                                                                                                                                                                                   | [webpack custom output filename](https://webpack.js.org/guides/asset-modules/#custom-output-filename)   |
| js                        | object                   | see below                                                                                                                                                                                                                                                                     |                                                                                                         |
| js.browserslist           | array                    | ["> 1%", "last 2 versions", "Firefox ESR"] / [ "last 2 Chrome versions", "not Chrome < 60", "last 2 Safari versions", "not Safari < 10.1", "last 2 iOS versions", "not iOS < 10.3", "last 2 Firefox versions", "not Firefox < 54", "last 2 Edge versions", "not Edge < 15", ] | [browserslist array definition](https://github.com/browserslist/browserslist#shareable-configs)         |
| js.output                 | object                   | see below                                                                                                                                                                                                                                                                     |                                                                                                         |
| js.output.path            | string                   | "./web/dist/"                                                                                                                                                                                                                                                                 | [webpack output.path](https://webpack.js.org/configuration/output/#outputpath)                          |
| js.output.publicPath      | string                   | "/dist/"                                                                                                                                                                                                                                                                      | [webpack output.publicPath](https://webpack.js.org/configuration/output/#outputpublicpath)              |
| js.output.filename        | string                   | "[name].[contenthash].js" / "[name]-legacy.[contenthash].js"                                                                                                                                                                                                                  | [webpack output.filename](https://webpack.js.org/configuration/output/#outputfilename)                  |
| js.entries                | string \| object         | null                                                                                                                                                                                                                                                                          | [webpack entry](https://webpack.js.org/configuration/entry-context/#entry)                              |
| css                       | object                   | see below                                                                                                                                                                                                                                                                     |                                                                                                         |
| css.browserslist          | array                    | ["> 1%", "last 2 versions", "Firefox ESR"]                                                                                                                                                                                                                                    | [browserslist array definition](https://github.com/browserslist/browserslist#shareable-configs)         |
| css.filename              | string                   | "[name].[contenthash].css"                                                                                                                                                                                                                                                    | [minicssextract filename](https://github.com/webpack-contrib/mini-css-extract-plugin#filename)          |
| css.sassLoaderOptions     | object                   | {}                                                                                                                                                                                                                                                                            | [webpack sass-loader options](https://webpack.js.org/loaders/sass-loader/#options)                      |
| devServer                 | object                   | see below                                                                                                                                                                                                                                                                     |                                                                                                         |
| devServer.port            | number                   | 8080                                                                                                                                                                                                                                                                          | [webpack dev-server port](https://webpack.js.org/configuration/dev-server/#devserverport)               |
| devServer.host            | string                   | localhost                                                                                                                                                                                                                                                                     | [webpack dev-server host](https://webpack.js.org/configuration/dev-server/#devserverhost)               |
| devServer.https           | boolean                  | false                                                                                                                                                                                                                                                                         | [webpack dev-server https](https://webpack.js.org/configuration/dev-server/#devserverhttps)             |
| devServer.contentBase     | string                   | "./templates",                                                                                                                                                                                                                                                                | [webpack dev-server contentBase](https://webpack.js.org/configuration/dev-server/#devservercontentbase) |
| devServer.public          | string                   | null                                                                                                                                                                                                                                                                          | [webpack dev-server public](https://webpack.js.org/configuration/dev-server/#devserverpublic)           |
| devServer.publicPath      | string                   | null                                                                                                                                                                                                                                                                          | [webpack dev-server publicPath](https://webpack.js.org/configuration/dev-server/#devserverpublicpath-)  |
| devServer.sockHost        | string                   | null                                                                                                                                                                                                                                                                          | [webpack dev-server sockHost](https://webpack.js.org/configuration/dev-server/#devserversockhost)       |
| devServer.staticDirectory | string                   | './web/assets'                                                                                                                                                                                                                                                                | [webpack dev-server static.directory](https://webpack.js.org/configuration/dev-server/#devserverstatic) |
| devServer.watchFiles      | string \| string[]       | './templates/\*_/_.twig'                                                                                                                                                                                                                                                      | [webpack dev-server watchFiles](https://webpack.js.org/configuration/dev-server/#devserverwatchfiles)   |
| resolve                   | object                   | see below                                                                                                                                                                                                                                                                     |                                                                                                         |
| resolve.aliases           | object                   | {}                                                                                                                                                                                                                                                                            | [webpack resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealias)                     |
| resolve.extensions        | array                    | []                                                                                                                                                                                                                                                                            | [webpack resolve.extensions](https://webpack.js.org/configuration/resolve/#resolveextensions)           |
| cache.cacheDirectory      | string                   | '.webpack_cache'                                                                                                                                                                                                                                                            |                                                                                                         |
| cache.type                | 'filesystem' \| 'memory' | 'filesystem'                                                                                                                                                                                                                                                                      |                                                                                                         |

2. `createConfigFunctions`(_array_): Array of _createConfig_ functions. Order matters.

3. `webpackMode`(_string_): [Webpack mode](https://webpack.js.org/configuration/mode/)

4. `[type]`("modern" | "legacy"): Default is "modern". Determinate which default settings are loaded and merged with your `userSettings`

#### Returns

(_Object_): Returns an object corresponding to partials returned by provided _creteConfigFunctions_ merged together and combined with settings. It should be partial of webpack config (not validated though).

#### Example

```javascript
const {
  buildWebpackConfig,
  createConfigFunctions,
} = require("@siteone/webpack-config");

const settings = {
  js: {
    entries: {
      test: "./templates/_modules/test/test.js",
    },
    output: {
      path: path.resolve(__dirname, "./web/dist/"),
    },
  },
};

const configFunctions = [
  // Common
  createConfigFunctions.createCommonConfig,
  createConfigFunctions.createManifestConfig,
  createConfigFunctions.createAssetsConfig,
  createConfigFunctions.createOptimizationConfig,
  // Scss
  createConfigFunctions.createMiniCssExtractConfig,
  createConfigFunctions.createCssLoaderConfig,
  createConfigFunctions.createPostCssLoaderConfig,
  createConfigFunctions.createSassLoaderConfig,
  createConfigFunctions.createCssMinimizerConfig,
  // JS
  createConfigFunctions.createBabelLoaderConfig,
];

const webpackModernProdConfig = buildWebpackConfig(
  settings,
  configFunctions,
  "production"
);

// Config with added dev server configuration
const webpackModernDevConfig = buildWebpackConfig(
  modernSettings,
  [createConfigFunctions.createDevServerConfig, ...configFunctions],
  "development"
);
```

### `type createConfig(settings)`

The whole configuration is created by these createConfig functions that accept settings object and returns partial of webpack config, that is later merged together in a specified order.

#### Arguments

1. `settings`(_object_): Settings object read more in buildWebpackConfig section.

#### Reurns

(_object_): Return a partial of webpack config object.

#### Example

```javascript
const createBabelLoaderConfig = (settings) => {
  return {
    module: {
      rules: [
        {
          test: /\.js$/,
          exclude: /node_modules/,
          use: {
            loader: "babel-loader",
            options: {
              cacheDirectory: true,
              sourceType: "unambiguous",
              presets: [
                [
                  "@babel/preset-env",
                  {
                    modules: false,
                    corejs: {
                      version: 2,
                      proposals: true,
                    },
                    useBuiltIns: "usage",
                    targets: {
                      browsers: settings.js.browserslist,
                    },
                  },
                ],
              ],
              plugins: [
                "@babel/plugin-syntax-dynamic-import",
                "@babel/plugin-transform-runtime",
              ],
            },
          },
        },
      ],
    },
  };
};
```

#### Tip

- You can add your custom settings in the settings object
- You can create your own set of createConfig functions and save it to your custom package and then use it together with this package

## Using with Twigpack

Like it was mentioned before this package was created to be used with Craft CMS and [Twigpack plugin](https://nystudio107.com/docs/twigpack/). Twigpack takes care of loading assets to your templates, cache busting, etc.

### Basic example

package.json

```json
"scripts": {
  "build": "webpack --config webpack.prod.js --progress",
  "dev": "webpack serve --config webpack.dev.js"
},
```

webpack.common.js - this is where your config lives

```javascript
const path = require("path");
const {
  buildWebpackConfig,
  createConfigFunctions,
} = require("@siteone/webpack-config");

const commonSettings = {
  js: {
    entries: {
      test: "./templates/_modules/test/test.js",
    },
    output: {
      path: path.resolve(__dirname, "./web/dist/"),
    },
  },
};

const modernSettings = {
  ...commonSettings,
};

const legacySettings = {
  ...commonSettings,
};

const commonConfigFunctions = [
  // Common
  createConfigFunctions.createCommonConfig,
  createConfigFunctions.createManifestConfig,
  createConfigFunctions.createAssetsConfig,
  createConfigFunctions.createOptimizationConfig,
  // Scss
  createConfigFunctions.createMiniCssExtractConfig,
  createConfigFunctions.createCssLoaderConfig,
  createConfigFunctions.createPostCssLoaderConfig,
  createConfigFunctions.createSassLoaderConfig,
  createConfigFunctions.createCssMinimizerConfig,
  // JS
  createConfigFunctions.createBabelLoaderConfig,
];

const webpackModernProdConfig = buildWebpackConfig(
  modernSettings,
  commonConfigFunctions,
  "production"
);

const webpackLegacyProdConfig = buildWebpackConfig(
  legacySettings,
  commonConfigFunctions,
  "production",
  "legacy"
);

const webpackModernDevConfig = buildWebpackConfig(
  modernSettings,
  [createConfigFunctions.createDevServerConfig, ...commonConfigFunctions],
  "development"
);

module.exports = {
  webpackModernProdConfig,
  webpackLegacyProdConfig,
  webpackModernDevConfig,
};
```

webpack.dev.js

```javascript
const { webpackModernDevConfig } = require("./webpack.common");

module.exports = webpackModernDevConfig;
```

webpack.prod.js

```javascript
const {
  webpackModernProdConfig,
  webpackLegacyProdConfig,
} = require("./webpack.common");

module.exports = [webpackModernProdConfig, webpackLegacyProdConfig];
```

```javascript
const {
  webpackModernProdConfig,
  webpackLegacyProdConfig,
} = require("./webpack.common");

module.exports = [webpackModernProdConfig, webpackLegacyProdConfig];
```

twigpack.php

```php
return [
    '*' => [
        'manifest' => [
            'legacy' => 'manifest-legacy.json',
            'modern' => 'manifest.json',
        ],

        'server' => [
            'manifestPath' => '@webroot/dist/',
            'publicPath' => '/',
        ],

        'devServer' => [
            'manifestPath' => 'http://localhost:8080/dist',
            'publicPath' => 'http://localhost:8080/',
        ],
    ]
];
```

This config doesn't support critical CSS generated by webpack. In our opinion, it doesn't make sense as it needs running compiled copy of your website and it doesn't exist at build time. So it's up to you to create some script to generate your critical CSS files. We recommend [critical](https://github.com/addyosmani/critical) js library.