Skip to content

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. Watch mode with hot reload.

bash
chef build ui.buttons              # Build an extension
chef build ui.* -w                 # Build a group + watch
chef build ui.buttons --production # Production build with minification

More 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.

ts
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.

ts
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.

bash
chef test ui.buttons               # All tests
chef test unit ui.buttons           # Unit only
chef test ui.buttons --debug       # With DevTools

See 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.

bash
chef build ui.buttons --production

See Production Build for details.

Diagnostics ​

Bundle sizes, dependency trees, circular dependencies, unused extensions — chef diag analyzes extensions across the entire project.

bash
chef diag top-used                             # Most depended-on extensions
chef diag top-bundle-size                      # Largest bundles
chef diag top-used --exclude 'main.*,im.*'     # Exclude main and im from results
chef diag circular-deps                        # Circular dependencies
chef diag unused                               # Unused extensions
chef diag find-usages main.core                # Find usages

Scaffold ​

chef create creates an extension with the right structure, config, entry point and test templates.

bash
chef create ui.buttons

JS 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
└── tests/
    ├── unit/              # Unit tests (Mocha + Chai)
    │   └── buttons.test.ts
    └── e2e/               # E2E tests (Playwright)
        └── buttons.spec.ts

The 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:

php
\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 module

bitrix/ 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, lint) accept extension lists in the same way.

One or more extensions by name:

bash
chef build ui.buttons
chef build ui.buttons main.core ui.icons

Glob pattern — to work with a group of extensions at once:

bash
chef build ui.*                    # All extensions with ui. prefix
chef build ui.bbcode.*             # All extensions inside ui.bbcode

TIP

In zsh, escape glob patterns to prevent the shell from expanding them:

bash
chef build ui.\*

Directory scan — without arguments, Chef scans the current directory:

bash
cd local/js/ui
chef build                         # All extensions inside ui/
chef build -p local/js/ui          # Or specify a directory explicitly

Build Pipeline ​

When running chef build, for each package:

  1. Read configuration — parse bundle.config.ts
  2. Build the bundle via Rollup:
    • TypeScript — compile .ts files
    • Babel — transpile to target browsers
    • PostCSS — autoprefixes, SVG optimization, inline images
    • Terser — minification (if enabled)
  3. Update config.php — analyze imports and write dependencies to rel
  4. Source maps — generate source maps (if enabled)

Packages are built sequentially.

Namespaces ​

Each extension declares a global namespace in bundle.config.ts:

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:

ts
// src/buttons.ts
export class Button { /* ... */ }
export class ButtonGroup { /* ... */ }

In the browser after build:

ts
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:

ts
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:

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:

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.

Released under the MIT License.