Skip to content

HMR always causes a full page reload in vite. #443

Description

@arnoudb

environment:

  • oxc-angular-compiler 0.0.35
  • vite 8.2.1
  • angular 21.2.21

When i have autoReload: true configured and i change a .html template HMR kicks in and works and updates the app. But right after vite completely reloads the page anyway. In the project https://github.com/analogjs/analog there is a wrapper around the angular compiler that can be used in vite. In that project the autoReload: true does work. Might be inspiration for a fix.

I created a small plugin that i run after the compiler and then it works. This is of course a temporary 'fix' and it would be great if it can be fixed in the oxc-angular-compiler. I include it here as it does clearly shows the issue.

/**
 * Injects client-side code that prevents Vite from doing a full page reload
 * on Angular component template (.html) changes
 */
export function oxcAngularHmrFix(): Plugin {
  let mainEntry: string | undefined;

  return {
    name: 'oxc-angular-hmr-fix',
    apply: 'serve', // only in dev
    enforce: 'pre',

    configResolved(config: ResolvedConfig) {
      // Try to find the main entry (common patterns)
      const input = config.build?.rolldownOptions?.input;
      if (typeof input === 'string') {
        mainEntry = normalizePath(input);
      } else if (input && typeof input === 'object') {
        // take the first entry
        const first = Object.values(input)[0];
        if (typeof first === 'string') mainEntry = normalizePath(first);
      }
    },

    transform(code, id) {
      const normalizedId = normalizePath(id);

      // Only inject into the main entry (or any file that looks like main.ts)
      const isMain =
        (mainEntry && normalizedId.endsWith(mainEntry)) ||
        /\/main\.(ts|tsx|js|jsx)$/.test(normalizedId) ||
        normalizedId.endsWith('/src/main.ts');

      if (!isMain) return;

      // Already injected?
      if (code.includes('oxc-angular-prevent-reload')) return;

      const snippet = `
        // === oxc-angular HMR full-reload prevention (injected) ===
        if (import.meta.hot) {
          const PREVENT = Symbol('oxc-angular-prevent-reload');
          import.meta.hot.on('vite:beforeFullReload', (payload) => {
            if (
              !payload?.path ||
              payload.path === '*' ||
              payload.path.endsWith('.html')
            ) {
              console.debug('[oxc-angular] suppressed full-reload for', payload?.path);
              throw PREVENT;
            }
          });
          window.addEventListener('unhandledrejection', (e) => {
            if (e.reason === PREVENT) e.preventDefault();
          });
        }
        // === end injection ===
      `;

      return {
        code: snippet + code,
        map: null, // or use magic-string if you need accurate sourcemaps
      };
    },
  };
}

in vite config:

... 
plugins: [
   angular({
        liveReload: true,
    }),
   oxcAngularHmrFix(),
]
...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions