Skip to content

bundle.config

Build configuration for an extension. Create bundle.config.ts in your extension directory:

ts
export default {
  input: './src/my.extension.ts',
  output: {
    js: './dist/my.extension.bundle.js',
    css: './dist/my.extension.bundle.css',
  },
  namespace: 'BX.MyExtension',
};

JavaScript configuration (bundle.config.js) is also supported.

Options

OptionTypeDescription
inputstringEntry point file (.ts, .js or .css)
outputstring | {js?, css?}Output bundle path(s)
namespacestringGlobal namespace for exports
concat{js?: string[], css?: string[]}Concatenate files in specified order
targetsstring | string[]Browser targets for transpilation
sourceMapsbooleanGenerate source maps
minificationboolean | objectTerser minification options
treeshakeboolean | string | objectRemove unused code. Accepts boolean, Rollup preset ('smallest', 'safest', 'recommended'), or TreeshakingOptions object (default: true)
pluginsPlugin[]Custom Rollup plugins
resolveNodeModulesbooleanResolve dependencies from node_modules
babelbooleanEnable/disable Babel transpilation (default: true)
standaloneboolean | objectStandalone build with inlined dependencies
protectedbooleanProtect from rebuilding
rebuildstring[]Rebuild dependent extensions
transformClassesboolean | string[]Transpile classes — all (true) or by name
emitDeclarationbooleanGenerate .d.ts with namespace declarations (default: true)
safeNamespacesbooleanSafe access to dependency namespaces via optional chaining
cssImagesobjectCSS image processing options
baselinebooleanCheck web feature availability during build (default: true)

CSS-only Extensions

If an extension contains only styles without JavaScript logic, you can use a CSS file as the entry point:

ts
export default {
  input: './src/style.css',
  output: {
    css: './dist/my.extension.bundle.css',
  },
};

With CSS-only builds:

  • No JS bundle is created
  • Only css needs to be specified in output
  • No wrapper index.ts file with style imports is needed

CSS Image Processing

The cssImages option controls how images referenced via url() in CSS are processed.

By default, images under 14 KB are inlined as base64 (SVGs are optimized via SVGO). Larger images are copied to the output directory with relative paths.

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  cssImages: {
    type: 'copy',
  },
};

cssImages Options

OptionTypeDefaultDescription
type'inline' | 'copy''inline'inline — inline small files (base64/SVG), copy — copy all files
maxSizenumber14Size threshold in KB for inlining (only with type: 'inline')
outputstringOutput directory for copied files
absolutePathsbooleanfalseUse absolute paths for images instead of relative

Absolute Paths

With absolutePaths: true, chef automatically computes the extension's public path and generates absolute URLs in CSS:

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  cssImages: {
    absolutePaths: true,
  },
};

Result in CSS:

css
/* Instead of */
.icon { background: url(./images/icon.png); }

/* Becomes */
.icon { background: url(/bitrix/js/ui/buttons/dist/images/icon.png); }

The public path is determined automatically based on the extension's location (/bitrix/js/... for sources, /local/js/... for projects).

Plugins

The plugins option accepts an array of Rollup-compatible plugins. Plugins are added at the end of the build chain, after Chef's built-in plugins.

Installation

Install the plugin in your extension directory:

bash
cd /path/to/my.extension
npm install @rollup/plugin-alias

Usage

ts
import alias from '@rollup/plugin-alias';
import { resolve } from 'node:path';

export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My',
  plugins: [
    alias({
      entries: [
        { find: '@utils', replacement: resolve(import.meta.dirname, 'src/utils') },
      ],
    }),
  ],
};

Multiple Plugins

ts
import alias from '@rollup/plugin-alias';
import replace from '@rollup/plugin-replace';

export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My',
  plugins: [
    alias({
      entries: [
        { find: '@utils', replacement: './src/utils' },
      ],
    }),
    replace({
      __VERSION__: JSON.stringify('1.0.0'),
      preventAssignment: true,
    }),
  ],
};

Resolving node_modules

The resolveNodeModules option enables resolving dependencies from node_modules. By default, Chef treats all npm dependencies as external — they are not included in the bundle.

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My',
  resolveNodeModules: true,
};

