# @gravity-ui/app-builder &middot; [![npm package](https://img.shields.io/npm/v/@gravity-ui/app-builder)](https://www.npmjs.com/package/@gravity-ui/app-builder) [![CI](https://img.shields.io/github/actions/workflow/status/gravity-ui/app-builder/.github/workflows/ci.yml?branch=main)](https://github.com/gravity-ui/app-builder/actions/workflows/ci.yml?query=branch:main)

Develop and build your client-server projects, powered by typescript and webpack.

## Install

```shell
npm install --save-dev @gravity-ui/app-builder
```

`@gravity-ui/app-builder` is ESM-only and requires Node.js 20.19 or newer. Use `import`
or dynamic `import()` to load its API; CommonJS `require()` is not supported.

## Usage

`@gravity-ui/app-builder` provides CLI (`npx app-builder`). You can view available commands with the `--help` flag.

### Develop your project

```sh
npx app-builder dev # to view supported options add the --help flag.
```

### Build your project

```sh
npx app-builder build # to view supported options add the --help flag.
```

### Examples

- [Library build](examples/library)
- [SSR application](examples/ssr)

### Profile a Rspack build

```sh
npx app-builder dev --rspack-profile 'rspack_core=info'
```

See [Rspack profiling](docs/rspack-profiling.md) for trace lifecycle, filters, environment
variables, and Perfetto analysis.

### Configuration

You can use any of these files:

- app-builder.config.ts
- app-builder.config.mts
- app-builder.config.js
- app-builder.config.mjs
- app-builder.config.cjs
- app-builder.config.json
- app-builder property in your package.json

You can also specify a custom filename using the `--config` flag

#### TypeScript/JavaScript

```ts
import {defineConfig} from '@gravity-ui/app-builder';

export default defineConfig({
  client: {
    // client settings
  },
  server: {
    // server settings
  },
});
```

#### Conditional config

If the config needs to be conditionally determined, it can export a function instead:

```ts
import {defineConfig} from '@gravity-ui/app-builder';

export default defineConfig(
  async function (
    /** @type dev | build */
    command,
    /**
     * values specified with --env flag
     *
     * @type {[k in string]: string}
     *
     * @example
     *   With follow command:
     *       app-build dev --env=path.to.member1=value1 --env=path.to.member2=value2
     *   you get:
     *       env = {path: {to: {member1: 'value1', member2: 'value2'}}}
     */
    env,
  ) {
    return {
      verbose: command === 'dev',
      client: {
        // client settings
      },
      server: {
        // server settings
      },
    };
  },
);

export default config;
```

#### package.json

```json
{
  "app-builder": {
    "client": {
      // client settings
    },
    "server": {
      // server settings
    }
  },
  "scripts": {
    "dev": "app-builder dev",
    "build": "app-builder build"
  }
}
```

### Common

- `target` (`client | server`) — select compilation unit.
- `verbose` (`boolean`) - turn on verbose output.

#### Environment Variables

`app-builder` automatically injects environment variables during the build process that are available in your application code:

- `process.env.PUBLIC_PATH` — automatically set to the resolved public path value (including CDN URLs if configured). This allows your application code to dynamically access the correct resource URLs at runtime.

  ```ts
  // In your application code, you can access:
  const publicPath = process.env.PUBLIC_PATH; // e.g., "https://cdn.example.com/build/" or "/build/"

  // Useful for dynamically loading assets or configuring Module Federation
  const assetUrl = `${process.env.PUBLIC_PATH}images/logo.png`;
  ```

  **Note**: On the server side, `process.env.PUBLIC_PATH` is only available when using SWC compiler (`compiler: 'swc'`). With TypeScript compiler, this variable is not injected.

- `process.env.NODE_ENV` — current environment (`'development'` | `'production'`)
- `process.env.IS_SSR` — boolean flag indicating if code is running in SSR context

### Server

`app-builder` compiles server with typescript.
Default folder for server code is `src/server`. There is must be file `tsconfig.json`

```json
{
  "compilerOptions": {
    "outDir": "../../dist/server"
  }
}
```

and `index.ts` - server entrypoint.

`outDir` - must be configured to place compiled files to `{rootDir}/dist/server`.
The server is started with the command `node {rootDir}/dist/server/index.js`.

#### Options

All server settings are used only in dev mode:

- `port` (`number | true`) — specify port that server listens. The port will be used to
  pass through requests from the client to the server. If set to `true`, the port will be selected automatically.
  The server is started with the command `APP_PORT=${port} node dist/${outputPath}/index.js --port ${port}`.
- `watch` (`string[]`) — by default `app-builder` monitors only `src/server` directory.
  If you need to watch other directories, specify them here.
