Pug support for @analogjs/vite-plugin-angular (JIT and AOT)
npm install angular-vite-pugPug support for @analogjs/vite-plugin-angular with zero-config JIT (dev) and AOT (build).
templateUrl: './view.pug' in your components.vite.config.ts.@analogjs/vite-plugin-angular 1.19+bash
pnpm add -D angular-vite-pug @analogjs/vite-plugin-angular vite
or
npm i -D angular-vite-pug @analogjs/vite-plugin-angular vite
`This package includes small
patch-package fixes that will apply on install.$3
Use angular-vite-pug in your vite.config.ts and keep using templateUrl: './cmp.pug' in components.`ts
// vite.config.ts
import { defineConfig } from 'vite';
import angularVitePug from 'angular-vite-pug';export default defineConfig({
plugins: [
// Auto-detects mode:
// - dev/serve → JIT (fast HMR)
// - build → AOT (resource transformer)
angularVitePug(),
],
});
`Component usage:
`ts
// example.component.ts
@Component({
selector: 'app-example',
templateUrl: './example.pug',
// styleUrls: ['./example.scss']
})
export class ExampleComponent {}
`Your Pug file can be colocated:
`pug
// example.pug
div.example
h1 Hello from Pug
`$3
`ts
// vite.config.ts
import path from 'node:path';
import { defineConfig } from 'vite';
import angularVitePug from 'angular-vite-pug';export default defineConfig(async () => {
const { default: viteTsconfigPaths } = await import('vite-tsconfig-paths');
return {
plugins: [
angularVitePug({
// Force mode if you need to:
// jit: true, // always JIT
// jit: false, // always AOT
}),
viteTsconfigPaths({
projects: [
path.resolve(__dirname, 'tsconfig.json'),
// path.resolve(__dirname, '../../tsconfig.base.json'),
],
}),
],
css: {
preprocessorOptions: {
sass: {
quietDeps: true,
logger: { warn: () => {} },
loadPaths: [
path.resolve(__dirname, 'src/styles'),
],
},
},
},
};
});
`$3
- JIT (development / serve)
- Automatically inlines or references the compiled HTML for templateUrl: './view.pug'.
- Watches Pug files for HMR-friendly updates.
- AOT (build / production)
- Uses an Angular resource transformer to compile Pug to HTML during the build pipeline.
- No runtime dependency on Pug in the final bundle.You can force a mode if needed:
`ts
angularVitePug({ jit: true }); // force JIT
angularVitePug({ jit: false }); // force AOT
`You can also pass through options to the underlying Angular plugin if required:
`ts
angularVitePug({
angularOptions: {
// any options supported by @analogjs/vite-plugin-angular
},
});
`$3
If you prefer to import Pug templates directly as strings (e.g., for small inline templates), you can do:
`ts
import template from './snippet.pug?raw';@Component({
selector: 'app-inline',
template,
})
export class InlineComponent {}
`$3
These are the concrete issues we hit in real Nx/Vite monorepos and how we fixed them.
- pnpm hoisting vs. vendored deps
- pnpm can hoist/dedupe dependencies so your package’s
node_modules may not contain the expected copy.
- We ship patched @analogjs/vite-plugin-angular and Pug deps as runtime dependencies and try to resolve them from inside this package first. If they are hoisted, we fall back to the workspace copy.
- Logs at startup will show which Analog plugin is used:
- “using VENDORED analog at …/angular-vite-pug/node_modules/@analogjs/vite-plugin-angular/…” ⇒ OK, patched plugin is in use.
- “using WORKSPACE analog at …/node_modules/@analogjs/vite-plugin-angular/…” ⇒ Workspace copy is used.- Vite importing package source (src) instead of dist
- In dev, Vite may prefer TypeScript sources if a package publishes
src/.
- Symptom: your logs look like they come from an older unpatched file or from src/index.ts instead of dist/index.js.
- Fix: we no longer publish src/ in the package to ensure consumers always load dist/.- TypeScript transformer crashes in AOT
- Symptom: “Cannot read properties of undefined (reading 'map')” or similar TS stack traces during transform.
- Cause: custom TS transformers running in AOT can collide with Angular’s emit pipeline or with a different TS instance.
- Fix: we only register the Pug TS transformer for JIT (dev). For AOT we rely on resource transformers.
- Multiple TypeScript instances / version drift
- Symptom: cryptic transformer errors like “statements is not iterable”.
- Cause: different parts of the toolchain using different
typescript versions/instances.
- Fix: align TS version across the workspace (e.g. pnpm.overrides: { typescript: "5.8.3" }). Avoid mixing global/local TS.- Nx not actually using Vite
- Symptom: changes in
vite.config.ts seem ignored.
- Fix: ensure project.json uses @nx/vite:build and @nx/vite:dev-server and points to the correct vite.config.ts.- ESM vs CJS config issues
- Symptom: “Dynamic require of 'fs' is not supported” when
vite.config.mts is loaded.
- Fix: use vite.config.ts (CommonJS evaluation by Nx) or migrate fully to ESM-safe imports.- Node version/chalk error with Nx
- Symptom: “chalk.cyan is not a function”.
- Fix: use Node LTS (e.g. v20). Some Nx versions are incompatible with Node 24 at the time of writing.
- Angular runtime requirements
- AOT requires Zone.js. Add this to your app entry:
`ts
import 'zone.js';
`
- JIT in dev requires the compiler:
`ts
if (process.env.NODE_ENV === 'development') {
import('@angular/compiler');
}
`- Pug template references (#ref) and Angular NG0301
- Symptom: “NG0301: Export of name '#myRef' not found!”.
- Cause: Pug emitting
#ref="..." as valued attribute instead of a boolean attribute.
- Fix: we ship patches for pug-code-gen/pug-lexer to output #ref as boolean and accept Angular bracket/paren tokens.- AOT Pug not converting / raw Pug in templates
- Ensure the Analog plugin receives
resourceTransformers and treats .pug as a template resource.
- This package patches the Analog plugin to accept resourceTransformers and adds .pug to resourceExtensions during AOT.
- Dev HMR: we include an auxiliary Vite plugin that watches .pug files and triggers full reload to refresh AOT resources.- Vite optimized cache masking changes
- Symptom: after upgrades, old behavior persists.
- Fix: clear Vite caches: delete
.vite dirs under the app and rerun the dev server.- Patches not applying / version mismatch
-
pnpm.patchedDependencies and patch-package must target exact versions; otherwise hunks fail.
- We embed patches and depend on the precise versions we patch. Avoid mixing external patches in the consumer.- Diagnostics: confirm what’s running
- On startup you should see logs from
angular-vite-pug:
- pkgDir, pkgRoot, whether vendored Analog exists, and which path is used.
- pug-code-gen at ... pointing to the copy inside this package when vendored is active.
- If you still see workspace paths for Analog, pnpm hoisting moved the dependency; functionally OK (patches are applied in our vendored copy), but consider aligning dependency layout if you need strict isolation.- Disable error overlay temporarily
- If the Vite overlay blocks you, set in your app’s
vite.config.ts:
`ts
export default defineConfig({
server: { hmr: { overlay: false } },
// ...
});
``