Skip to content

Feature: dev-server hot file at var/vite for automatic dev-mode detection #39

Description

@BreathCodeFlow

Summary

Add an optional dev-server “hot file” mechanism to vite-plugin-typo3 — mirroring Laravel’s laravel/vite-plugin — so the PHP side can reliably detect whether the Vite dev server is currently running, without relying solely on TYPO3_CONTEXT or environment variables.

Motivation

Today, vite-asset-collector decides whether to load assets from the dev server based on:

  • useDevServer extension setting (true / false / 'auto')
  • Environment::getContext()->isDevelopment() when set to 'auto'
  • VITE_SERVER_URI / VITE_PRIMARY_PORT env vars to know where the server lives

This works, but it has gaps:

  1. 'auto' is coarse.
    In Development context, asset rendering switches to the dev server even when npm run dev is not actually running, which produces broken pages / 502s via the DDEV Vite sidecar.

  2. No live signal of dev-server state.
    Stopping or restarting Vite is invisible to PHP until something fails.

  3. Each project rolls its own hot-file plugin.
    We currently have one in our template, but this leads to subtle bugs around exit-handler binding, parent-directory creation, and HMR-restart cleanup.

A standardized hot file — written when Vite starts listening and removed when it stops — would let vite-asset-collector decide via a cheap file_exists() check, identical in spirit to Laravel’s public/hot pattern.

Proposal

Add a plugin, or extend the existing one, that during vite dev:

  • Writes a marker file at <projectRoot>/var/vite on httpServer listening.
  • Creates the parent directory (var/) if missing.
  • Removes the file on:
    • httpServer close
    • process exit
    • SIGINT
    • SIGTERM
    • SIGHUP
  • Binds the process-level handlers exactly once across multiple configureServer invocations, using a module-scope guard similar to Laravel’s exitHandlersBound.

Configuration

hotFile?: string;
hotFileContent?: 'marker' | 'url';

hotFile

Override the hot-file location.

Default:

var/vite

The default should be relative to process.cwd(), matching Environment::getVarPath() on the PHP side.

hotFileContent

Controls what gets written to the file.

Options:

  • 'marker' — write a static marker such as VITE_SERVER_RUNNING
  • 'url' — write the dev-server URL, similar to Laravel

For TYPO3, a static marker is sufficient if vite-asset-collector consumes it only via file_exists(). However, 'url' would enable richer PHP-side use later, for example surfacing the live URL in the backend.

Reference implementation

We currently maintain this locally and would be happy to upstream it as a PR if the API direction is acceptable:

import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import type { Plugin } from 'vite';

const hotFile = resolve(process.cwd(), 'var/vite');
const cleanup = (): void => rmSync(hotFile, { force: true });
let exitHandlersBound = false;

export const hotFilePlugin: Plugin = {
  name: 'typo3:hot-file',
  apply: 'serve',

  configureServer(server) {
    server.httpServer?.once('listening', () => {
      mkdirSync(dirname(hotFile), { recursive: true });
      writeFileSync(hotFile, 'VITE_SERVER_RUNNING\n');
    });

    server.httpServer?.once('close', cleanup);

    if (exitHandlersBound) {
      return;
    }

    exitHandlersBound = true;

    process.on('exit', cleanup);

    for (const signal of ['SIGINT', 'SIGTERM', 'SIGHUP'] as const) {
      process.on(signal, () => process.exit());
    }
  },
};

Differences vs. Laravel’s implementation

  • Adds httpServer.once('close', …).
    Laravel only cleans up via process exit, so a Vite :restart can leave a stale file behind.

  • Uses rmSync(..., { force: true }) instead of existsSync + rmSync.
    This is atomic and avoids a TOCTOU window.

PHP side

This pairs with a small change in vite-asset-collector, so that the 'auto' branch can short-circuit when the marker is absent:

$useDevServer = file_exists(Environment::getVarPath() . '/vite');

Happy to open a parallel issue / PR on [s2b/vite-asset-collector](https://github.com/s2b/vite-asset-collector) if this direction is agreeable.

Out of scope

  • Writing the live dev-server URL into the hot file, matching Laravel’s full contract.
    This can be added later behind hotFileContent: 'url' once a PHP consumer needs it.

  • Replacing the env-var-based discovery via VITE_SERVER_URI.
    The hot file is an additional signal, not a replacement.

Open questions

  1. Should the plugin be opt-in, for example typo3({ hotFile: true }), or enabled by default for vite dev?

  2. What should the default location be?

    • var/vite, matching Environment::getVarPath()
    • var/vite/hot, to keep var/ cleaner
  3. Should this work in target: 'extension' mode, or only in target: 'project'?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions