Features
Chef is a CLI tool for building, testing and maintaining Bitrix frontend extensions. It finds bundle.config.ts in the project structure and runs a Rollup build for each discovered package.
Build
Rollup + Babel + PostCSS under the hood. TypeScript and Vue 3 SFC out of the box. Parallel builds for up to 4 extensions at once. Watch mode with hot reload.
chef build ui.buttons # Build an extension
chef build ui.* -w # Build a group + watch
chef build ui.buttons --production # Production build with minificationMore about the build pipeline — in How the Build Works.
TypeScript
Native TypeScript support — .ts file compilation is built into the build pipeline. Create an extension with chef create — and get a typed config, path aliases for all project extensions right away.
import { Loc, Tag } from 'main.core';
import { Button } from 'ui.buttons';See TypeScript for details.
Vue 3
Write Vue 3 components with TypeScript — Chef compiles templates, styles and scripts. import 'vue' automatically maps to ui.vue3. Single File Components (.vue) are supported out of the box.
import { BitrixVue } from 'ui.vue3';
import Counter from './components/Counter.vue';
BitrixVue.component('ui-counter', Counter);See Vue 3 for details.
Testing
Unit tests with Mocha + Chai run in a real browser via Playwright. E2E tests with automatic authentication.
chef test ui.buttons # All tests
chef test unit ui.buttons # Unit only
chef test ui.buttons --debug # With DevToolsSee Testing for details.
Production Build
Minification via Terser, automatic environment variable replacement (process.env.NODE_ENV, import.meta.env), source maps disabled. Standalone mode for building without external dependencies.
chef build ui.buttons --productionSee Production Build for details.
Analytics
Bundle sizes, dependency tree and duplicates — chef stat shows everything for one extension or a whole group.
chef stat ui.buttons
chef stat ui.*Scaffold
chef create creates an extension with the right structure, config, entry point and test templates.
chef create ui.buttonsJS Extensions
The recommended way to organize frontend code in Bitrix is JS extensions. These are standalone modules with an entry point, build configuration and a PHP manifest.
A JS extension is loaded on a page via \Bitrix\Main\UI\Extension::load('vendor.name'), supports a dependency system and can be imported in other extensions.
local/js/ui/buttons/
├── bundle.config.ts # Build configuration
├── config.php # PHP manifest (dependencies, assets)
├── src/
│ └── buttons.ts # Entry point
├── dist/
│ ├── buttons.bundle.js # Compiled JS
│ └── buttons.bundle.css # Compiled styles
└── test/
├── unit/ # Unit tests (Mocha + Chai)
│ └── buttons.test.ts
└── e2e/ # E2E tests (Playwright)
└── buttons.spec.tsThe extension name is derived from its path: local/js/ui/buttons/ → ui.buttons.
See JS Extension for details.
Other Entities
Besides JS extensions, Chef can build frontend code in other Bitrix entities: components, templates and activities. However, this approach is considered deprecated — such entities do not support the dependency system.
Building deprecated entities
Components, templates and activities do not support the dependency system — they cannot be imported in code or listed as dependencies in config.php. All frontend code should be moved to JS extensions, leaving only initialization in other entities:
\Bitrix\Main\UI\Extension::load('vendor.news-list');Project Structure
A standard Bitrix installation with bitrix/ and local/ directories. Root-level configs are created by chef init commands:
project/
├── .browserslistrc # Target browsers (chef init build)
├── tsconfig.json # TypeScript config (chef init build)
├── aliases.tsconfig.json # Extension path aliases (chef init build)
├── playwright.config.ts # Playwright config (chef init tests)
├── .env.test # Test credentials (chef init tests)
│
├── bitrix/ # System directory (read-only)
│ └── js/
│ └── main/
│ └── core/ # System extension: main.core
│
└── local/ # User directory (build happens here)
├── js/
│ └── vendor/
│ └── my-extension/ # JS extension: vendor.my-extension
└── modules/
└── vendor.module/
└── install/
└── js/
└── vendor/
└── feature/ # Extension inside a modulebitrix/ directory — read only
bitrix/ contains the platform core and is overwritten on updates. Chef uses bitrix/js/ only for reading — to resolve dependencies and determine namespaces of core extensions. Builds only run in local/.
Overriding system extensions
If you need to modify a system extension — copy it to local/js/ and modify it there. When loading an extension, Bitrix first looks in local/js/, then in bitrix/js/, so the local copy automatically replaces the system one.
How the Build Works
Specifying Packages
Most Chef commands (build, test, stat) accept extension lists in the same way.
One or more extensions by name:
chef build ui.buttons
chef build ui.buttons main.core ui.iconsGlob pattern — to work with a group of extensions at once:
chef build ui.* # All extensions with ui. prefix
chef build ui.bbcode.* # All extensions inside ui.bbcodeTIP
In zsh, escape glob patterns to prevent the shell from expanding them:
chef build ui.\*Directory scan — without arguments, Chef scans the current directory:
cd local/js/ui
chef build # All extensions inside ui/
chef build -p local/js/ui # Or specify a directory explicitlyBuild Pipeline
When running chef build, for each package:
- Read configuration — parse
bundle.config.ts - Build the bundle via Rollup:
- TypeScript — compile
.tsfiles - Babel — transpile to target browsers
- PostCSS — autoprefixes, SVG optimization, inline images
- Terser — minification (if enabled)
- TypeScript — compile
- Update
config.php— analyze imports and write dependencies torel - Source maps — generate source maps (if enabled)
Up to 4 packages are built in parallel.
Namespaces
Each extension declares a global namespace in bundle.config.ts:
export default {
input: './src/buttons.ts',
output: './dist/buttons.bundle.js',
namespace: 'BX.UI.Buttons',
};After building, everything exported from the entry point becomes accessible via this namespace:
// src/buttons.ts
export class Button { /* ... */ }
export class ButtonGroup { /* ... */ }In the browser after build:
const button = new BX.UI.Buttons.Button();
const group = new BX.UI.Buttons.ButtonGroup();If namespace is not set, window is used by default — exports become global variables.
Dependencies
Standard ES imports are used in source code:
import { Loc, Tag } from 'main.core';
import { Button } from 'ui.buttons';During build, Chef analyzes imports, replaces them with global namespace references and updates config.php:
return [
'js' => ['./dist/my.extension.bundle.js'],
'css' => ['./dist/my.extension.bundle.css'],
'rel' => [
'main.core',
'ui.buttons',
],
];If the extension does not depend on main.core, Chef automatically adds 'skip_core' => true to avoid loading the core unnecessarily.
Protected Extensions
An extension can be marked as protected in bundle.config.ts:
export default {
input: './src/index.ts',
output: './dist/index.bundle.js',
protected: true,
};Protected extensions are skipped during scanning (chef build without arguments or with a glob pattern), but are built when explicitly named.