Skip to content

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".

Assets

The Chill assets (js, css, images, …) are managed by Vite in this repository.

Installation

Vite needs to run in a Node.js environment. This Node.js environment can be set up using a node docker image. The bash script docker-node.sh sets up a Node.js environment with an adequate configuration. Launch this container by typing:

$ bash docker-node.sh

In this NodeJS environment, install all the assets required by Chill with:

node@b91cab4f7cfc:/app$ npm install

This command will install all the packages that are listed in package.json.

Any further required dependencies can be installed using npm. For example:

node@b91cab4f7cfc:/app$ npm install --save-dev

Usage

Organize your assets

Chill assets usually live under the /Resources/public folder of each Chill bundle. Entry points are listed explicitly in ../../../vite/vite.entries.ts at the project root.

To add a new asset entry point:

  1. Create your entrypoint file (e.g. src/Bundle/ChillFooBundle/Resources/public/vuejs/MyApp/index.ts)
  2. Add it to vite/vite.entries.ts:
my_entry_name: r("src/Bundle/ChillFooBundle/Resources/public/vuejs/MyApp/index.ts"),

To gather css files, import them from your js/ts entry file:

// assets/js/main.js
import '../css/app.css';

For finer configuration, refer to the Vite documentation.

Building the assets of an application

This repository is only used for development. To deploy Chill, you create a Symfony application and install chill-project/chill-bundles with composer. The assets of such an application come from two separate builds, run as two separate processes:

Build Sources Output Public path
Chill vendor/chill-project/chill-bundles public/build/ /build/
Application the application's own entries, if any public/build-app/ /build-app/

Both builds run with Node.js only. Their JavaScript dependencies are installed with npm into node_modules: the build must never import a JavaScript file from a composer package other than chill-project/chill-bundles itself.

Chill's build

The Vite configuration lives in vite/preset.ts and is exported as chillVitePreset(); the vite.config.ts at the root of chill-bundles is a three-line consumer of it. The preset reads its paths from environment variables, resolved against the directory the build is launched from:

Variable Default Meaning
CHILL_VITE_OUT_DIR public/build inside chill-bundles where the build lands
CHILL_VITE_BASE /build/ public URL of the built files
CHILL_TRANSLATIONS_DIR var/translations inside chill-bundles translations dumped by Symfony UX Translator

The translations are dumped by the application when its cache is warmed up, so warm the cache before building:

# at the root of the application
symfony console cache:warmup

cd vendor/chill-project/chill-bundles
npm ci
CHILL_VITE_OUT_DIR=../../../public/build \
CHILL_TRANSLATIONS_DIR=../../../var/translations \
  npm run build

The application's build

Most applications have no entry of their own and skip this step. Otherwise, the application has its own vite.config.ts, which builds only its entries into public/build-app/. The vite-plugin-symfony plugin writes the entrypoints.json file that Symfony reads to render the tags:

// vite.config.ts, in your application
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import symfony from "vite-plugin-symfony";

export default defineConfig({
  base: "/build-app/",
  plugins: [vue(), symfony()],
  build: {
    outDir: "public/build-app",
    manifest: true,
    emptyOutDir: true,
    rolldownOptions: {
      input: {
        my_entry_name: "assets/my_entry/index.ts",
      },
    },
  },
});

Symfony configuration

The tags are rendered by pentatrion/vite-bundle, installed as a dependency of chill-bundles. Register it in config/bundles.php:

Pentatrion\ViteBundle\PentatrionViteBundle::class => ['all' => true],

and declare one configuration per build in config/packages/pentatrion_vite.yaml:

pentatrion_vite:
    default_config: chill
    configs:
        chill:
            build_directory: build
        app:
            build_directory: build-app

when@prod:
    pentatrion_vite:
        cache: true

With cache: true, the entrypoints.json files are read once, when the cache is warmed up, instead of on every request.

Chill's layout loads the entry app of the application's build, if the application defines one. Any other entry of the application's build is loaded by passing the name of its configuration as third argument:

{{ vite_entry_script_tags('my_entry_name', {}, 'app') }}

Loading an entry of each build in the same page loads their shared dependencies twice (two copies of Vue, for instance).

Compile the assets

To compile assets for production:

node@b91cab4f7cfc:/app$ npm run build

While developing, you can continuously watch files and recompile on every change (requires a manual browser refresh):

node@b91cab4f7cfc:/app$ npm run watch

Use the assets in the templates

Any entry of ../../../vite/vite.entries.ts can be linked from Twig using the helpers of pentatrion/vite-bundle:

<head>
    ...
    {{ vite_entry_link_tags('my_entry_name') }}
</head>
<body>
    ...
    {{ vite_entry_script_tags('my_entry_name') }}
</body>

vite_entry_link_tags also renders the modulepreload links of the chunks the entry imports.

Hot Module Replacement (HMR) in development

HMR allows the browser to update Vue components, SCSS, and TypeScript files instantly without a full page reload, preserving application state.

The dev server is only used when developing chill-bundles itself, with a single build. Applications only run the production builds described above.

How it works

Chill uses a Symfony backend to render HTML pages. Vite normally assumes it controls the full page, so a small adaptation is required:

  • When the dev server starts, vite-plugin-symfony writes public/build/.vite/entrypoints.json with the address of the dev server instead of the built files.
  • pentatrion/vite-bundle reads it and renders <script> tags pointing to http://localhost:5173/build/src/Bundle/..., so Vite serves the source files directly.
  • The @vite/client script is added to the page to open a WebSocket back to the Vite server for hot updates.
  • vite-plugin-symfony sets server.origin, so that fonts and assets referenced in CSS also resolve via Vite (not Symfony).

Setup

  1. Start both servers in two separate terminals:

    # Terminal 1 – Vite dev server
    npm run dev-server
    
    # Terminal 2 – Symfony (must be HTTP, not HTTPS)
    symfony server:start --no-tls
    
  2. Open http://localhost:8000 (HTTP only — HTTPS would block cross-origin scripts).

  3. Edit any .vue, .ts, or .scss file. The browser updates instantly.

Note: Stopping the dev server does not restore the built files: run npm run build again, so that entrypoints.json points to them.

Linux: inotify limit

On Linux, npm run dev-server may fail with:

Error: ENOSPC: System limit for number of file watchers reached

Fix (persistent):

sudo tee /etc/sysctl.d/99-inotify.conf >/dev/null <<'EOF'
fs.inotify.max_user_watches=524288
fs.inotify.max_user_instances=1024
fs.inotify.max_queued_events=32768
EOF
sudo sysctl --system

Fallback without sudo:

CHOKIDAR_USEPOLLING=1 CHOKIDAR_INTERVAL=250 npm run dev-server