TypeScript Build Configuration for a 1C-Bitrix Project
Most TypeScript tutorials assume a SPA with a single entrypoint. Bitrix is different: PHP generates pages, each component loads its own JS files, and the site template contains global code. A standard tsc --watch does not cover this structure — the bundler must be configured to match the platform's architecture.
TypeScript Build Configuration for a 1C-Bitrix Project
Vite as the Primary Bundler
Vite is the optimal choice for Bitrix projects: fast HMR during development, Rollup under the hood for production builds, and native TypeScript support with no additional configuration.
// package.json (in /local/templates/my_site/ or in /local/)
{
"name": "bitrix-frontend",
"private": true,
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"watch": "vite build --watch",
"check": "tsc --noEmit"
},
"devDependencies": {
"typescript": "^5.4.0",
"vite": "^5.2.0"
}
}
tsc --noEmit && vite build — TypeScript checks types, Vite builds. If there are type errors, the build will not start.
Multiple Entrypoints for Bitrix
Instead of a single bundle — separate files for different sections of the site. Each PHP template loads only what it needs:
// vite.config.ts
import { defineConfig } from 'vite';
import { resolve } from 'path';
export default defineConfig({
resolve: {
alias: { '@': resolve(__dirname, 'src') },
},
build: {
outDir: 'dist',
emptyOutDir: true,
manifest: true, // generates manifest.json for PHP
rollupOptions: {
input: {
// Global code for all pages
app: resolve(__dirname, 'src/app.ts'),
// Catalog and filter
catalog: resolve(__dirname, 'src/pages/catalog.ts'),
// Product page
product: resolve(__dirname, 'src/pages/product.ts'),
// Cart and checkout
cart: resolve(__dirname, 'src/pages/cart.ts'),
// User account
account: resolve(__dirname, 'src/pages/account.ts'),
},
output: {
entryFileNames: '[name].[hash].js',
chunkFileNames: 'chunks/[name].[hash].js',
assetFileNames: 'assets/[name].[hash][extname]',
},
},
},
});
Using manifest.json in the PHP Template
manifest: true in Vite generates the .vite/manifest.json file with a mapping of names to hashed filenames. PHP reads it and loads the required files with cache-busting versioning:
// /local/templates/my_site/include/vite_assets.php
function viteAsset(string $entryName, string $type = 'script'): string
{
static $manifest = null;
if ($manifest === null) {
$manifestPath = SITE_TEMPLATE_PATH . '/dist/.vite/manifest.json';
if (file_exists($_SERVER['DOCUMENT_ROOT'] . $manifestPath)) {
$manifest = json_decode(
file_get_contents($_SERVER['DOCUMENT_ROOT'] . $manifestPath),
true
);
}
}
if (!$manifest) return '';
$key = 'src/pages/' . $entryName . '.ts';
$file = $manifest[$key]['file'] ?? '';
if (!$file) return '';
$url = SITE_TEMPLATE_PATH . '/dist/' . $file;
if ($type === 'script') {
return '<script type="module" src="' . $url . '"></script>';
}
$css = $manifest[$key]['css'] ?? [];
return implode("\n", array_map(
fn($c) => '<link rel="stylesheet" href="' . SITE_TEMPLATE_PATH . '/dist/' . $c . '">',
$css
));
}
In the catalog component template:
// Load catalog JS with versioned hash
<?= viteAsset('catalog') ?>
<?= viteAsset('catalog', 'css') ?>
tsconfig.json: Strict Configuration
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"baseUrl": ".",
"paths": { "@/*": ["src/*"] },
"types": ["vite/client"],
"skipLibCheck": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
exactOptionalPropertyTypes catches cases where undefined is explicitly assigned to an optional property — a common issue when working with data from Bitrix.
HMR During Development
For HMR to work, the Vite dev server and Apache/nginx (Bitrix) must not conflict. The setup: Vite dev server on port 5173, Bitrix on 80/443. In dev mode, PHP loads scripts from the Vite dev server:
// In the template
$isDev = file_exists(SITE_TEMPLATE_PATH . '/.vite-dev'); // dev mode marker
if ($isDev):
?>
<script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173/src/pages/catalog.ts"></script>
<?php else: ?>
<?= viteAsset('catalog') ?>
<?php endif; ?>
The .vite-dev file is created when vite dev starts and removed when it stops. It is not committed to the repository.
Timelines
| Task | Timeline |
|---|---|
| Basic Vite + TypeScript setup for the site template | 4–8 hours |
| Multiple entrypoints + manifest.json for PHP | 4–8 hours |
| CI/CD integration (build in pipeline) | 4 hours |







