---
title: Asset bundling
description: "Web applications usually need to serve assets to users. Tempest provide a seamless integration with Vite, the most popular front-end development server and build tool"
keywords: ["vite", "frontend", "js", "css", "ts", "typescript", "javascript", "sri", "manifest", "assets"]
---

## Overview

[Vite](https://vite.dev) is the de-facto standard build tool for front-end development. It provides a very fast development server and bundles your assets for production without barely any configuration needed.

Tempest provides an integration with Vite that consists of a [Vite plugin](https://github.com/tempestphp/tempest-framework/tree/main/packages/vite-plugin-tempest) and a [server-side package](https://github.com/tempestphp/tempest-framework/tree/main/src/Tempest/Vite).

## Quick start

To install Vite, you may run the corresponding installer command. The wizard will guide you through the installation, including adding the Vite plugin, the `vite.config.ts` configuration file, the TypeScript entrypoint, and, if you chose so, Tailwind CSS.

```sh
php tempest install vite
```

The next step is to add the [`{html}<x-vite-tags />`](../1-essentials/02-views.md#x-vite-tags) component to your base template. This is how the script and style tags including your entrypoints are provided to the browser.

```html x-base.view.php
<html lang="en">
	<head>
		<!-- ... -->
		<x-vite-tags />
	</head>
	<body>
		<x-slot />
	</body>
</html>
```

## Running the development server

During development, the purpose of Vite is to transpile asset files on-the-fly to a format that the browser understands. This is the concept that makes Vite really fast—it doesn't need to bundle the whole application everytime some code is updated.

For Vite to be able to transpile assets, its server needs to be started. This is done by running its command-line interface, `vite`.

```sh
npm run dev
```

The command above looks for the `dev` script in `package.json`, which in turns runs the `vite` CLI. This is the equivalent of running the `{sh}npx vite` command.

## Entrypoints

An entrypoint is a primary script or stylesheet that serves as the starting point in an application. Any asset file ending with `.entrypoint.{ts,css,js}` will automatically be discovered by Tempest, meaning you don't have to configure anything.

```js app/main.entrypoint.ts
console.log('Hello, world! 🌊')
```

### Manually including an entrypoint

It might happen that you only need a specific script or stylesheet in a particular view. In this situation, you may use the [`{html}<x-vite-tags />`](./03-views#x-vite-tags) component with an `entrypoint` attribute that points to the file you want to include:

```html app/Profile/show.view.php
<x-base>
	<slot name="head">
		<x-vite-tags entrypoint="src/Profile/profile.css" />
	</slot>
	<!-- ... -->
</x-base>
```

:::warning
For Vite to bundle this file in production, it still needs to be [configured as an entrypoint](#manually-configuring-entrypoints). Otherwise, it will not be included in the production manifest, and Tempest won't be able to generate a link to it.
:::

### Manually configuring entrypoints

If you prefer, you may opt-out of the `*.entrypoint.{ts,cs,js}` naming convention and manually configure entrypoints in the Vite configuration.

To do so, create a `vite.config.php` file that returns a {`Tempest\Vite\ViteConfig`} instance. You should configure the `entrypoints` parameter:

```php app/vite.config.php
return new ViteConfig(
    entrypoints: [
        'app/main.css',
        'app/main.ts',
    ],
);
```

Note that the paths to the entrypoint files must be relative to the root of the project.

If you opted in for manual entrypoint configuration, your base template should also [specify which entrypoint to include by default](#manually-including-an-entrypoint). Otherwise, all configured entrypoints will be used.

## Building for production

Running the `build` script from the `package.json` will bundle your application's assets, versioning them and creating a `manifest.json` file.

```sh
npm run build
```

By default, assets are compiled in the `public/build` directory. This directory should be added to `.gitignore`, to avoid adding compiled assets to version control.

:::info
This directory is already in your `.gitignore` if you used the `{sh}php tempest install vite` command.
:::

## Using a `nonce` attribute

If your application uses a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP), you may need to include `nonce` attributes to the tags generated by `{html}<x-vite-tags />`.

The value of a `nonce` attribute should be used only once per request, as it is mainly used to prevent [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). To generate and configure that value for each request, you may use a [route middleware](../1-essentials/02-views.md#route-middleware):

```php
use Tempest\Support\Random;
use Tempest\Vite\ViteConfig;

final class ConfigureViteNonce implements HttpMiddleware
{
    public function __construct(
        private readonly ViteConfig $viteConfig,
    ) {}

    public function __invoke(Request $request, HttpMiddlewareCallable $next): Response
    {
        $this->viteConfig->nonce = Random\secure_string(length: 40);

        return $next($request);
    }
}
```

Note that middleware are not automatically registered, as their order generally matters. You may manually include this middleware to routes that need it, or apply it automatically by registering it globally:

```php
use Tempest\Core\KernelEvent;
use Tempest\EventBus\EventHandler;
use Tempest\Router\HttpMiddleware;
use Tempest\Router\HttpMiddlewareCallable;
use Tempest\Http\Request;
use Tempest\Http\Response;
use Tempest\Router\Router;
use Tempest\Support\Random;
use Tempest\Vite\ViteConfig;

final class ConfigureViteNonce implements HttpMiddleware
{
    public function __construct(
        private readonly Router $router,
        private readonly ViteConfig $viteConfig,
    ) {}

    public function __invoke(Request $request, HttpMiddlewareCallable $next): Response
    {
        $this->viteConfig->nonce = Random\secure_string(length: 40);

        return $next($request);
    }

    #[EventHandler(KernelEvent::BOOTED)]
    public function register(): void
    {
        $this->router->addMiddleware(self::class);
    }
}
```

The `register` method above is an [event handler](../2-features/08-events.md) that is called when Tempest boots. It registers the middleware on the injected {`Tempest\Router\Router`} instance, effectively registering it for every route.

Alternatively, you may also set the `nonce` directly in the event handler. However, keep in mind that this would be called every time the framework boots, even when only using console commands.

## Subresource integrity

Tempest will detect [subresource integrity](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) hashes in your `manifest.json` file and will automatically add them to the generated script and style tags.

Integrity hashes are not included in Vite manifests by default, but the `vite-plugin-manifest-sri` plugin provides this functionality. You may install it through `bun` or `npm` and register it like any other Vite plugin in your configuration file:

```js vite.config.ts
import tailwindcss from '@tailwindcss/vite'
import { defineConfig } from 'vite'
import sri from 'vite-plugin-manifest-sri'
import tempest from 'vite-plugin-tempest'

export default defineConfig({
	plugins: [
		tailwindcss(),
		tempest(),
		sri(),
	],
})
```

## Testing

By default, Tempest is intructed to not generate any tag during tests. This behavior is in place to prevent triggering `ManifestNotFoundException` exceptions in your test suite.

If, for any reason, you wish to restore tag resolution in a test, you may call the `{php}allowTagResolution()` method on the `ViteTester` instance:

```php tests/SomeTest.php
public function setUp(): void
{
    parent::setUp();

    $this->vite->allowTagResolution();
}
```
