diff --git a/MAINTAINER.md b/MAINTAINER.md
index a39a3c93f..c3e417704 100644
--- a/MAINTAINER.md
+++ b/MAINTAINER.md
@@ -1,5 +1,9 @@
# Contributing (Maintainer)
+## Debugging
+
+Repo-local debugging guidance for Analog package development lives in [docs/debugging.md](docs/debugging.md).
+
## Adding and updating an package
Adding or updating a package requires some extra step if its part of `dependencies` or `devDependencies`.
diff --git a/apps/docs-app/docs/guides/debugging.md b/apps/docs-app/docs/guides/debugging.md
index ca299ba53..9f1cf1267 100644
--- a/apps/docs-app/docs/guides/debugging.md
+++ b/apps/docs-app/docs/guides/debugging.md
@@ -4,7 +4,9 @@ sidebar_position: 4
# Debugging
-Analog includes structured debug logging powered by [obug](https://www.npmjs.com/package/obug). Debug output can be enabled through the `debug` option in your Vite config or via the `DEBUG` environment variable.
+Analog includes structured debug logging powered by [obug](https://www.npmjs.com/package/obug). You can enable debug output through the `debug` option in your Vite config or via the `DEBUG` environment variable.
+
+The examples below use `npm`, but the same `DEBUG` values work with any package manager.
## Enabling Debug Output
@@ -91,20 +93,20 @@ analog({
});
```
-You can mix immediate and deferred entries — entries without `mode` enable immediately for both commands:
+You can mix immediate and deferred entries. Entries without `mode` enable immediately for both commands:
```ts
analog({
debug: [
- { scopes: ['analog:platform'] }, // both commands
- { scopes: ['analog:angular:hmr'], mode: 'dev' }, // dev only
- { scopes: ['analog:platform:typed-router'], mode: 'build' }, // build only
+ { scopes: ['analog:platform'] },
+ { scopes: ['analog:angular:hmr'], mode: 'dev' },
+ { scopes: ['analog:platform:typed-router'], mode: 'build' },
],
});
```
:::tip
-To enable debug output for **both** build and dev, simply omit `mode`. Any form without `mode` — `true`, a `string[]`, or `{ scopes }` — outputs in both commands.
+To enable debug output for both build and dev, omit `mode`. Any form without `mode` outputs in both commands.
:::
### Environment variable
@@ -113,13 +115,13 @@ The `DEBUG` environment variable works independently of the config option and is
```bash
# All Analog scopes
-DEBUG=analog:* pnpm dev
+DEBUG=analog:* npm run dev
# Specific scopes
-DEBUG=analog:platform:routes,analog:angular:compiler pnpm build
+DEBUG=analog:platform:routes,analog:angular:compiler npm run build
# All platform scopes
-DEBUG=analog:platform:* pnpm dev
+DEBUG=analog:platform:* npm run dev
```
## Debugging a local Analog checkout from another pnpm workspace
@@ -248,7 +250,7 @@ import angular from '@analogjs/vite-plugin-angular';
export default defineConfig({
plugins: [
angular({
- debug: true, // enables analog:angular:* scopes
+ debug: true,
}),
],
});
diff --git a/apps/docs-app/docs/guides/migrating-v2-to-v3.md b/apps/docs-app/docs/guides/migrating-v2-to-v3.md
index b2cfbb54b..88b0c92e6 100644
--- a/apps/docs-app/docs/guides/migrating-v2-to-v3.md
+++ b/apps/docs-app/docs/guides/migrating-v2-to-v3.md
@@ -158,4 +158,4 @@ Keep automated migration tooling focused on the breaking changes above:
- rewrite only the legacy `@analogjs/vite-plugin-angular/setup-vitest` setup import
- flag `@analogjs/trpc` as a removed package that needs a manual migration plan
- flag `experimental.useAnalogCompiler`, `analogCompilationMode`, and `@analogjs/angular-compiler` as unsupported on the current v3 alpha line rather than removed outright
-- treat optional helpers such as `withTypedRouter`, `withRouteContext`, `withLoaderCaching`, `withDebugRoutes`, and compatibility aliases such as `liveReload` as opt-in rather than mandatory rewrites
+- treat optional helpers such as `withTypedRouter`, `withRouteContext`, `withLoaderCaching`, `withDebugRoutes`, and `liveReload` as opt-in rather than mandatory rewrites
diff --git a/apps/docs-app/docs/guides/migrating.md b/apps/docs-app/docs/guides/migrating.md
index 02f71e57a..401adc758 100644
--- a/apps/docs-app/docs/guides/migrating.md
+++ b/apps/docs-app/docs/guides/migrating.md
@@ -190,8 +190,11 @@ export default defineConfig(({ mode }) => ({
## Enabling HMR
-Angular supports HMR where in most cases components can be updated without a full page reload. In Analog, prefer the `hmr` option. `liveReload` is still accepted as a compatibility alias, but `hmr` is the primary API.
-Analog requires Angular v19 or newer for `hmr` / `liveReload` to work. On Angular v17-v18, `hmr` and its `liveReload` alias are forcibly disabled at runtime with a console warning, so HMR is unavailable on those versions.
+Angular supports HMR where in most cases components can be updated without a full page reload. In Analog, use `liveReload` to control the Angular live-reload pipeline.
+
+This is separate from Vite's `server.hmr` option, which configures the HMR websocket transport. You can use `server.hmr` together with `liveReload` when you need custom host, port, or path settings.
+
+Analog requires Angular v19 or newer for `liveReload` to work. On Angular v17-v18, `liveReload` is forcibly disabled at runtime with a console warning, so HMR is unavailable on those versions.
```ts
///
@@ -204,7 +207,7 @@ export default defineConfig(({ mode }) => ({
// .. other configuration
plugins: [
analog({
- hmr: true,
+ liveReload: true,
}),
],
}));
@@ -223,7 +226,7 @@ import tailwindcss from '@tailwindcss/vite';
export default defineConfig(() => ({
plugins: [
analog({
- hmr: true,
+ liveReload: true,
vite: {
tailwindCss: {
rootStylesheet: resolve(import.meta.dirname, 'src/styles.css'),
diff --git a/apps/docs-app/docs/integrations/storybook/index.md b/apps/docs-app/docs/integrations/storybook/index.md
index 67532239a..448d78105 100644
--- a/apps/docs-app/docs/integrations/storybook/index.md
+++ b/apps/docs-app/docs/integrations/storybook/index.md
@@ -91,7 +91,9 @@ const config: StorybookConfig = {
export default config;
```
-For current Analog projects, prefer `framework.options.hmr` if you need to configure Angular HMR. `liveReload` is still accepted as a compatibility alias, but `hmr` is the recommended option.
+For current Analog projects, use `framework.options.liveReload` to control Analog's Angular live-reload behavior.
+
+This is separate from Vite's `server.hmr` option, which configures the HMR websocket transport. You can use `server.hmr` together with `framework.options.liveReload` when Storybook needs custom host, port, or path settings.
Remove the existing `webpackFinal` config function if present.
@@ -177,7 +179,7 @@ If your project uses Tailwind v4, keep Storybook aligned with the same opinionat
- one root stylesheet such as `src/styles.css`
- `@import 'tailwindcss';` in that stylesheet
- `framework.options.tailwindCss.rootStylesheet` pointing at that stylesheet
-- `framework.options.hmr` for Angular HMR behavior
+- `framework.options.liveReload` for Angular reload behavior
```ts
import { resolve } from 'node:path';
@@ -187,7 +189,7 @@ const config: StorybookConfig = {
framework: {
name: '@analogjs/storybook-angular',
options: {
- hmr: true,
+ liveReload: true,
tailwindCss: {
rootStylesheet: resolve(__dirname, '../src/styles.css'),
},
diff --git a/apps/docs-app/docs/integrations/tailwind/index.md b/apps/docs-app/docs/integrations/tailwind/index.md
new file mode 100644
index 000000000..198fdcadf
--- /dev/null
+++ b/apps/docs-app/docs/integrations/tailwind/index.md
@@ -0,0 +1,146 @@
+# Tailwind CSS v4
+
+Analog does not replace Tailwind's installation guides. Start with one Tailwind setup that matches your project:
+
+- [Install Tailwind with Vite](https://tailwindcss.com/docs/installation/using-vite)
+- [Install Tailwind with PostCSS](https://tailwindcss.com/docs/installation/using-postcss)
+- [Install Tailwind with Angular](https://tailwindcss.com/docs/installation/framework-guides/angular)
+
+Once Tailwind is installed, Analog adds the Angular-specific part: component stylesheet handling for `@apply` and Tailwind-aware `@reference` injection.
+
+## What Analog adds
+
+Use Analog's `tailwindCss.rootStylesheet` option when you want Tailwind utilities inside Angular component styles.
+
+That option lets Analog:
+
+- detect component stylesheets that use Tailwind utilities
+- inject the correct `@reference` to your root stylesheet
+- keep component styles aligned with your root Tailwind theme, prefixes, and plugins
+- avoid manual `@reference` directives in every component stylesheet
+
+If you only use Tailwind utilities in templates and a global stylesheet, you can follow Tailwind's install docs and keep your generated scaffold defaults without adding extra Analog configuration.
+
+## Component Styles Setup
+
+When you enable `tailwindCss.rootStylesheet`, keep Tailwind wired through Vite for the component stylesheet path:
+
+```ts
+///
+
+import { resolve } from 'node:path';
+import { defineConfig } from 'vite';
+import analog from '@analogjs/platform';
+import tailwindcss from '@tailwindcss/vite';
+
+export default defineConfig(() => ({
+ plugins: [
+ analog({
+ vite: {
+ tailwindCss: {
+ rootStylesheet: resolve(__dirname, 'src/styles.css'),
+ },
+ },
+ }),
+ tailwindcss(),
+ ],
+}));
+```
+
+If you are using `@analogjs/vite-plugin-angular` directly instead of `@analogjs/platform`, the same option lives on the Angular plugin:
+
+```ts
+import { resolve } from 'node:path';
+import { defineConfig } from 'vite';
+import angular from '@analogjs/vite-plugin-angular';
+import tailwindcss from '@tailwindcss/vite';
+
+export default defineConfig(() => ({
+ plugins: [
+ angular({
+ tailwindCss: {
+ rootStylesheet: resolve(__dirname, 'src/styles.css'),
+ },
+ }),
+ tailwindcss(),
+ ],
+}));
+```
+
+List `analog()` before `tailwindcss()` in your Vite config. Current generators now scaffold that order.
+
+## Root Stylesheet
+
+In `src/styles.css`:
+
+```css
+@import 'tailwindcss';
+```
+
+You can keep your theme, `@source`, plugins, and prefixes there as well:
+
+```css
+@import 'tailwindcss' prefix(tw);
+
+@source './src';
+
+@theme {
+ --color-primary: #3b82f6;
+}
+```
+
+Use an absolute `rootStylesheet` path. Analog may serve component styles through virtual stylesheet ids during dev, so relative `@reference` paths are not reliable there.
+
+## How Component Styles Work
+
+Angular compiles component styles in isolation. When a component stylesheet contains `@apply`, Tailwind still needs access to the root stylesheet that defines prefixes, theme values, and plugins.
+
+Analog handles that by:
+
+- detecting Tailwind usage in component CSS
+- injecting `@reference` to the configured root stylesheet
+- routing those component styles through the Vite CSS pipeline when needed
+
+That means you should not manually add `@reference` to every component stylesheet in the normal setup.
+
+## Prefixes
+
+If your component styles use custom-prefixed utilities, configure `prefixes` so Analog knows which stylesheets need Tailwind `@reference` injection:
+
+```ts
+analog({
+ vite: {
+ tailwindCss: {
+ rootStylesheet: resolve(__dirname, 'src/styles.css'),
+ prefixes: ['tw:'],
+ },
+ },
+});
+```
+
+Without `prefixes`, Analog falls back to its default Tailwind usage detection for component styles.
+
+## HMR
+
+Use `liveReload` when you need to configure Analog's Angular live-reload behavior explicitly.
+
+Vite's `server.hmr` option is separate. It controls the HMR websocket transport, so you can use `server.hmr` together with `liveReload` when your dev server needs custom host, port, or path settings.
+
+Angular HMR requires Angular v19 or newer. On Angular v17-v18, `liveReload` is intentionally disabled at runtime and emits a console warning, so HMR is unavailable on those versions. For broader migration guidance, see the [migration guide](/docs/guides/migrating).
+
+Tailwind support does not require you to enable HMR manually. The stylesheet pipeline is handled independently from whether Angular can produce a hot component update for a given edit.
+
+## Generated Apps
+
+Current `create-analog` and Nx app scaffolds already:
+
+- import Tailwind in `src/styles.css`
+- register Tailwind in `vite.config.ts`
+- keep the generated Vite plugin order aligned with the current Analog templates
+
+Some templates may also include additional Tailwind tooling config files. Treat the generated scaffold as your project default, and only diverge after validating your own dev and build behavior.
+
+## Related
+
+- [Using CSS Pre-processors](/docs/packages/vite-plugin-angular/css-preprocessors)
+- [create-analog](/docs/packages/create-analog/overview)
diff --git a/apps/docs-app/docs/packages/create-analog/overview.md b/apps/docs-app/docs/packages/create-analog/overview.md
index efc62021d..1ac297658 100644
--- a/apps/docs-app/docs/packages/create-analog/overview.md
+++ b/apps/docs-app/docs/packages/create-analog/overview.md
@@ -47,14 +47,11 @@ pnpm create analog
### Tailwind v4
-`create-analog` scaffolds Tailwind v4 with the Vite plugin by default for the current Analog templates. Generated projects use `@tailwindcss/vite`, add `@import 'tailwindcss';` to `src/styles.css`, and also generate a `postcss.config.mjs` with `@tailwindcss/postcss` so the build path and tool integrations use the same Tailwind setup.
+`create-analog` scaffolds Tailwind v4 for the current Analog templates. Generated projects import Tailwind in `src/styles.css` and wire the Tailwind-related Vite config the template expects.
-This is the recommended Analog v3 direction:
+If you only need Tailwind utilities in templates and global styles, keep the scaffold defaults.
-- Keep one root stylesheet, usually `src/styles.css`, that contains `@import 'tailwindcss';`
-- Keep `@tailwindcss/vite` enabled in `vite.config.ts`
-- Let Analog handle component-level `@reference` injection through its Tailwind-aware stylesheet pipeline instead of adding `@reference` directives manually in every component stylesheet
-- Prefer the `hmr` option over `liveReload` when you need to configure Angular HMR explicitly
+If you also want `@apply` inside Angular component styles, add Analog's `tailwindCss.rootStylesheet` option and follow the [Tailwind CSS guide](/docs/integrations/tailwind).
If you do not want Tailwind in the generated app, pass `--skipTailwind true`. The default Tailwind v4 flow expects a plain CSS entry file for global styles.
diff --git a/apps/docs-app/docs/packages/vite-plugin-angular/css-preprocessors.md b/apps/docs-app/docs/packages/vite-plugin-angular/css-preprocessors.md
index 9f6a49f9e..4a256b24b 100644
--- a/apps/docs-app/docs/packages/vite-plugin-angular/css-preprocessors.md
+++ b/apps/docs-app/docs/packages/vite-plugin-angular/css-preprocessors.md
@@ -4,9 +4,9 @@ title: 'Using CSS Pre-processors'
The Vite Plugin supports CSS pre-processing using external `styleUrls` and inline `styles` in the Component decorator metadata.
-## Recommended Tailwind v4 setup
+## Tailwind v4 component styles
-If your app uses Tailwind v4, the recommended Analog setup is opinionated:
+Tailwind installation itself should follow Tailwind's docs. The Analog-specific configuration below is for Angular component styles that use Tailwind utilities such as `@apply`.
- keep a single root stylesheet such as `src/styles.css`
- put `@import 'tailwindcss';` in that root stylesheet
@@ -15,6 +15,8 @@ If your app uses Tailwind v4, the recommended Analog setup is opinionated:
This lets Analog preprocess component stylesheets and inject the correct `@reference` directive automatically for component CSS that uses Tailwind utilities.
+For the broader Tailwind + Analog overview, see the [Tailwind CSS guide](/docs/integrations/tailwind).
+
```ts
///
@@ -29,7 +31,7 @@ export default defineConfig(() => ({
tailwindCss: {
rootStylesheet: resolve(__dirname, 'src/styles.css'),
},
- hmr: true,
+ liveReload: true,
}),
tailwindcss(),
],
@@ -44,8 +46,12 @@ And in `src/styles.css`:
Use an absolute path for `rootStylesheet`. Analog serves some component styles through virtual stylesheet ids during dev, so relative `@reference` paths are not reliable there.
+Use `liveReload` to control Analog's Angular reload behavior. Vite's top-level `server.hmr` option remains available when you need to configure the HMR websocket transport separately.
+
You only need `tailwindCss.prefixes` when your component styles use custom-prefixed utilities and you want Analog to look for those prefixes instead of the default `@apply` detection.
+If you only use Tailwind utilities in templates and a global stylesheet, you can keep your Tailwind install path and skip `tailwindCss.rootStylesheet`.
+
External `styleUrls` can be used without any additional configuration.
An example with `styleUrls`:
diff --git a/docs/debugging.md b/docs/debugging.md
new file mode 100644
index 000000000..439fbbf88
--- /dev/null
+++ b/docs/debugging.md
@@ -0,0 +1,143 @@
+# Debugging
+
+This document is for maintainers and contributors working inside the Analog monorepo.
+
+For consumer-facing debug flags and scope reference, see the public guide in `apps/docs-app/docs/guides/debugging.md`.
+
+This repo-local file covers the monorepo-specific workflow that does not belong in the public docs site.
+
+## Repo Root Commands
+
+From this workspace, the common debug flow is to run commands from the repo root with `pnpm` or `pnpm nx`.
+
+```bash
+# Serve the default dev app with all Analog scopes
+DEBUG=analog:* pnpm dev
+
+# Build from the repo root with selected scopes
+DEBUG=analog:platform:routes,analog:angular:compiler pnpm build
+
+# Serve a specific app target through Nx
+DEBUG=analog:platform:* pnpm nx serve docs-app
+```
+
+## Package Development
+
+When debugging package changes in this monorepo, prefer the project-level Nx targets so you stay on the same workspace graph and dependency layout as CI:
+
+```bash
+# Focus on Angular plugin HMR/style behavior
+DEBUG=analog:angular:hmr,analog:angular:styles pnpm nx test vite-plugin-angular
+
+# Focus on platform routing output
+DEBUG=analog:platform:routes pnpm nx build platform
+
+# Focus on the style-pipeline integration seam in a served app
+DEBUG=analog:platform:style-pipeline,analog:angular:style-pipeline pnpm nx serve your-app
+```
+
+## Debugging a local Analog checkout from another pnpm workspace
+
+If you want to debug this checkout while serving a different app on your machine,
+point that consumer workspace at the built Analog package outputs under
+`/path/to/analog/packages/*/dist`.
+
+Use the built `dist` directories, not the raw package roots. Build the packages
+first so each `dist` folder contains its generated `package.json`. The source
+package manifests still contain `catalog:` and `workspace:*` references that are
+only rewritten during Analog's release-style build pipeline.
+
+### Local checkout example
+
+`pnpm-workspace.yaml`
+
+```yaml
+packages:
+ - 'apps/*'
+ - 'libs/**'
+
+overrides:
+ '@analogjs/platform': file:/path/to/analog/packages/platform/dist
+ '@analogjs/router': file:/path/to/analog/packages/router/dist
+ '@analogjs/vite-plugin-angular': file:/path/to/analog/packages/vite-plugin-angular/dist
+ '@analogjs/vite-plugin-nitro': file:/path/to/analog/packages/vite-plugin-nitro/dist
+ '@analogjs/vitest-angular': file:/path/to/analog/packages/vitest-angular/dist
+```
+
+Root `package.json`
+
+```json
+{
+ "dependencies": {
+ "@analogjs/platform": "file:/path/to/analog/packages/platform/dist"
+ },
+ "overrides": {
+ "@analogjs/platform": "file:/path/to/analog/packages/platform/dist",
+ "@analogjs/router": "file:/path/to/analog/packages/router/dist",
+ "@analogjs/vite-plugin-angular": "file:/path/to/analog/packages/vite-plugin-angular/dist",
+ "@analogjs/vite-plugin-nitro": "file:/path/to/analog/packages/vite-plugin-nitro/dist",
+ "@analogjs/vitest-angular": "file:/path/to/analog/packages/vitest-angular/dist"
+ }
+}
+```
+
+:::important
+Keep the overrides in both places. If you only pin `@analogjs/platform`, pnpm
+can still resolve transitive packages like `@analogjs/vite-plugin-angular` and
+`@analogjs/vite-plugin-nitro` from npm instead of your local checkout.
+:::
+
+:::note
+pnpm currently does not allow `file:` entries in `catalog`, so local checkout
+wiring needs direct `file:` overrides instead of `catalog:` indirection.
+:::
+
+If your app also uses other published Analog packages such as
+`@analogjs/content` or `@analogjs/storybook-angular`, pin those the same way.
+
+### GitHub branch example
+
+If you want the same pattern from a GitHub branch instead of a local path, pnpm
+supports Git subdirectory specs via `#branch&path:...`.
+
+`pnpm-workspace.yaml`
+
+```yaml
+catalog:
+ '@analogjs/platform': github:your-user/analog#your-branch&path:packages/platform/dist
+ '@analogjs/router': github:your-user/analog#your-branch&path:packages/router/dist
+ '@analogjs/vite-plugin-angular': github:your-user/analog#your-branch&path:packages/vite-plugin-angular/dist
+ '@analogjs/vite-plugin-nitro': github:your-user/analog#your-branch&path:packages/vite-plugin-nitro/dist
+ '@analogjs/vitest-angular': github:your-user/analog#your-branch&path:packages/vitest-angular/dist
+```
+
+Root `package.json`
+
+```json
+{
+ "dependencies": {
+ "@analogjs/platform": "catalog:"
+ },
+ "overrides": {
+ "@analogjs/platform": "$@analogjs/platform",
+ "@analogjs/router": "$@analogjs/router",
+ "@analogjs/vite-plugin-angular": "$@analogjs/vite-plugin-angular",
+ "@analogjs/vite-plugin-nitro": "$@analogjs/vite-plugin-nitro",
+ "@analogjs/vitest-angular": "$@analogjs/vitest-angular"
+ }
+}
+```
+
+:::caution
+For Analog, the GitHub form only works when the branch exposes release-ready
+`dist/package.json` files at those paths. Pointing pnpm at
+`path:packages/platform` or any other raw source package path will fail because
+those manifests still contain unresolved `catalog:` and `workspace:*`
+specifiers.
+:::
+
+## Notes
+
+- Use the repo root unless you have a specific reason to run inside a package subdirectory.
+- Prefer `pnpm nx ` when you want task-graph behavior that matches CI.
+- The scope names themselves are documented in the public guide so consumer and maintainer docs stay aligned.
diff --git a/packages/create-analog/__tests__/cli.spec.ts b/packages/create-analog/__tests__/cli.spec.ts
index fe90609f7..6bf4dbdc0 100644
--- a/packages/create-analog/__tests__/cli.spec.ts
+++ b/packages/create-analog/__tests__/cli.spec.ts
@@ -55,7 +55,7 @@ const expectTailwindScaffold = () => {
expect(readGeneratedStyles()).toContain(`@import 'tailwindcss';`);
expect(viteConfig).toContain(`import tailwindcss from '@tailwindcss/vite';`);
expect(viteConfig).toMatch(
- /plugins:\s*\[[\s\S]*tailwindcss\(\),[\s\S]*analog\(/,
+ /plugins:\s*\[[\s\S]*analog\(\),[\s\S]*tailwindcss\(\)/,
);
expect(readFileSync(join(genPath, 'postcss.config.mjs'), 'utf-8')).toContain(
`'@tailwindcss/postcss': {}`,
diff --git a/packages/create-analog/template-latest/vite.config.ts b/packages/create-analog/template-latest/vite.config.ts
index 997423525..8737bf35c 100644
--- a/packages/create-analog/template-latest/vite.config.ts
+++ b/packages/create-analog/template-latest/vite.config.ts
@@ -12,8 +12,8 @@ export default defineConfig(({ mode }) => ({
mainFields: ['module'],
},
plugins: [
-__TAILWIND_PLUGIN__ analog(),
- ],
+ analog(),
+__TAILWIND_PLUGIN__ ],
test: {
globals: true,
environment: 'jsdom',
diff --git a/packages/nx-plugin/src/generators/app/files/template-angular-v17/vite.config.ts__template__ b/packages/nx-plugin/src/generators/app/files/template-angular-v17/vite.config.ts__template__
index a7c5e682b..3ca4b4e30 100644
--- a/packages/nx-plugin/src/generators/app/files/template-angular-v17/vite.config.ts__template__
+++ b/packages/nx-plugin/src/generators/app/files/template-angular-v17/vite.config.ts__template__
@@ -23,10 +23,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
+ analog(),
<% if (addTailwind) { %>
tailwindcss(),
<% } %>
- analog(),
],
test: {
globals: true,
diff --git a/packages/nx-plugin/src/generators/app/files/template-angular-v18/vite.config.ts__template__ b/packages/nx-plugin/src/generators/app/files/template-angular-v18/vite.config.ts__template__
index f8efa3634..8cd519adc 100644
--- a/packages/nx-plugin/src/generators/app/files/template-angular-v18/vite.config.ts__template__
+++ b/packages/nx-plugin/src/generators/app/files/template-angular-v18/vite.config.ts__template__
@@ -22,10 +22,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
- <% if (addTailwind) { %>
+ analog(),
+ <% if (addTailwind) { %>
tailwindcss(),
<% } %>
- analog(),
],
test: {
globals: true,
diff --git a/packages/nx-plugin/src/generators/app/files/template-angular-v19/vite.config.ts__template__ b/packages/nx-plugin/src/generators/app/files/template-angular-v19/vite.config.ts__template__
index d0fe48d9e..749ed2ccd 100644
--- a/packages/nx-plugin/src/generators/app/files/template-angular-v19/vite.config.ts__template__
+++ b/packages/nx-plugin/src/generators/app/files/template-angular-v19/vite.config.ts__template__
@@ -22,10 +22,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
+ analog(),
<% if (addTailwind) { %>
tailwindcss(),
<% } %>
- analog(),
],
test: {
globals: true,
diff --git a/packages/nx-plugin/src/generators/app/files/template-angular/vite.config.ts__template__ b/packages/nx-plugin/src/generators/app/files/template-angular/vite.config.ts__template__
index 2d4262240..bc85590a1 100644
--- a/packages/nx-plugin/src/generators/app/files/template-angular/vite.config.ts__template__
+++ b/packages/nx-plugin/src/generators/app/files/template-angular/vite.config.ts__template__
@@ -22,10 +22,10 @@ export default defineConfig(({ mode }) => {
},
},
plugins: [
+ analog(),
<% if (addTailwind) { %>
tailwindcss(),
<% } %>
- analog(),
],
test: {
globals: true,
diff --git a/packages/nx-plugin/src/generators/app/generator.spec.ts b/packages/nx-plugin/src/generators/app/generator.spec.ts
index cc0321ae5..a92a78ff7 100644
--- a/packages/nx-plugin/src/generators/app/generator.spec.ts
+++ b/packages/nx-plugin/src/generators/app/generator.spec.ts
@@ -114,7 +114,7 @@ describe('nx-plugin generator', () => {
`import tailwindcss from '@tailwindcss/vite';`,
);
expect(viteConfig).toMatch(
- /plugins:\s*\[[\s\S]*tailwindcss\(\),[\s\S]*analog\(/,
+ /plugins:\s*\[[\s\S]*analog\(\),[\s\S]*tailwindcss\(\)/,
);
};
diff --git a/packages/nx-plugin/src/generators/app/templates.spec.ts b/packages/nx-plugin/src/generators/app/templates.spec.ts
index 8e1d3ee30..4f67f5600 100644
--- a/packages/nx-plugin/src/generators/app/templates.spec.ts
+++ b/packages/nx-plugin/src/generators/app/templates.spec.ts
@@ -21,6 +21,17 @@ const versionedTemplates = readdirSync(templatesDir).filter((d) =>
);
describe('generator templates', () => {
+ describe.each(versionedTemplates)('%s vite.config.ts', (template) => {
+ const viteConfig = readFileSync(
+ join(templatesDir, template, 'vite.config.ts__template__'),
+ 'utf-8',
+ );
+
+ it('registers analog exactly once', () => {
+ expect(viteConfig.match(/analog\(\)/g)).toHaveLength(1);
+ });
+ });
+
describe.each(versionedTemplates)('%s tsconfig.json', (template) => {
const tsconfig = readTemplateJson(template, 'tsconfig.json__template__');
diff --git a/packages/nx-plugin/src/generators/preset/__snapshots__/generator.spec.ts.snap b/packages/nx-plugin/src/generators/preset/__snapshots__/generator.spec.ts.snap
index dcf7ec6f4..c71d7c385 100644
--- a/packages/nx-plugin/src/generators/preset/__snapshots__/generator.spec.ts.snap
+++ b/packages/nx-plugin/src/generators/preset/__snapshots__/generator.spec.ts.snap
@@ -119,7 +119,7 @@ export default defineConfig(({ mode }) => {
allow: ['.'],
},
},
- plugins: [tailwindcss(), analog()],
+ plugins: [analog(), tailwindcss()],
test: {
globals: true,
environment: 'jsdom',
diff --git a/packages/platform/src/lib/options.ts b/packages/platform/src/lib/options.ts
index e780e33e4..a02697ce3 100644
--- a/packages/platform/src/lib/options.ts
+++ b/packages/platform/src/lib/options.ts
@@ -98,7 +98,7 @@ export interface Options {
*
* When `false`, the following top-level options are ignored because they
* are only forwarded to the internal Angular plugin: `jit`,
- * `disableTypeChecking`, `hmr`, `liveReload`, `inlineStylesExtension`,
+ * `disableTypeChecking`, `liveReload`, `inlineStylesExtension`,
* `fileReplacements`, and `include`.
*
* Use this to configure the embedded Angular integration itself, not as the
@@ -116,14 +116,13 @@ export interface Options {
*/
inlineStylesExtension?: string;
/**
- * Enables Angular's HMR during development/watch mode.
+ * Enables Analog's Angular live-reload/HMR pipeline during development/watch mode.
+ *
+ * This is separate from Vite's `server.hmr` option, which configures the
+ * HMR client transport.
*
* Defaults to `true` for watch mode.
*/
- hmr?: boolean;
- /**
- * @deprecated Use `hmr` instead. Kept as a compatibility alias.
- */
liveReload?: boolean;
/**
* Enable debug logging for specific scopes.
diff --git a/packages/platform/src/lib/platform-plugin.ts b/packages/platform/src/lib/platform-plugin.ts
index a5312d550..054373596 100644
--- a/packages/platform/src/lib/platform-plugin.ts
+++ b/packages/platform/src/lib/platform-plugin.ts
@@ -30,6 +30,11 @@ export function platformPlugin(opts: Options = {}): Plugin[] {
const isTest = process.env['NODE_ENV'] === 'test' || !!process.env['VITEST'];
const viteOptions = opts?.vite === false ? undefined : opts?.vite;
+ const {
+ experimental: viteExperimental,
+ hmr: _removedViteHmrOption,
+ ...forwardedViteOptions
+ } = viteOptions ?? {};
const { ...platformOptions } = {
ssr: true,
...opts,
@@ -56,7 +61,7 @@ export function platformPlugin(opts: Options = {}): Plugin[] {
const useAngularCompilationAPI =
platformOptions.experimental?.useAngularCompilationAPI ??
- viteOptions?.experimental?.useAngularCompilationAPI;
+ viteExperimental?.useAngularCompilationAPI;
debugPlatform('experimental options resolved', {
useAngularCompilationAPI: !!useAngularCompilationAPI,
typedRouter: platformOptions.experimental?.typedRouter,
@@ -112,7 +117,6 @@ export function platformPlugin(opts: Options = {}): Plugin[] {
),
],
additionalContentDirs: platformOptions.additionalContentDirs,
- hmr: platformOptions.hmr,
liveReload: platformOptions.liveReload,
inlineStylesExtension: platformOptions.inlineStylesExtension,
fileReplacements: platformOptions.fileReplacements,
@@ -124,9 +128,9 @@ export function platformPlugin(opts: Options = {}): Plugin[] {
platformOptions.experimental.stylePipeline.angularPlugins,
}
: undefined,
- ...(viteOptions ?? {}),
+ ...forwardedViteOptions,
experimental: {
- ...(viteOptions?.experimental ?? {}),
+ ...(viteExperimental ?? {}),
useAngularCompilationAPI,
},
}),
diff --git a/packages/storybook-angular/README.md b/packages/storybook-angular/README.md
index ba7f84b89..a08cfa0b8 100644
--- a/packages/storybook-angular/README.md
+++ b/packages/storybook-angular/README.md
@@ -35,7 +35,7 @@ const config: StorybookConfig = {
framework: {
name: '@analogjs/storybook-angular',
options: {
- hmr: true,
+ liveReload: true,
},
},
};
@@ -152,7 +152,9 @@ In your global stylesheet, import Tailwind with:
Storybook does not automatically infer the Tailwind plugin from your app's `vite.config.ts`, so add it in `viteFinal` when your stories depend on Tailwind utilities.
-Angular HMR is controlled with `framework.options.hmr`. `liveReload` is still accepted as a compatibility alias, but `hmr` is the preferred option.
+Angular reload behavior is controlled with `framework.options.liveReload`.
+
+This is separate from Vite's `server.hmr` option, which configures the HMR websocket transport. You can use `server.hmr` together with `framework.options.liveReload` when Storybook needs custom host, port, or path settings.
## Enabling Zoneless Change Detection
diff --git a/packages/storybook-angular/src/lib/preset.spec.ts b/packages/storybook-angular/src/lib/preset.spec.ts
index c54501dc4..7b0c58103 100644
--- a/packages/storybook-angular/src/lib/preset.spec.ts
+++ b/packages/storybook-angular/src/lib/preset.spec.ts
@@ -148,30 +148,24 @@ describe('viteFinal', () => {
};
describe('Angular plugin options', () => {
- it('prefers hmr over liveReload and keeps liveReload as compatibility input', async () => {
- const options = makeOptions({ hmr: true, liveReload: false });
+ it('forwards liveReload without a duplicate hmr flag', async () => {
+ const options = makeOptions({ liveReload: false });
await viteFinal(baseConfig, options);
- expect(angularPluginMock).toHaveBeenCalledWith(
- expect.objectContaining({
- hmr: true,
- liveReload: false,
- }),
- );
+ const [angularOptions] = angularPluginMock.mock.calls[0];
+ expect(angularOptions.liveReload).toBe(false);
+ expect(angularOptions).not.toHaveProperty('hmr');
});
- it('falls back to liveReload when hmr is omitted', async () => {
- const options = makeOptions({ liveReload: true });
+ it('defaults liveReload to false when omitted', async () => {
+ const options = makeOptions();
await viteFinal(baseConfig, options);
- expect(angularPluginMock).toHaveBeenCalledWith(
- expect.objectContaining({
- hmr: true,
- liveReload: true,
- }),
- );
+ const [angularOptions] = angularPluginMock.mock.calls[0];
+ expect(angularOptions.liveReload).toBe(false);
+ expect(angularOptions).not.toHaveProperty('hmr');
});
});
diff --git a/packages/storybook-angular/src/lib/preset.ts b/packages/storybook-angular/src/lib/preset.ts
index 5f0555968..3cfc9d0d5 100644
--- a/packages/storybook-angular/src/lib/preset.ts
+++ b/packages/storybook-angular/src/lib/preset.ts
@@ -76,10 +76,16 @@ export const viteFinal = async (config: any, options: any): Promise => {
// @ts-expect-error - untyped storybook presets API
const framework = await options.presets.apply('framework');
+ const { hmr: _removedHmrOption, ...frameworkOptions } =
+ framework.options ?? {};
const experimentalZoneless = await resolveExperimentalZoneless(
- framework.options,
+ frameworkOptions,
options?.angularBuilderOptions,
);
+ const liveReload =
+ typeof frameworkOptions.liveReload !== 'undefined'
+ ? frameworkOptions.liveReload
+ : false;
return vite.mergeConfig(config, {
// Add dependencies to pre-optimization
optimizeDeps: {
@@ -101,23 +107,17 @@ export const viteFinal = async (config: any, options: any): Promise => {
plugins: [
angular({
jit:
- typeof framework.options?.jit !== 'undefined'
- ? framework.options?.jit
+ typeof frameworkOptions.jit !== 'undefined'
+ ? frameworkOptions.jit
: true,
- hmr:
- typeof framework.options?.hmr !== 'undefined'
- ? framework.options?.hmr
- : typeof framework.options?.liveReload !== 'undefined'
- ? framework.options?.liveReload
- : false,
- liveReload: framework.options?.liveReload,
+ liveReload,
tsconfig:
- typeof framework.options?.tsconfig !== 'undefined'
- ? framework.options?.tsconfig
+ typeof frameworkOptions.tsconfig !== 'undefined'
+ ? frameworkOptions.tsconfig
: (options?.tsConfig ?? './.storybook/tsconfig.json'),
inlineStylesExtension:
- typeof framework.options?.inlineStylesExtension !== 'undefined'
- ? framework.options?.inlineStylesExtension
+ typeof frameworkOptions.inlineStylesExtension !== 'undefined'
+ ? frameworkOptions.inlineStylesExtension
: 'css',
}),
angularOptionsPlugin(options, {
diff --git a/packages/storybook-angular/src/types.ts b/packages/storybook-angular/src/types.ts
index 1fa7f702c..232e32b9a 100644
--- a/packages/storybook-angular/src/types.ts
+++ b/packages/storybook-angular/src/types.ts
@@ -10,9 +10,11 @@ type BuilderName = CompatibleString<'@storybook/builder-vite'>;
export type FrameworkOptions = {
builder?: BuilderOptions;
jit?: boolean;
- hmr?: boolean;
/**
- * @deprecated Use `hmr` instead. Kept as a compatibility alias.
+ * Enables Analog's Angular live-reload/HMR pipeline for Storybook.
+ *
+ * This is separate from Vite's `server.hmr` option, which configures the
+ * HMR client transport.
*/
liveReload?: boolean;
inlineStylesExtension?: string;
diff --git a/packages/vite-plugin-angular/README.md b/packages/vite-plugin-angular/README.md
index fe506e091..18b0c4c24 100644
--- a/packages/vite-plugin-angular/README.md
+++ b/packages/vite-plugin-angular/README.md
@@ -89,3 +89,9 @@ Create a `tsconfig.app.json` in the root of the project.
"include": ["src/**/*.ts"]
}
```
+
+## Tailwind CSS v4
+
+For Angular component styles that use Tailwind utilities like `@apply`, configure `tailwindCss.rootStylesheet` and follow the Tailwind guide for Analog:
+
+- https://analogjs.org/docs/integrations/tailwind
diff --git a/packages/vite-plugin-angular/src/lib/angular-vite-plugin-live-reload.spec.ts b/packages/vite-plugin-angular/src/lib/angular-vite-plugin-live-reload.spec.ts
index 345c8e0be..09a2458a0 100644
--- a/packages/vite-plugin-angular/src/lib/angular-vite-plugin-live-reload.spec.ts
+++ b/packages/vite-plugin-angular/src/lib/angular-vite-plugin-live-reload.spec.ts
@@ -125,9 +125,10 @@ async function setupLiveReloadPlugin(options: {
const { angular } = await import('./angular-vite-plugin');
const plugin = angular({
+ tsconfig: `${resolvedWorkspaceRoot}/tsconfig.base.json`,
+ liveReload: true,
include: options.include,
tsconfig: resolvedTsconfig,
- hmr: true,
inlineStylesExtension: 'css',
stylePreprocessor: options.stylePreprocessor,
stylePipeline: options.stylePipeline,
diff --git a/packages/vite-plugin-angular/src/lib/angular-vite-plugin.spec.ts b/packages/vite-plugin-angular/src/lib/angular-vite-plugin.spec.ts
index 710385a09..f6fd6af1d 100644
--- a/packages/vite-plugin-angular/src/lib/angular-vite-plugin.spec.ts
+++ b/packages/vite-plugin-angular/src/lib/angular-vite-plugin.spec.ts
@@ -87,7 +87,7 @@ describe('angularVitePlugin', () => {
});
});
-describe('hmr option', () => {
+describe('liveReload option', () => {
beforeEach(() => {
process.env['NODE_ENV'] = 'development';
delete process.env['VITEST'];
@@ -107,8 +107,8 @@ describe('hmr option', () => {
}
});
- it('disables HMR helper plugins when hmr is false', () => {
- const plugins = angular({ hmr: false });
+ it('disables HMR helper plugins when liveReload is false', () => {
+ const plugins = angular({ liveReload: false });
const names = plugins.map((plugin) => plugin.name);
expect(names).toEqual(expect.not.arrayContaining(hmrPluginNames));
@@ -119,20 +119,6 @@ describe('hmr option', () => {
expect(names).toEqual(expect.arrayContaining(hmrPluginNames));
});
-
- it('accepts liveReload as a compatibility alias for HMR', () => {
- const plugins = angular({ liveReload: true });
- const names = plugins.map((plugin) => plugin.name);
-
- expect(names).toEqual(expect.arrayContaining(hmrPluginNames));
- });
-
- it('prefers hmr over liveReload when both are provided', () => {
- const plugins = angular({ hmr: false, liveReload: true });
- const names = plugins.map((plugin) => plugin.name);
-
- expect(names).toEqual(expect.not.arrayContaining(hmrPluginNames));
- });
});
describe('isTestWatchMode', () => {
diff --git a/packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts b/packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts
index 984b3be57..8c6dffb66 100644
--- a/packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts
+++ b/packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts
@@ -160,14 +160,14 @@ export interface PluginOptions {
include?: string[];
additionalContentDirs?: string[];
/**
- * Enables Angular's HMR during development/watch mode.
+ * Enables Analog's Angular live-reload/HMR pipeline during development/watch mode.
*
- * Defaults to `true` for watch mode. Set to `false` to disable HMR while
- * keeping other stylesheet externalization behavior available when needed.
- */
- hmr?: boolean;
- /**
- * @deprecated Use `hmr` instead. Kept as a compatibility alias.
+ * This is separate from Vite's `server.hmr` option, which configures the
+ * HMR client transport.
+ *
+ * Defaults to `true` for watch mode. Set to `false` to disable Angular
+ * reload updates while keeping other stylesheet externalization behavior
+ * available when needed.
*/
liveReload?: boolean;
disableTypeChecking?: boolean;
@@ -442,6 +442,7 @@ export function buildStylePreprocessor(
export function angular(options?: PluginOptions): Plugin[] {
applyDebugOption(options?.debug, options?.workspaceRoot);
+ const liveReload = options?.liveReload ?? true;
/**
* Normalize plugin options so defaults
@@ -463,7 +464,7 @@ export function angular(options?: PluginOptions): Plugin[] {
jit: options?.jit,
include: options?.include ?? [],
additionalContentDirs: options?.additionalContentDirs ?? [],
- hmr: options?.hmr ?? options?.liveReload ?? true,
+ liveReload,
disableTypeChecking: options?.disableTypeChecking ?? true,
fileReplacements: options?.fileReplacements ?? [],
useAngularCompilationAPI:
@@ -512,9 +513,17 @@ export function angular(options?: PluginOptions): Plugin[] {
const transformedStyleOwnerMetadata = new Map();
const styleSourceOwners = new Map>();
- function shouldEnableHmr(): boolean {
+ function hasViteHmrTransport(): boolean {
+ return resolvedConfig ? resolvedConfig.server.hmr !== false : true;
+ }
+
+ function shouldEnableLiveReload(): boolean {
const effectiveWatchMode = isTest ? testWatchMode : watchMode;
- return !!(effectiveWatchMode && pluginOptions.hmr);
+ return !!(
+ effectiveWatchMode &&
+ pluginOptions.liveReload &&
+ hasViteHmrTransport()
+ );
}
/**
@@ -536,7 +545,7 @@ export function angular(options?: PluginOptions): Plugin[] {
function shouldExternalizeStyles(): boolean {
const effectiveWatchMode = isTest ? testWatchMode : watchMode;
if (!effectiveWatchMode) return false;
- return !!(shouldEnableHmr() || pluginOptions.hasTailwindCss);
+ return !!(shouldEnableLiveReload() || pluginOptions.hasTailwindCss);
}
/**
@@ -824,7 +833,7 @@ export function angular(options?: PluginOptions): Plugin[] {
function angularPlugin(): Plugin {
let isProd = false;
- if (angularFullVersion < 190000 && pluginOptions.hmr) {
+ if (angularFullVersion < 190000 && pluginOptions.liveReload) {
// Angular < 19 does not support externalRuntimeStyles or _enableHmr.
debugHmr('hmr disabled: Angular version does not support HMR APIs', {
angularVersion: angularFullVersion,
@@ -834,7 +843,7 @@ export function angular(options?: PluginOptions): Plugin[] {
'[@analogjs/vite-plugin-angular]: HMR was disabled because Angular v19+ is required for externalRuntimeStyles/_enableHmr support. Detected Angular version: %s.',
angularFullVersion,
);
- pluginOptions.hmr = false;
+ pluginOptions.liveReload = false;
}
if (isTest) {
@@ -843,7 +852,7 @@ export function angular(options?: PluginOptions): Plugin[] {
// This does NOT block style externalization — shouldExternalizeStyles()
// independently checks hasTailwindCss, so Tailwind utilities in
// component styles still work in unit tests.
- pluginOptions.hmr = false;
+ pluginOptions.liveReload = false;
debugHmr('hmr disabled', {
angularVersion: angularFullVersion,
isTest,
@@ -1051,7 +1060,7 @@ export function angular(options?: PluginOptions): Plugin[] {
let result;
- if (shouldEnableHmr()) {
+ if (shouldEnableLiveReload()) {
await pendingCompilation;
pendingCompilation = null;
result = fileEmitter(fileId);
@@ -1079,7 +1088,7 @@ export function angular(options?: PluginOptions): Plugin[] {
}
if (
- shouldEnableHmr() &&
+ shouldEnableLiveReload() &&
result?.hmrEligible &&
classNames.get(fileId)
) {
@@ -1434,7 +1443,7 @@ export function angular(options?: PluginOptions): Plugin[] {
}
if (
- shouldEnableHmr() &&
+ shouldEnableLiveReload() &&
/\.(html|htm)$/.test(ctx.file) &&
fileModules.length === 0
) {
@@ -2152,7 +2161,7 @@ export function angular(options?: PluginOptions): Plugin[] {
},
} satisfies Plugin),
angularPlugin(),
- pluginOptions.hmr && liveReloadPlugin({ classNames, fileEmitter }),
+ pluginOptions.liveReload && liveReloadPlugin({ classNames, fileEmitter }),
...(isTest && !isStackBlitz ? angularVitestPlugins() : []),
(jit &&
jitPlugin({
@@ -2405,7 +2414,7 @@ export function angular(options?: PluginOptions): Plugin[] {
// Populate classNames during initial compilation so HMR for
// HTML template changes can find the parent TS module.
- if (shouldEnableHmr() && className && containingFile) {
+ if (shouldEnableLiveReload() && className && containingFile) {
classNames.set(normalizePath(containingFile), className as string);
}
@@ -2483,14 +2492,15 @@ export function angular(options?: PluginOptions): Plugin[] {
tsCompilerOptions['externalRuntimeStyles'] = true;
}
- if (shouldEnableHmr()) {
+ if (shouldEnableLiveReload()) {
tsCompilerOptions['_enableHmr'] = true;
// Workaround for https://github.com/angular/angular/issues/59310
tsCompilerOptions['supportTestBed'] = true;
}
debugCompiler('tsCompilerOptions (compilation API)', {
- hmr: pluginOptions.hmr,
+ liveReload: pluginOptions.liveReload,
+ viteHmr: hasViteHmrTransport(),
hasTailwindCss: pluginOptions.hasTailwindCss,
watchMode,
shouldExternalize: shouldExternalizeStyles(),
@@ -2762,14 +2772,15 @@ export function angular(options?: PluginOptions): Plugin[] {
tsCompilerOptions['externalRuntimeStyles'] = true;
}
- if (shouldEnableHmr()) {
+ if (shouldEnableLiveReload()) {
tsCompilerOptions['_enableHmr'] = true;
// Workaround for https://github.com/angular/angular/issues/59310
tsCompilerOptions['supportTestBed'] = true;
}
debugCompiler('tsCompilerOptions (NgtscProgram path)', {
- hmr: pluginOptions.hmr,
+ liveReload: pluginOptions.liveReload,
+ viteHmr: hasViteHmrTransport(),
shouldExternalize: shouldExternalizeStyles(),
externalRuntimeStyles: !!tsCompilerOptions['externalRuntimeStyles'],
hmrEnabled: !!tsCompilerOptions['_enableHmr'],
@@ -2930,7 +2941,7 @@ export function angular(options?: PluginOptions): Plugin[] {
const fileMetadata = getFileMetadata(
builder,
angularCompiler!,
- pluginOptions.hmr,
+ pluginOptions.liveReload,
pluginOptions.disableTypeChecking,
);
diff --git a/packages/vite-plugin-angular/src/lib/live-reload-plugin.ts b/packages/vite-plugin-angular/src/lib/live-reload-plugin.ts
index c3201cce6..dfcbed8af 100644
--- a/packages/vite-plugin-angular/src/lib/live-reload-plugin.ts
+++ b/packages/vite-plugin-angular/src/lib/live-reload-plugin.ts
@@ -19,6 +19,11 @@ export function liveReloadPlugin({
name: 'analogjs-live-reload-plugin',
apply: 'serve',
configureServer(server: ViteDevServer) {
+ if (server.config.server.hmr === false) {
+ debugHmr('middleware disabled: vite server.hmr is false');
+ return;
+ }
+
const angularComponentMiddleware: Connect.HandleFunction = async (
req: Connect.IncomingMessage,
res: ServerResponse,