Angular Adapter
Angular 17+ with Signals, standalone components, lazy-loaded routes and RxJS. Production builds are compiled ahead of time by your project's Angular compiler, as ng build does.
lunx build runs @angular/compiler-cli (ngtsc), Angular's linker and Angular's build optimizations: no runtime template compiler, bundles the size of ng build's.
Angular 17 Signals API (signal, computed, effect) for fine-grained reactivity without Zone.js overhead.
No NgModule required. Standalone components, directives, and pipes with direct imports.
Full RxJS 7+ support. Observable-based data fetching, event streams, and state management.
lunx dev compiles components at runtime (JIT), with templateUrl / styleUrl inlined and SCSS, Less or Stylus styles compiled.
loadComponent() and loadChildren() produce separate async chunks for route-level code splitting.
Setup
npx lunx-dev@latest create my-angular-app --template angular --ts cd my-angular-app && npm install && npm run dev
import { defineConfig } from 'lunx-dev' export default defineConfig({ framework: 'angular', })
Standalone component with Signals
import { Component, signal, computed, effect } from '@angular/core' import { CommonModule } from '@angular/common' @Component({ selector: 'app-counter', standalone: true, imports: [CommonModule], template: ` <div class="counter"> <h2>Count: {{ count() }}</h2> <p>Doubled: {{ doubled() }}</p> <div class="controls"> <button (click)="decrement()">−</button> <button (click)="increment()">+</button> <button (click)="reset()" [disabled]="count() === 0">Reset</button> </div> </div> `, styles: [` .counter { display: flex; flex-direction: column; gap: 1rem; padding: 1.5rem; } .controls { display: flex; gap: 0.5rem; } button { padding: 0.5rem 1rem; border-radius: 8px; cursor: pointer; } button:disabled { opacity: 0.4; cursor: not-allowed; } `], }) export class CounterComponent { count = signal(0) doubled = computed(() => this.count() * 2) constructor() { // Runs whenever count changes — like useEffect effect(() => { console.log(`Count changed to ${this.count()}`) }) } increment() { this.count.update(n => n + 1) } decrement() { this.count.update(n => n - 1) } reset() { this.count.set(0) } }
Standalone app bootstrap
import { bootstrapApplication } from '@angular/platform-browser' import { provideRouter } from '@angular/router' import { provideHttpClient } from '@angular/common/http' import { AppComponent } from './app/app.component' import { routes } from './app/app.routes' bootstrapApplication(AppComponent, { providers: [ provideRouter(routes), provideHttpClient(), ], }).catch(console.error)
Lazy-loaded routes
import { Routes } from '@angular/router' export const routes: Routes = [ { path: '', loadComponent: () => import('./pages/home.component').then(m => m.HomeComponent), }, { path: 'dashboard', loadComponent: () => import('./pages/dashboard.component').then(m => m.DashboardComponent), canActivate: [() => inject(AuthService).isLoggedIn()], }, { path: 'admin', loadChildren: () => import('./admin/admin.routes').then(m => m.adminRoutes), }, { path: '**', loadComponent: () => import('./pages/not-found.component').then(m => m.NotFoundComponent), }, ]
HTTP data fetching with rxjs
import { Injectable, inject } from '@angular/core' import { HttpClient } from '@angular/common/http' import { Observable } from 'rxjs' import { catchError, map } from 'rxjs/operators' export interface User { id: number name: string email: string } @Injectable({ providedIn: 'root' }) export class UsersService { private http = inject(HttpClient) private base = import.meta.env.LUNX_PUBLIC_API_URL getUsers(): Observable<User[]> { return this.http.get<User[]>(`${this.base}/users`).pipe( map(users => users.sort((a, b) => a.name.localeCompare(b.name))), catchError(err => { console.error(err); throw err }) ) } getUserById(id: number): Observable<User> { return this.http.get<User>(`${this.base}/users/${id}`) } }
Production builds
With @angular/compiler-cli installed (the starter has it), lunx buildcompiles the app ahead of time with the project's Angular compiler, finishes partially compiled libraries with Angular's linker and runs Angular's build optimizations, so @angular/compileris not shipped. The starter's bundle is 31.7 kB gzip against 31.9 kB from ng build, built in 3.8 s cold against 4.9 s. It readstsconfig.app.json, or tsconfig.json.
npm install -D @angular/compiler-cliWithout it, the build compiles at runtime (JIT) and ships the template compiler (about 250 kB gzip), and lunx says so.
provideZonelessChangeDetection() and does not install Zone.js.