import { Badge } from '@cerner/webpack-config-terra/package.json?dev-site-package';

<Badge />

# Webpack Config Terra

The webpack-config-terra package contains the base webpack config required to build terra applications. [Webpack](https://webpack.js.org/) is a module bundler used to compile modules with dependencies and generate static assets. Webpack is a very powerful tool that is highly configurable and Terra components rely on specific polyfills, webpack loaders and plugins to render correctly.

## Terra's Configuration Requirements

Below is the list of polyfills, webpack loaders and plugins Terra components rely on:

### JavaScript Loaders

* [babel-loader](https://webpack.js.org/loaders/babel-loader/) - Allows transpiling JavaScript files using [Babel](https://github.com/babel/babel) and webpack.
* [file-loader](https://webpack.js.org/loaders/file-loader/) - Instructs webpack to emit the required object as file and to return its public URL.

### JavaScript Plugins

* [DefinePlugin](https://webpack.js.org/plugins/define-plugin/) - Plugin to define global compile-time values, including:
  * `CERNER_BUILD_TIMESTAMP` - The time that webpack was executed in ISO8601 format.
  * `TERRA_AGGREGATED_LOCALES` - The list of successfully aggregated locales available to the browser. See [i18n config](/dev_tools/terra-toolkit-docs/webpack-config-terra/terra-i-18-n-config-js) for more details about the object structure.
  * `TERRA_THEME_CONFIG` - The scoped and default theme. See [theme config](/dev_tools/terra-toolkit-docs/webpack-config-terra/terra-theme-config-js) for more details about the object structure.

### CSS Loaders and Plugins

* [autoprefixer](https://github.com/postcss/autoprefixer) - Plugin to parse CSS and add vendor prefixes to CSS rules. This should be configured with [`browserslist-config-terra`](https://github.com/cerner/browserslist-config-terra). \*
* [css-loader](https://webpack.js.org/loaders/css-loader/) - The css-loader interprets ``@import`` and ``url()`` like ``import/require()`` and will resolve them. The css-loader is also used to parse CSS Modules.
* [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) - This plugin extracts CSS into separate files and supports on-demand-loading of CSS and SourceMaps.
* [postcss-loader](https://webpack.js.org/loaders/postcss-loader/) - Transforms styles with JS plugins.
* [postcss-assets-webpack-plugin](https://github.com/klimashkin/postcss-assets-webpack-plugin#apply-postcss-plugins-to-webpack-css-asset) - Gets the css, extracted by ExtractTextPlugin and apply postcss plugins to it.
* [postcss-custom-properties](https://github.com/postcss/postcss-custom-properties) - Transforms W3C CSS Custom Properties to static values.\*
* [postcss-rtl](https://github.com/vkalinichev/postcss-rtl) - PostCSS-plugin for RTL-adaptivity.
* [sass-loader](https://webpack.js.org/loaders/sass-loader/) - Loads a SASS/SCSS file and compiles it to CSS.
* [style-loader](https://webpack.js.org/loaders/style-loader/) - Adds CSS to the DOM by injecting a ``<style>`` tag.

### Production Only Plugins

* [clean-webpack-plugin](https://github.com/johnagan/clean-webpack-plugin) -
A webpack plugin to remove/clean your build folder(s) before building.
* [terser-webpack-plugin](https://webpack.js.org/plugins/terser-webpack-plugin/) - minifies your JavaScript.

_\* Required to support IE legacy browsers_

## Configuring Webpack

Terra-toolkit's [default webpack configuration](https://github.com/cerner/terra-toolkit/blob/main/packages/webpack-config-terra/src/webpack.config.js).
* Use when building a standalone site.
* Additional configuration required.
* Can be extended with terra-dev-site plugins.

By using this default configuration, we will manage webpack dependencies and set up translation aggregation. If you choose not to use the default configuration, Toolkit's configuration can  be extend to meet your needs or it can be used as an guide to build your own.

### API

There are several different ways to customize terra's webpack config without overriding it.

#### webpack env

Webpack environment options can be set via the command line. For example:

```bash
webpack --env.disableHotReloading=true
```
Read more on webpack's doc site about [webpack environment options](https://webpack.js.org/api/cli/#environment-options)

|name|Type|Default|Description|
|---|---|---|---|
|`disableAggregateTranslations`|Bool|`false`|Disable the aggregate translations feature.|
|`disableHotReloading`|Bool|`false`|Disable hot reloading, this is generally used by CI.|
|`disableCSSCustomProperties`|Bool|`false`|A webpack environment variable `disableCSSCustomProperties` to disable css custom properties.|
|`defaultTheme`|string|none|Override the default theme set in the terra-theme.config.js file. Useful for testing.|
|`generateLoaderSourceMaps`|Bool|`true` for dev `false` for prod|Used to enable source map generation for prod. This should be used in conjunction with setting the `devtool` webpack option. Caution, This may have a large performance impact, especially with large bundles.|
|`enableAggregateThemes`|Bool|`false`|Enable theme aggregation.|

#### options

Options can be supplied to the terra webpack config as the third parameter after `env` and `argv`. For example:

```js
const webpackConfig = require('webpack-config-terra');

const themeConfig = {
  theme: 'terra-dark-theme',
};

module.exports = webpackConfig({}, {}, {themeConfig});
```

|name|Type|Description|
|---|---|---|
|`themeConfig`|object|Override terra-theme.config.js values. Useful when building multiple sites from one webpack config.|

### Extending the Default Config

1. Create a `webpack.config.js` file.
2. Import terra-toolkit's webpack configuration.
3. Create an app-level webpack configuration which, at a minimum, supplies an entry and an [HtmlWebpackPlugin](https://github.com/jantimon/html-webpack-plugin) entry (version ^3.2.0 or higher).
4. Use [`webpack-merge`](https://github.com/survivejs/webpack-merge) to combine the app config with terra-toolkit's default config. Note: since the default config is an function, it will need to be executed first.

Here is an example app-level webpack configuration:

```javascript
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { merge } = require('webpack-merge');

// Import the terra-toolkit configuration.
const defaultWebpackConfig = require('@cerner/webpack-config-terra');

// Create the app-level configuration
const appWebpackConfig = () => ({
  entry: {
    index: path.resolve(path.join(__dirname, 'lib', 'site', 'Index')),
  },
  plugins: [
      new HtmlWebpackPlugin({
        title: 'My App',
        template: path.join(__dirname, 'lib', 'index.html'),
      }),
    ],
});

// combine the configurations using webpack-merge
const mergedConfig = (env, argv) => (
  merge(defaultWebpackConfig(env, argv), appWebpackConfig(env, argv))
);

module.exports = mergedConfig;
```

#### Translation Aggregation

Terra's supported locales will be aggregated when using the default webpack configuration through the `aggregate-translations` pre-build tool. To customize which translations are aggregated, refer these docs on [aggregating translations](https://github.com/cerner/terra-aggregate-translations#terrai18nconfig-example). Alternatively, if translations are not required, disable translation aggregation within the webpack build by passing the environment variable `--env.disableAggregateTranslations` to the webpack command.

```bash
webpack --env.disableAggregateTranslations
```

### Theme Aggregation

Theme aggregation supports legacy versions of components built before terra transitioned to using the theme-context to apply the current theme to components. Theme Aggregation is turned off by default and enabling it will significantly reduce webpack performance.

Minimum terra component versions that support theme context as of 12/8/2020. Any components not listed here either do not have themeable variables, do not support the theme context and should be replaced, or have been introduced since this list was generated and support the theme context.

  * `terra-abstract-modal@3.25.0`
  * `@cerner/terra-docs@1.0.0`
  * `terra-action-footer@2.42.0`
  * `terra-action-header@2.43.0`
  * `terra-alert@4.29.0`
  * `terra-application-header-layout@3.28.0`
  * `terra-application-links@6.34.0`
  * `terra-application-name@3.30.0`
  * `terra-application-navigation@1.37.0`
  * `terra-application-utility@2.35.0`
  * `terra-application@1.19.0`
  * `terra-avatar@3.3.0`
  * `terra-badge@3.35.0`
  * `terra-brand-footer@2.24.0`
  * `terra-button-group@3.39.0`
  * `terra-button@3.36.0`
  * `terra-card@3.27.0`
  * `terra-cell-grid@1.5.0`
  * `terra-clinical-data-grid@2.25.0`
  * `terra-clinical-detail-view@3.20.0`
  * `terra-clinical-header@3.16.0`
  * `terra-clinical-item-display@3.18.0`
  * `terra-clinical-item-view@3.19.0`
  * `terra-clinical-label-value-view@3.20.0`
  * `terra-clinical-onset-picker@4.21.0`
  * `terra-collapsible-menu-view@6.34.0`
  * `terra-date-input@1.14.0`
  * `terra-date-picker@4.38.0`
  * `terra-date-time-picker@4.38.0`
  * `terra-demographics-banner@3.37.0`
  * `terra-dialog-modal@3.38.0`
  * `terra-dialog@2.42.0`
  * `terra-divider@3.27.0`
  * `terra-dropdown-button@1.14.0`
  * `terra-form-checkbox@4.3.0`
  * `terra-form-field@4.3.0`
  * `terra-form-fieldset@2.42.0`
  * `terra-form-input@3.5.0`
  * `terra-form-radio@4.5.0`
  * `terra-form-select@6.6.0`
  * `terra-form-textarea@4.5.0`
  * `terra-grid@6.21.0`
  * `terra-html-table@1.6.0`
  * `terra-hyperlink@2.34.0`
  * `terra-icon@3.32.0`
  * `terra-image@3.28.0`
  * `terra-layout@4.24.0`
  * `terra-list@4.31.0`
  * `terra-menu@6.34.0`
  * `terra-modal-manager@6.34.0`
  * `terra-navigation-side-menu@2.31.0`
  * `terra-notification-dialog@3.35.0`
  * `terra-overlay@3.49.0`
  * `terra-paginator@2.51.0`
  * `terra-popup@6.35.0`
  * `terra-profile-image@3.30.0`
  * `terra-progress-bar@4.23.0`
  * `terra-search-field@3.51.0`
  * `terra-section-header@2.37.0`
  * `terra-show-hide@2.35.0`
  * `terra-signature@2.30.0`
  * `terra-slide-group@4.21.0`
  * `terra-slide-panel@3.27.0`
  * `terra-spacer@3.40.0`
  * `terra-status-view@4.27.0`
  * `terra-switch@1.0.0`
  * `terra-table@4.8.0`
  * `terra-tabs@6.35.0`
  * `terra-tag@2.35.0`
  * `terra-text@4.31.0`
  * `terra-time-input@4.29.0`
  * `terra-toolbar@1.8.0`

#### Hot Reloading with Webpack Dev Server

Terra's webpack configuration enables hot reloading by default in development mode. Disable this behavior by passing `--env.disableHotReloading` to the cli when running webpack. This is useful to generate the production assets used during testing.

```bash
webpack --env.disableHotReloading
```

#### Development vs Production

The default webpack configuration is a function that will flex between production and development modes when passing the `-p` flag while compiling with webpack. See webpack's documentation on [configuration types](https://webpack.js.org/configuration/configuration-types/) for more information.

#### Duplicate Asset Management

The `@cerner/duplicate-package-checker-webpack-plugin` is used to detect duplicated packages within a generated Webpack bundle. If more than one version of the same package are present in a bundle, the package information will be logged with a Webpack compilation warning.

Some packages can be duplicated safely, so these warnings may not indicate a serious problem. However, some packages are intended to be used as singleton packages. If these singleton packages are duplicated, errors will be logged and the Webpack compilation will fail.

|Package Name|Description|
|---|---|
|`react`|Has undefined behavior when multiple versions are loaded at the same time.|
|`react-dom`|Has undefined behavior when multiple versions are loaded at the same time.|
|`react-intl`|Uses React Context for communication of intl APIs.|
|`react-on-rails`|Uses a singleton registry to manage available components.|
|`terra-breakpoints`|Uses React Context for communication of active breakpoint APIs.|
|`terra-application`|Uses a number of Context-providing components.|
|`terra-disclosure-manager`|Uses React Context for communication of progressive disclosure APIs.|
|`terra-navigation-prompt`|Uses React Context for communication of navigation prompt APIs.|
|`terra-theme-context`|Uses React Context to provide the current active theme name and class.|

If duplicates of the above packages are detected, options for remediation include:

- Updating the dependencies that are causing the duplication. Generally, the above packages should be listed as peerDependencies to prevent duplication.
- Adding a webpack `resolve.alias` to the configuration that will force Webpack to use a single version of the duplicated package. However, this may cause logic to fail if the APIs between the expected versions differ.

#### Disabling CSS Custom Properties

Certain browsers do not support [CSS custom properties](https://caniuse.com/css-variables). If you have a scenario where you are going to need to be in one of those browsers and you only have one supported theme, you can run the following to disable CSS custom properties:

```bash
webpack --env.disableCSSCustomProperties
```

This will shrink the size of css and ensure that the appropriate CSS properties are defined appropriately without using CSS custom properties.
