Setting up a TypeScript build for a 1C-Bitrix project

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    745
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

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