Baseline: main 23.0+ · Verified: main 26.800.0, ui 26.687.0
ID → directory
Every dot of the ID is a directory level (Extension::getPath()): vendor.module.editor → js/vendor/module/editor/, searched in /local/js/ first, then /bitrix/js/. At least 2 segments, each [a-z0-9_-]. A dotted directory (/local/js/vendor.module/editor/) cannot be loaded by ID.
| Code | Where |
|---|---|
| Project | /local/js/vendor/module/editor/ → ID vendor.module.editor |
Distributed module vendor.module |
install/js/vendor/module/editor/, copied to /local/js/ on install |
| Kernel | /bitrix/js/<module>/<ext>/ (ui.buttons) — never edit; a same-path copy in /local/js/ shadows it and stops getting updates |
public function InstallFiles(): void
{
CopyDirFiles(__DIR__ . '/js', \Bitrix\Main\Application::getDocumentRoot() . '/local/js', true, true); // 4th arg = recursive
}
public function UnInstallFiles(): void { DeleteDirFilesEx('/local/js/vendor/module'); // DeleteDirFiles() is flat, leaves nested dirs }
## Layout
/local/js/vendor/module/editor/
├── src/index.js # ES6+ entry
├── dist/ # built bundles — commit and deploy
├── lang/ru/config.php # optional $MESS phrases
├── bundle.config.js # deploy too: server reads namespace from it
└── config.php
Scaffold: `bitrix create`; build: `bitrix build` in the extension dir (`@bitrix/cli`).
```javascript
module.exports = {
input: './src/index.js',
output: './dist/editor.bundle.js',
namespace: 'BX.Vendor.Module.Editor',
browserslist: true,
};
- CSS:
import './style.css';in the entry →dist/editor.bundle.css. - Imports:
import { Loc, Runtime } from 'main.core';, legacy CoreJS 1.0import 'main.date';. - The build rewrites
config.php(js,css,relfrom imports) unlessadjustConfigPhp: false.
config.php
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) { die(); }
return [
'js' => './dist/editor.bundle.js',
'css' => './dist/editor.bundle.css',
'rel' => ['main.core', 'ui.buttons'],
'skip_core' => false,
'settings' => ['maxItems' => 50],
];
Other keys: lang_additional (computed phrases), oninit (callable, gets and returns the config), controllerEntrypoint (JS function for renderExtension(), see bitrix-controllers).
Settings in JS: Extension.getSettings('vendor.module.editor').get('maxItems', 20) (main.core).
Phrases
If lang/ exists, the kernel loads lang/<LANGUAGE_ID>/config.php with the extension (no lang key needed) and exposes it to JS: Loc.getMessage('VENDOR_EDITOR_TITLE'). Phrases share one global BX.message — prefix codes with vendor + extension.
<?php
$MESS['VENDOR_EDITOR_TITLE'] = 'Editor';
Loading
\Bitrix\Main\UI\Extension::load(['vendor.module.editor', 'ui.buttons']); // unknown ID = silent no-op
import { Runtime } from 'main.core';
Runtime.loadExtension('vendor.module.editor').then(({ Editor }) => {
new Editor().render();
});
Runtime.loadExtension(...ids) resolves with the merged exports of the requested extensions (from their namespace), after JS/CSS/phrases/settings are on the page.
Checklist
- Directory path equals the ID with dots →
/; module extensions prefixedvendor.module.. - Load only by ID (
Extension::load(),rel,Runtime.loadExtension()); no<script src>/Asset::addJs()to any/local/js/…or/bitrix/js/…file — updates move files (ui 26.675 movedui.actionpanelintodist/). -
dist/rebuilt aftersrc/changes;bundle.config.jsdeployed with it. - Every dependency in
rel. - UI strings in
lang/<lang>/config.php, read viaLoc.getMessage().