When enabled:

  1. Install dependencies: npm install in the extension directory
  2. Dependencies from node_modules will be inlined into the bundle
  3. The bundle size will increase, but the extension becomes independent of npm

TIP

If you need full independence from Bitrix dependencies as well, use standalone mode.

Standalone

A standalone build inlines all Bitrix dependencies and npm packages into a single bundle. See Standalone Build for details.

Simple form:

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  standalone: true,
};

With dependency remapping via remap:

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  standalone: {
    remap: {
      // Bitrix extension → another Bitrix extension
      'ui.type-only-dep': 'ui.forms',

      // Bitrix extension → npm package
      'ui.lexical.core': { npm: 'lexical', from: 'ui.lexical' },

      // Glob pattern
      'ui.lexical.*': { npm: '@lexical/*', from: 'ui.lexical' },
    },
  },
};

The key is a Bitrix extension name (or glob pattern), the value is:

  • string — name of another Bitrix extension whose code should be used instead
  • { npm, from } — npm package (npm) from node_modules of the specified extension (from)

In glob patterns * is replaced with the matched part of the name. For example, 'ui.lexical.*': { npm: '@lexical/*', from: 'ui.lexical' } turns ui.lexical.rich-text into @lexical/rich-text.

Disabling Babel

The babel option allows disabling Babel transpilation. This is useful when the code is already pre-built and doesn't need Babel processing.

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My',
  babel: false,
};

WARNING

Without Babel, the code won't be transpiled for target browsers. Only use this option if you're sure the input code is already compatible with the required browsers.

Class Transpilation

The transformClasses option enables transpiling ES classes into functions via Babel. This is required for compatibility with legacy code that extends classes using BX.merge, Object.assign, or other patterns incompatible with native classes.

Transpile all classes:

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My',
  transformClasses: true,
};

Transpile only specific classes:

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My',
  transformClasses: ['EventEmitter', 'BaseEvent'],
};

When an array of names is provided, all other classes remain native. This allows transpiling only the classes that actually need backward compatibility, preserving bundle size and performance.

Type Declarations

When building TypeScript extensions, Chef automatically generates a .d.ts file with ambient namespace declarations alongside the bundle. This enables IDE hints when accessing exports via namespace, e.g. new BX.UI.Bbcode.App().

dist/
├── app.bundle.js      # Compiled bundle (comments stripped)
└── app.bundle.d.ts    # Type declarations with JSDoc

JSDoc comments from source files are preserved in .d.ts for IDE hints, but stripped from the runtime bundle to reduce size.

Declaration generation only works for TypeScript extensions with a namespace other than window.

To disable:

ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My',
  emitDeclaration: false,
};

Safe Namespaces

By default, if an extension depends on another extension that hasn't been loaded on the page, including the bundle will cause a fatal error — JavaScript cannot access a non-existent namespace (e.g., BX.Main.Core).

The safeNamespaces option enables optional chaining for dependency namespace references in the IIFE wrapper. If a dependency is missing, the extension receives an empty object instead of a fatal error. This allows you to check for the dependency in code and work with it conditionally:

js
if (BX.Main?.Core)
{
  // Dependency is loaded, safe to use
}
ts
export default {
  input: './src/index.ts',
  output: './dist/my.bundle.js',
  namespace: 'BX.My.Extension',
  safeNamespaces: true,
};

When enabled, dependency references in the IIFE wrapper use ?. and ??:

js
// Before:
})(this.BX.My.Extension = this.BX.My.Extension || {}, BX.Main.Core, BX.UI.Buttons);

// After:
})(this.BX.My.Extension = this.BX.My.Extension || {}, BX?.Main?.Core??{}, BX?.UI?.Buttons??{});

The extension's own namespace is not transformed — it is already safely initialized at the top of the bundle.

Environment Variables

Chef automatically replaces environment variables during build:

VariableProductionDevelopment
process.env.NODE_ENV"production""development"
import.meta.env.MODE"production""development"
import.meta.env.PRODtruefalse
import.meta.env.DEVfalsetrue

Replacement happens statically at build time. This enables tree-shaking to remove dev-only code from npm packages:

ts
if (process.env.NODE_ENV !== 'production') {
  console.warn('Debug info');
}

In chef build mode (no flags), variables are set to development. In chef build --production mode — to production.

Released under the MIT License.