- `watchThrottle` (`number`) — use to add an extra throttle, or delay restarting.
- `inspect/inspectBrk` (`number | true`) — listen for a debugging client on specified port.
  If specified `true`, try to listen on `9229`.
- `compiler` (`'typescript' | 'swc'`) — choose TypeScript compiler for server code compilation.
  Default is `'typescript'`. Set to `'swc'` for faster compilation with SWC.
- `outputPath` (`string`) — custom output path for compiled server code relative to `dist` directory.
  Default: `server`. Use this when your `server` entrypoint changed from `dist/server` to a different location (e.g., `package/src/server` for path `dist/package/src/server` in monorepo setups).

### Client

`app-builder` bundles client with [webpack](https://webpack.js.org). Client code must be in `src/ui` folder.
`src/ui/entries` - each file in this folder is used as entrypoint. `dist/public/build` is output directory for bundles.

#### Runtime public path (`window.__PUBLIC_PATH__`)

`window.__PUBLIC_PATH__` is a global variable you set in your HTML page — it is **not** a config option. The build-time `publicPath` and `publicPathPrefix` options only affect the dev server and the value of `process.env.PUBLIC_PATH`; they do **not** control where the browser fetches async chunks at runtime. That base URL comes from `window.__PUBLIC_PATH__`.

`app-builder` prepends a tiny bootstrap module to every client entry that overrides webpack's [`__webpack_public_path__`](https://webpack.js.org/guides/public-path/#on-the-fly) from this global:

```js
__webpack_public_path__ = window.__PUBLIC_PATH__;
```

Define it in the page **before** any bundle is loaded — e.g. in the `<head>` of `index.html`:

```html
<script>
  window.__PUBLIC_PATH__ = '/'; // path under which the build output is served
</script>
```

Set it to wherever the `dist/public/build` output is actually served: `'/'` if assets are served from the site root, `'/build/'` for the default layout, or a full origin such as `'https://cdn.example.com/build/'` for a CDN.

If dynamic imports 404 at `/build/js/...` while other assets load from `/js/...`, or HMR breaks in dev, set this global — changing `publicPath` in config will not help.

#### Options

All paths must be specified relative `rootDir` of the project.

- `modules` (`string[]`) — Tell webpack what directories should be searched when resolving modules. `modules` automatically
  populates with `baseUrl` from `src/ui/tsconfig.json`.
- `alias` (`Record<string, string>`) — Create aliases to import or require certain modules more easily, [more](https://webpack.js.org/configuration/resolve/#resolvealias)

With this `{rootDir}/src/ui/tsconfig.json`:

```json
{
  "compilerOptions": {
    "baseDir": ".",
    "paths": {
      "~units": ["units/*"]
    }
  }
}
```

`modules` will contain `["{rootDir}/src"]` and aliases - `{"~units": ["{rootDir}/src/units"]}`;

- `includes` (`string[]`) — additional compilation paths. Example: `includes: ['node_modules/my-lib', 'src/shared']`
- `images` (`string[]`) — Additional paths for images. Example: `images: ['node_modules/my-lib/img']`
- `icons` (`string[]`) — Additional paths for svg icons. By default, all svgs with paths including `icons/` will be processed.
  Example: `icons: [node_modules/@fortawesome/fontawesome-pro/svgs]`
- `publicPathPrefix` (`string`) — publicPath prefix, will be added to `/build/`
- `publicPath` (`string`) — publicPath for bundler, this option has higher priority than publicPathPrefix
- `outputPath` (`string`) — Build directory for output, default: `dist/public/build` and `dist/ssr` - for SSR
- `assetsManifestFile` (`string`) — File name for assets manifest, default: `assets-manifest.json`
- `symlinks` (`boolean`) — Follow symbolic links while looking for a file. [more](https://webpack.js.org/configuration/resolve/#resolvesymlinks)
- `externals` — specify dependencies that shouldn't be resolved by webpack, but should become dependencies of the resulting bundle. [more](https://webpack.js.org/configuration/externals/)
- `node` — include polyfills or mocks for various node stuff. [more](https://webpack.js.org/configuration/node/)
- `fallback` — Redirect module requests when normal resolving fails. [more](https://webpack.js.org/configuration/resolve/#resolvefallback)
- `polyfill` — allow enable Node.js `process` object polyfill.
- `hiddenSourceMap` (`boolean=true`) - if `false` - source maps will be generated for prod builds
- `disableSourceMapGeneration` (`boolean`) — disable sourcemap generation;
- `definitions` — add additional options to DefinePlugin. [more](https://webpack.js.org/plugins/define-plugin/#usage)
- `svgr` (`SvgrConfig`) — svgr plugin options. [more](https://react-svgr.com/docs/options/)
- `entry` (`string | string[] | Record<string, string | string[]>`) — entry for bundler, overrides entry which is generated from entries directory
- `entryFilter` (`string[]`) — filter used entrypoints.
- `excludeFromClean` (`string[]`) — do not clean provided paths before build.
- `forkTsCheker` (`false | ForkTsCheckerWebpackPluginOptions`) - config for ForkTsCheckerWebpackPlugin [more](https://github.com/TypeStrong/fork-ts-checker-webpack-plugin#options). If `false`, ForkTsCheckerWebpackPlugin will be disabled.
- `cache` (`boolean | FileCacheOptions | MemoryCacheOptions`) — Cache the generated webpack modules and chunks to improve build speed. [more](https://webpack.js.org/configuration/cache/)
- `bundler` (`'webpack' | 'rspack'`) - Option to choose a bundler.
- `javaScriptLoader` (`'babel' | 'swc'`) - Option to choose a JavaScript loader.
- `babel` (`(config: babel.TransformOptions, options: {configType: 'development' | 'production'; isSsr: boolean}) => babel.TransformOptions | Promise<babel.TransformOptions>`) - Allow override the default babel transform options.
- `babelCacheDirectory` (`boolean | string`) — Set directory for babel-loader cache (`default: node_modules/.cache/babel-loader``)
- `swc` (`(config: SwcConfig, options: {configType: 'development' | 'production'; isSsr: boolean}) => SwcConfig | Promise<SwcConfig>`) - Allow override the default swc configuration.
- `webpack` (`(config: webpack.Configuration, options: {configType: 'development' | 'production'; isSsr: boolean}) => webpack.Configuration | Promise<webpack.Configuration>`) - Allow override the default webpack configuration.
- `rspack` (`(config: rspack.Configuration, options: {configType: 'development' | 'production'; isSsr: boolean}) => rspack.Configuration | Promise<rspack.Configuration>`) - Allow override the default rspack configuration.
- `ssr` - build SSR bundle. The SSR entries should be inside `src/ui/ssr` directory and match the client entries.
  - `noExternal` (`string | RegExp | (string | RegExp)[] | true`) - prevent listed dependencies from being externalized for SSR. By default, all dependencies are externalized.
  - `moduleType`: (`'commonjs' | 'esm'`) - library type for the SSR bundle, by default `commonjs`.

##### Dev build

- `devServer` (`DevServerConfig`) — [webpack-dev-server](https://webpack.js.org/configuration/dev-server/) options with app-builder-specific settings. All options are passed to the underlying dev server and can override defaults.

  App-builder-specific options:

  - `ipc` (`string`) — the Unix socket to listen to. If `ipc` and `port` are not defined, then the socket `{rootDir}/dist/run/client.sock` is used.
  - `port` (`number | true`) — specify a port number to listen for requests on. If `true`, the free port will be selected automatically.
  - `webSocketPath` (`string`) — tells clients connected to devServer to use the provided path to connect. Default is `${publicPathPrefix}/build/sockjs-node`.
  - `webSocketClientPort` (`number`) - tells clients to connect to devServer using this port from a browser. Default is `${devServer.port}`
  - `type` (`'https'`) — allow to serve over HTTPS.
  - `options` (`import('https').ServerOptions`) — allow to provide your own certificate.
  - `writeToDisk` (`boolean | (targetPath: string) => boolean`) — write dev middleware output to disk.

  Commonly used [webpack-dev-server](https://webpack.js.org/configuration/dev-server/) options:

  - `host` (`string`) — hostname to bind the server to. Default is `'0.0.0.0'`.
  - `proxy` (`ProxyConfigArray`) — proxy configuration for API requests. [more](https://webpack.js.org/configuration/dev-server/#devserverproxy)
  - `historyApiFallback` (`boolean | ConnectHistoryApiFallbackOptions`) — enable SPA fallback for client-side routing. [more](https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback)
  - `client` (`boolean | ClientConfiguration`) — browser client settings. [more](https://webpack.js.org/configuration/dev-server/#devserverclient)
    - `overlay` (`boolean | {errors?: boolean, warnings?: boolean, runtimeErrors?: boolean}`) — show compile errors and warnings in the browser overlay.

  Example:

  ```typescript
  import {defineConfig} from '@gravity-ui/app-builder';

  export default defineConfig({
    client: {
      devServer: {
        port: true,
        host: 'localhost',
        historyApiFallback: true,
        proxy: [{context: ['/api'], target: 'http://localhost:3000'}],
        client: {
          overlay: false,
        },
      },
    },
  });
  ```

- `watchOptions` — a set of options used to customize watch mode, [more](https://webpack.js.org/configuration/watch/#watchoptions)
  - `watchPackages` (`boolean`) - watch all changes in `node_modules`.
- `reactRefresh` (`false | (options: ReactRefreshPluginOptions) => ReactRefreshPluginOptions`) — disable or configure `react-refresh` in dev mode, [more](https://github.com/pmmmwh/react-refresh-webpack-plugin/blob/main/docs/API.md#options)
- `detectCircularDependencies` (`true | CircularDependenciesOptions`) - detect modules with circular dependencies, [more](https://github.com/aackerman/circular-dependency-plugin)
- `lazyCompilation` (`true | LazyCompilationConfig`) — enable experimental [lazy compilation](https://webpack.js.org/configuration/experiments/#experimentslazycompilation) feature
  - `true` — enable feature
  - `LazyCompilationConfig`
    - `port` (`number`) — port where to listen to from the server
    - `entries` (`boolean=true`) — if `false` - disables lazy compilation for `src/ui/entries` folder content

##### Production build

- `analyzeBundle` (`true | statoscope | rsdoctor`) — tools to analyze bundle.
  - `true` — enable [webpack-bundle-analyzer](https://github.com/webpack-contrib/webpack-bundle-analyzer) plugin. Report generated to `dist/public/build/stats.html`
  - `statoscope` — enable [statoscope](https://github.com/statoscope/statoscope) plugin. Reports generated to `dist/public/build/stats.json` and `dist/public/build/report.json`
  - `rsdoctor` — enable [Rsdoctor](https://rsdoctor.dev/) plugin (`@rsdoctor/webpack-plugin` or `@rsdoctor/rspack-plugin` depending on the chosen bundler). Defaults to `brief` mode.
- `reactProfiling` (`boolean`) — use react profiler API in production, this option also disable minimization. The API is required by React developers tools for profile.
- `statoscopeConfig` (`Options`) — `@statoscope/webpack-plugin` [configuration options](https://github.com/statoscope/statoscope/tree/master/packages/webpack-plugin#usage). Might be used to override the defaults. Requires `analyzeBundle: statoscope`.
- `rsdoctorConfig` (`RsdoctorRspackPluginOptions`) — Rsdoctor plugin [configuration options](https://rsdoctor.dev/config/options/options). Merged on top of the defaults (`{ output: {mode: 'brief'} }`). Requires `analyzeBundle: rsdoctor`.

  ```ts
  import {defineConfig} from '@gravity-ui/app-builder';

  export default defineConfig({
    client: {
      analyzeBundle: 'rsdoctor',
      rsdoctorConfig: {
        disableClientServer: true,
        output: {
          mode: 'normal',
          reportDir: './reports/rsdoctor',
        },
        features: ['loader', 'plugins', 'bundle'],
      },
    },
  });
  ```

- `cdn` (`CdnUploadConfig | CdnUploadConfig[]`) - upload bundled client files to CDN.
  - `bucket` (`string`) — bucket name
  - `prefix` (`string`) — path to files inside the bucket
  - `region` (`string`) — AWS region or any string
  - `endpoint` (`string`) - cdn host to upload files
  - `publicPath` (`string`) - public path to access files from the browser
  - `compress` (`boolean`) - upload also gzip and brotli compressed versions of files
  - `additionalPattern` (`string[]`) — patterns for uploading additional files. By default, only files generated by webpack are loaded.
- `sentryConfig` (`Options`) — `@sentry/webpack-plugin` [configuration options](https://www.npmjs.com/package/@sentry/webpack-plugin/v/2.7.1).

##### Optimization

- `vendors` (`string[] | (defaultVendors: string[]) => string[]`) — additional libraries or a function returning libraries for a vendor chunk;
- `momentTz` — [settings](https://www.npmjs.com/package/moment-timezone-data-webpack-plugin) for moment-timezone (by default data is truncated);
- `contextReplacement` (`object`)
  - `highlight.js` (`string[]`) — list of language names to include, e.g. `['javascript', 'python', 'bash']`;
  - `locale`: (`string[]=['ru']`) — list of `moment.js` or `day.js` locales to include, e.g. `['de', 'es']`. Locale `En` is always present.
- `safari10` (`boolean`) — Enables `safari10` terser's option. [Terser options](https://github.com/terser/terser#minify-options)
- `transformCssWithLightningCss` (`boolean`) — use [Lighting CSS](https://lightningcss.dev) to transform and minimize css instead of PostCSS and cssnano
- `lightningCssMinimizerOptions` (`(options: LightningCssMinimizerRspackPluginOptions) => LightningCssMinimizerRspackPluginOptions`) - modify or return a custom [LightningCssMinimizerRspackPlugin](https://rspack.dev/plugins/rspack/lightning-css-minimizer-rspack-plugin)
- `terser` (`(options: TerserOptions) => TerserOptions`) - modify or return a custom [Terser options](https://github.com/terser/terser#minify-options).

##### CSS Loader configuration

- `cssLoader` (`Partial<CssLoaderOptions>`) — allows to override default css-loader settings. All options are optional and will be merged with default values.

**CssLoaderOptions interface:**

- `url` (`boolean | {filter: (url: string, resourcePath: string) => boolean}`) — enables/disables `url()`/`image-set()` functions handling. Default: `{filter: (url: string) => !url.startsWith('data:')}` (ignores data URIs)
- `import` (`boolean | {filter: (url: string, media: string, resourcePath: string) => boolean}`) — enables/disables `@import` at-rules handling
- `modules` (`boolean | 'local' | 'global' | 'pure' | 'icss' | CssLoaderModulesOptions`) — enables/disables CSS Modules or ICSS and setup configuration. Default: `{auto: true, localIdentName: '[name]__[local]--[hash:base64:5]', exportLocalsConvention: 'camelCase'}`
- `sourceMap` (`boolean`) — enables/disables source maps. Default: `!disableSourceMapGeneration`
- `esModule` (`boolean`) — use the ES modules syntax
- `exportType` (`'array' | 'string' | 'css-style-sheet'`) — allows exporting styles as array with modules, string or constructable stylesheet

**CssLoaderModulesOptions interface:**

- `auto` (`RegExp | ((resourcePath: string) => boolean) | boolean`) — allows auto enable CSS modules based on filename
- `mode` (`'local' | 'global' | 'pure' | 'icss' | ((resourcePath: string) => 'local' | 'global' | 'pure' | 'icss')`) — setup mode option
- `localIdentName` (`string`) — allows to configure the generated local ident name
- `localIdentContext` (`string`) — allows to redefine basic loader context for local ident name
- `localIdentHashSalt` (`string`) — allows to add custom hash to generate more unique classes
- `localIdentHashFunction` (`string`) — allows to specify hash function to generate classes
- `localIdentHashDigest` (`string`) — allows to specify hash digest to generate classes
- `localIdentHashDigestLength` (`number`) — allows to specify hash digest length to generate classes
- `hashStrategy` (`'resource-path-and-local-name' | 'minimal-subset'`) — allows to specify should localName be used when computing the hash
- `localIdentRegExp` (`string | RegExp`) — allows to specify custom RegExp for local ident name
- `getLocalIdent` (`(context: {resourcePath: string, resourceQuery: string}, localIdentName: string, localName: string, options: CssLoaderModulesOptions) => string`) — allows to specify a function to generate the classname
- `namedExport` (`boolean`) — enables/disables ES modules named export for locals
- `exportGlobals` (`boolean`) — allows to export names from global class or id, so you can use that as local name
- `exportLocalsConvention` (`'asIs' | 'as-is' | 'camelCase' | 'camel-case' | 'camelCaseOnly' | 'camel-case-only' | 'dashes' | 'dashesOnly' | 'dashes-only' | ((className: string) => string)`) — style of exported classnames
- `exportOnlyLocals` (`boolean`) — export only locals
- `getJSON` (`(cssModules: Record<string, string>) => void`) — allows outputting of CSS modules mapping through a callback

For more details, see [css-loader documentation](https://github.com/webpack/css-loader#options).

**Default configuration:**

```ts
{
  url: {
    filter: (url: string) => !url.startsWith('data:'),
  },
  sourceMap: !disableSourceMapGeneration,
  modules: {
    auto: true,
    localIdentName: '[name]__[local]--[hash:base64:5]',
    exportLocalsConvention: 'camelCase',
  },
}
```

##### Client TypeScript types

Ambient types are shipped as two subpaths (similar to `vite/client`):

- `@gravity-ui/app-builder/client` — bundler defaults (CSS modules, static assets, workers, globals)
- `@gravity-ui/app-builder/ui` — React UI overrides (`*.svg` as SVGR component)

React apps should use `ui` (it references `client`):

```ts
/// <reference types="@gravity-ui/app-builder/ui" />
```

`client` includes:

- CSS/SCSS side-effect imports and CSS Modules (`*.module.css`, `*.module.scss`)
- Static assets (`*.png`, `*.jpg`, `*.webp`, fonts, etc.)
- Web workers with `webWorkerHandle: 'loader'` (`*.worker.ts`)
- Runtime globals (`window.__PUBLIC_PATH__`, `window.__REMOTE_VERSIONS__`)
- Injected `process.env` keys (`NODE_ENV`, `PUBLIC_PATH`, `IS_SSR`)

`ui` adds:

- `*.svg` as React component (assumes `icons/` paths or `client.icons` config in TS/TSX)

Ambient type sources live in the package `types/` directory (similar to Vite/Next.js).

Types reflect default `cssLoader` settings (`exportLocalsConvention: 'camelCase'`, default export for modules). Override `cssLoader.modules` options may require local type adjustments.

##### Monaco editor support

- `monaco` (`object`) — use [monaco-editor-webpack-plugin](https://github.com/microsoft/monaco-editor/tree/main/webpack-plugin#monaco-editor-webpack-loader-plugin)

  - `fileName` (`string`) — custom filename template for worker scripts.
  - `languages` (`string[]`) - include only a subset of the languages supported. If you don't need support for all languages, set needed languages explicitly, since it may significantly affect build time.
  - `features` (`string[]`) - include only a subset of the editor features.
  - `customLanguages` (`IFeatureDefinition[]`) - include custom languages (outside of the ones shipped with the `monaco-editor`).

##### WebWorker support

Web workers allow you to run JavaScript code in a separate thread from the main UI thread.
This can improve the performance and responsiveness of your web application by offloading
intensive tasks to the background.

To create a web worker, you need to write a script file that defines the logic of the worker. For example,
this file (my.worker.ts) implements a simple function that adds two numbers and sends the result back to the main thread:

```ts
// my.worker.ts
self.onmessage = async (ev) => {
  const {a = 0, b = 0} = ev.data || {};
  const result = a + b;
  self.postMessage({
    result,
  });
};
```

`app-builder` provides built-in support for web workers for files with the `.worker.[jt]s` suffix. You can choose
between two variants of getting web workers by setting the `webWorkerHandle` option:

- `loader` (default) - use the `worker-loader` to import web workers.
  Content of worker file will be included in main bundle as blob. This variant does not
  support dynamic imports inside worker. For example:

```ts
// main.ts
import MyWorker from './my.worker.ts';

const worker = new MyWorker();
```

In this variant, add ambient types via `@gravity-ui/app-builder/ui` (includes `*.worker.ts` declarations) or declare them locally:

```ts
/// <reference types="@gravity-ui/app-builder/ui" />
```

Alternatively:

```ts
// worker.d.ts
declare module '*.worker.ts' {
  class WebpackWorker extends Worker {}

  export default WebpackWorker;
}
```

- `cdn-compat` - use the webpack 5 web workers [syntax](https://webpack.js.org/guides/web-workers/#syntax)
  to import web workers. This variant allows to use dynamic imports inside worker and load worker bundle from CDN. For example:

```ts
import {Worker} from '@gravity-ui/app-builder/worker';

const MyWorker = new Worker(new URL('./my.worker', import.meta.url));
```

- `none` - disable worker-specific handling.

To use the web worker in your main script, you need to communicate with it using the postMessage and onmessage methods. For example:

```ts
// main.ts

const worker = '...'; // Worker creation, first or second variant

worker.onmessage = ({data: {result}}) => {
  console.log(result);
};

worker.postMessage({a: 1, b: 2});
```

##### Module Federation

Module Federation is a Webpack 5 feature that enables micro-frontend architecture, where JavaScript applications can dynamically load code from each other at runtime.

`app-builder` uses `@module-federation/enhanced` for advanced Module Federation support.

- `moduleFederation` (`object`) — Module Federation configuration

  - `name` (`string`) — unique name of the application in the Module Federation ecosystem. Required parameter.
  - `version` (`string`) — application version. When specified, the entry file will be named `entry-{version}.js` instead of `entry.js`.
  - `disableManifest` (`boolean`) — disable manifest file generation. When `true`, uses regular `.js` files for remote entry instead of manifest files. Default is `false`.
  - `remotes` (`string[]`) — list of remote application names that this application can load. This is a simplified alternative to `originalRemotes` that automatically generates remote URLs based on your public path configuration.

    **How it works:**

    - In **development mode**: Remote URLs are automatically generated using the pattern `{commonPublicPath}{remoteName}/entry.js` (or manifest files if enabled)
    - In **production mode**: You need to ensure remote applications are deployed and accessible at the expected URLs
    - Remote entry files are loaded at runtime to provide federated modules from other micro-frontends

    **File naming patterns** (depends on configuration):

    - With `disableManifest: false` (default): `mf-manifest.json` or `mf-manifest-[version].json` (with versioning)
    - With `disableManifest: true`: `entry.js` or `entry-[version].js` (with versioning)

    **Example:**

    ```ts
    // Simple configuration - URLs auto-generated in development
    remotes: ['header', 'navigation', 'footer'];

    // Results in loading from (in development):
    // - https://localhost:3000/header/mf-manifest.json
    // - https://localhost:3000/navigation/mf-manifest.json
    // - https://localhost:3000/footer/mf-manifest.json
    ```

    **Development vs Production:**

    - **Development**: App-builder automatically starts all remote applications and generates their URLs
    - **Production**: Remote applications must be independently deployed and accessible at the generated URLs

    **Integration with other options:**

    - Works with `enabledRemotes` to selectively enable remotes in development
    - Affected by `remotesRuntimeVersioning` for versioned file names
    - File format controlled by `disableManifest` option

    For more complex scenarios requiring custom URLs or cross-environment configurations, use `originalRemotes` instead.

  - `enabledRemotes` (`string[]`) — list of enabled remotes for module federation. **Development mode only**. If not specified, all remotes from the `remotes` array will be enabled by default.

    **Purpose:**

    - Allows selective enabling/disabling of specific remotes during development
    - Useful for debugging, testing individual micro-frontends, or working with partial system setups
    - Helps reduce development startup time by loading only needed remotes
    - Can be overridden via CLI flag: `--mf-remotes header footer`

    **Loading behavior:**

    **When CDN is disabled:**

    - **Enabled remotes**: Loaded from local development server with version-specific paths
    - **Disabled remotes**: Loaded from `cdnPublicPath` (if configured), otherwise fallback to local paths

    **When CDN is enabled:**

    - **All remotes**: Use common public path, `enabledRemotes` selection has no effect on URLs

    **In production builds:**

    - This option is completely ignored, all configured remotes are included in the bundle

    **Example:**

    ```ts
    // Load all available remotes (header, navigation, footer)
    remotes: ['header', 'navigation', 'footer'];

    // Enable only specific remotes in development
    enabledRemotes: ['header', 'footer']; // navigation will be skipped
    ```

    **CLI Override:**

    ```bash
    # Override enabledRemotes from command line
    npx app-builder dev --mf-remotes header navigation
    ```

    **Note:** This option has no effect in production builds - all configured remotes will be included in the production bundle configuration.

  - `originalRemotes` (`RemotesObject`) — full configuration of remote applications in Module Federation Plugin format. Use this when you need explicit control over remote URLs or for cross-environment deployments.

    **When to use:**

    - Custom remote URLs (different domains, ports, paths)
    - Cross-environment loading (staging, production CDN URLs)
    - Complex deployment scenarios
    - When `remotes` auto-generation doesn't meet your needs

    **Format:**

    ```ts
    originalRemotes: {
      remoteName: 'remoteName@remoteUrl',
    }
    ```

    **Examples:**

    ```ts
    originalRemotes: {
      header: 'header@https://cdn.example.com/header/entry.js',
      footer: 'footer@https://cdn.example.com/footer/entry.js',
    }
    ```

    **Note:** When `originalRemotes` is specified, the `remotes` option is ignored. Use either `remotes` OR `originalRemotes`, not both.

  - `remotesRuntimeVersioning` (`boolean`) — enables runtime versioning for remote applications. When enabled, remote entry files include version information in their filenames, allowing for cache busting and version-specific loading.

    **How it affects file names:**

    - With `disableManifest: false`: `mf-manifest-[version].json` instead of `mf-manifest.json`
    - With `disableManifest: true`: `entry-[version].js` instead of `entry.js`
    - Version is taken from the remote application's `version` configuration

    **Benefits:**

    - **Cache busting**: Different versions get different URLs, preventing browser caching issues
    - **Rollback capability**: Can load specific versions of remotes
    - **Deployment safety**: Gradual rollouts with version-specific remote loading

    **Important considerations:**

    - **Without CDN**: Version is inlined immediately during build time, as single build cannot contain multiple application versions
    - **With CDN**: True runtime versioning is possible, versions are resolved dynamically at runtime
    - **Recommendation**: Use `remotesRuntimeVersioning` together with CDN configuration for full dynamic versioning capabilities

    **Example with versioning enabled:**

    ```ts
    // Host application
    {
      moduleFederation: {
        name: 'shell',
        remotes: ['header', 'navigation'],
        remotesRuntimeVersioning: true, // Enable versioning
      }
    }

    // Remote application (header)
    {
      moduleFederation: {
        name: 'header',
        version: '2.1.0', // This version appears in filename
        exposes: { './Header': './src/Header' }
      }
    }

    // Results in loading: header/mf-manifest-2.1.0.json
    ```

    **Runtime behavior:**

    - The version is resolved at runtime from the remote's manifest or entry file
    - Enables loading different versions of the same remote in different environments
    - Works with both `remotes` and `originalRemotes` configurations

  - `isolateAssets` (`boolean`) - put all assets to a folder with the name of Module Federation app name
  - `isolateStyles` (`object`) — CSS style isolation settings to prevent conflicts between micro-frontends.
    - `getPrefix` (`(entryName: string) => string`) — function to generate CSS class prefix.
    - `prefixSelector` (`(prefix: string, selector: string, prefixedSelector: string, filePath: string) => string`) — function to add prefix to CSS selectors.
  - Also supports all standard options from [@module-federation/enhanced](https://module-federation.io/), except `name` and `remotes`, such as:
    - `filename` — entry file name (default `entry.js`)
    - `exposes` — modules that this application exports
    - `shared` — shared dependencies between applications
    - `runtimePlugins` — plugins for Module Federation runtime

**Host Application Configuration Example:**

Host applications consume remote modules from other micro-frontends:

```ts
export default defineConfig({
  client: {
    moduleFederation: {
      name: 'shell',
      // Simple remotes configuration
      remotes: ['header', 'footer', 'sidebar'],
      shared: {
        react: {singleton: true, requiredVersion: '^18.0.0'},
        'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
        lodash: {singleton: true},
      },
    },
  },
});
```

**Advanced Host Configuration:**

```ts
export default defineConfig({
  client: {
    moduleFederation: {
      name: 'main-shell',
      version: '2.1.0',
      // Detailed remotes configuration
      originalRemotes: {
        header: 'header@https://cdn.example.com/header/entry.js',
        footer: 'footer@https://cdn.example.com/footer/entry.js',
        userProfile: 'userProfile@https://cdn.example.com/user-profile/entry.js',
      },
      remotesRuntimeVersioning: true,
      isolateStyles: {
        getPrefix: (entryName) => `.app-${entryName}`,
        prefixSelector: (prefix, selector, prefixedSelector, filePath) => {
          if (
            [prefix, ':root', 'html', 'body', '.g-root', '.remote-app'].some((item) =>
              selector.startsWith(item),
            ) ||
            filePath.includes('@gravity-ui/chartkit')
          ) {
            return selector;
          }
          return prefixedSelector;
        },
      },
      shared: {
        react: {singleton: true, requiredVersion: '^18.0.0'},
        'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
        lodash: {singleton: true},
      },
    },
  },
});
```

**Remote Application Configuration Example:**

Remote applications expose their modules for consumption by host applications:

```ts
export default defineConfig({
  client: {
    moduleFederation: {
      name: 'header',
      // Expose modules for other applications
      exposes: {
        './Header': './src/components/Header',
        './Navigation': './src/components/Navigation',
        './UserMenu': './src/components/UserMenu',
      },
      shared: {
        react: {singleton: true, requiredVersion: '^18.0.0'},
        'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
        lodash: {singleton: true},
      },
    },
  },
});
```

**Bidirectional Configuration Example:**

Applications can be both host and remote simultaneously:

```ts
export default defineConfig({
  client: {
    moduleFederation: {
      name: 'dashboard',
      version: '1.5.0',
      // Consume remote modules
      remotes: ['charts', 'notifications'],
      // Expose own modules
      exposes: {
        './DashboardLayout': './src/layouts/DashboardLayout',
        './DataTable': './src/components/DataTable',
      },
      shared: {
        react: {singleton: true, requiredVersion: '^18.0.0'},
        'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
        lodash: {singleton: true},
      },
    },
  },
});
```

**Advanced Remotes Configuration Examples:**

```ts
export default defineConfig({
  client: {
    moduleFederation: {
      name: 'advanced-shell',
      version: '3.0.0',

      // Example 1: Simple remotes for development
      remotes: ['header', 'sidebar', 'footer', 'analytics'],

      // Example 2: Enable only specific remotes in development
      enabledRemotes: ['header', 'sidebar'], // Only these will load in dev mode

      // Example 3: Runtime versioning with manifests (production-ready)
      remotesRuntimeVersioning: true, // Enables version-specific loading
      disableManifest: false, // Use mf-manifest-[version].json files

      shared: {
        react: {singleton: true, requiredVersion: '^18.0.0'},
        'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
      },
    },
  },
});

// Alternative: Explicit URLs for production
export default defineConfig({
  client: {
    moduleFederation: {
      name: 'production-shell',

      // Use originalRemotes for explicit control
      originalRemotes: {
        // Static CDN URLs
        header: 'header@https://cdn.company.com/header/mf-manifest-2.1.0.json',
        sidebar: 'sidebar@https://cdn.company.com/sidebar/entry-1.5.0.js',

        // Dynamic remote loading
        analytics: `promise new Promise((resolve) => {
          const remoteUrl = process.env.NODE_ENV === 'production' 
            ? 'https://analytics.cdn.com/entry.js'
            : 'http://localhost:3003/entry.js';
          resolve(\`analytics@\${remoteUrl}\`);
        })`,
      },

      shared: {
        react: {singleton: true, requiredVersion: '^18.0.0'},
        'react-dom': {singleton: true, requiredVersion: '^18.0.0'},
      },
    },
  },
});
```

**Development Workflow with Remotes:**

```bash
# Start host application with all remotes
npx app-builder dev

# Start host with only specific remotes (faster development)
npx app-builder dev --mf-remotes header sidebar

# The above is equivalent to setting enabledRemotes in config:
# enabledRemotes: ['header', 'sidebar']
```
