From 002e00f16da0c866843ee0a68087ec616a2b9843 Mon Sep 17 00:00:00 2001 From: wyattb Date: Thu, 12 Mar 2026 05:47:28 -0400 Subject: [PATCH 1/3] #199 Reduce initial bundle size via lazy loading and route-level code splitting Convert all route components to use loadComponent for lazy loading instead of eager imports. Remove bulk NgModule imports from main.ts bootstrap since standalone components already import what they need. Initial bundle drops from 4.43 MB to 1.47 MB (67% reduction). Co-Authored-By: Claude Opus 4.6 (1M context) --- angular-client/src/app/app-routing.module.ts | 79 ++++++++++++-------- angular-client/src/main.ts | 59 +-------------- 2 files changed, 53 insertions(+), 85 deletions(-) diff --git a/angular-client/src/app/app-routing.module.ts b/angular-client/src/app/app-routing.module.ts index ee1c5dd9..77d4a387 100644 --- a/angular-client/src/app/app-routing.module.ts +++ b/angular-client/src/app/app-routing.module.ts @@ -1,14 +1,4 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { BmsDebugPageComponent } from 'src/pages/bms-debug-page/bms-debug-page.component'; -import { BmsSegmentViewComponent } from 'src/pages/bms-debug-page/bms-segment-view/bms-segment-view.component'; -import { CameraPageComponent } from 'src/pages/camera-page/camera-page.component'; -import CarCommandComponent from 'src/pages/car-command-page/car-command.component'; -import ChargingPageComponent from 'src/pages/charging-page/charging-page.component'; -import FaultPageComponent from 'src/pages/fault-page/fault-page.component'; -import GraphPageComponent from 'src/pages/graph-page/graph-page.component'; -import LandingPageComponent from 'src/pages/landing-page/landing-page.component'; -import MapComponent from 'src/pages/map/map.component'; +import { Routes } from '@angular/router'; import { Segment } from 'src/utils/bms.utils'; const landingRoute = () => `/landing`; @@ -35,24 +25,53 @@ export const appRoutes = { commandsRoute }; -// Routes should be defined carefully in accordance with the appRoutes -const routes: Routes = [ - { path: 'landing', component: LandingPageComponent }, - { path: 'graph', component: GraphPageComponent }, +// Routes use loadComponent for lazy loading / route-level code splitting. +// Each page is loaded on demand rather than bundled into the initial chunk. +export const routes: Routes = [ + { + path: 'landing', + loadComponent: () => import('src/pages/landing-page/landing-page.component') + }, + { + path: 'graph', + loadComponent: () => import('src/pages/graph-page/graph-page.component') + }, { path: '', redirectTo: appRoutes.landingRoute(), pathMatch: 'full' }, - { path: 'map', component: MapComponent }, - { path: 'charging', component: ChargingPageComponent }, - { path: 'bms', component: BmsDebugPageComponent }, - // NOTE: paramaterized routes should be even more carefully defined in accordance with the appRoutes - { path: 'bms/segment/:id', component: BmsSegmentViewComponent }, - { path: 'faults', component: FaultPageComponent }, - { path: 'faults/fault-graph', component: GraphPageComponent }, - { path: 'camera', component: CameraPageComponent }, - { path: 'commands', component: CarCommandComponent } + { + path: 'map', + loadComponent: () => import('src/pages/map/map.component') + }, + { + path: 'charging', + loadComponent: () => import('src/pages/charging-page/charging-page.component') + }, + { + path: 'bms', + loadComponent: () => + import('src/pages/bms-debug-page/bms-debug-page.component').then((m) => m.BmsDebugPageComponent) + }, + { + path: 'bms/segment/:id', + loadComponent: () => + import('src/pages/bms-debug-page/bms-segment-view/bms-segment-view.component').then( + (m) => m.BmsSegmentViewComponent + ) + }, + { + path: 'faults', + loadComponent: () => import('src/pages/fault-page/fault-page.component') + }, + { + path: 'faults/fault-graph', + loadComponent: () => import('src/pages/graph-page/graph-page.component') + }, + { + path: 'camera', + loadComponent: () => + import('src/pages/camera-page/camera-page.component').then((m) => m.CameraPageComponent) + }, + { + path: 'commands', + loadComponent: () => import('src/pages/car-command-page/car-command.component') + } ]; - -@NgModule({ - imports: [RouterModule.forRoot(routes)], - exports: [RouterModule] -}) -export class AppRoutingModule {} diff --git a/angular-client/src/main.ts b/angular-client/src/main.ts index a9256ce8..47cf57f0 100644 --- a/angular-client/src/main.ts +++ b/angular-client/src/main.ts @@ -6,68 +6,17 @@ import { provideClientHydration, BrowserModule, bootstrapApplication } from '@an import Lara from '@primeng/themes/aura'; import { provideAnimationsAsync } from '@angular/platform-browser/animations/async'; import { providePrimeNG } from 'primeng/config'; -import { AppRoutingModule } from './app/app-routing.module'; -import { CarouselModule } from 'primeng/carousel'; -import { NgApexchartsModule } from 'ng-apexcharts'; -import { ToastModule } from 'primeng/toast'; -import { OrderListModule } from 'primeng/orderlist'; -import { ProgressSpinnerModule } from 'primeng/progressspinner'; -import { MatIconModule } from '@angular/material/icon'; -import { MatGridListModule } from '@angular/material/grid-list'; -import { ButtonModule } from 'primeng/button'; -import { MatToolbarModule } from '@angular/material/toolbar'; -import { MatButtonModule } from '@angular/material/button'; -import { MatInputModule } from '@angular/material/input'; -import { ReactiveFormsModule, FormsModule } from '@angular/forms'; -import { MatCardModule } from '@angular/material/card'; -import { MatDividerModule } from '@angular/material/divider'; -import { SidebarModule } from 'primeng/sidebar'; -import { MatSelectModule } from '@angular/material/select'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { SelectModule } from 'primeng/select'; -import { AccordionModule } from 'primeng/accordion'; -import { TableModule } from 'primeng/table'; -import { TreeModule } from 'primeng/tree'; -import { InputTextModule } from 'primeng/inputtext'; -import { InputNumberModule } from 'primeng/inputnumber'; -import { PasswordModule } from 'primeng/password'; +import { provideRouter } from '@angular/router'; +import { routes } from './app/app-routing.module'; import { importProvidersFrom, provideExperimentalZonelessChangeDetection } from '@angular/core'; import AppContextComponent from './app/context/app-context.component'; -import { ToggleSwitchModule } from 'primeng/toggleswitch'; bootstrapApplication(AppContextComponent, { providers: [ + provideRouter(routes), importProvidersFrom( BrowserModule, - AppRoutingModule, - CarouselModule, - NgApexchartsModule, - NgApexchartsModule, - ToastModule, - OrderListModule, - ProgressSpinnerModule, - MatIconModule, - MatGridListModule, - DynamicDialogModule, - ButtonModule, - MatToolbarModule, - MatButtonModule, - MatInputModule, - ReactiveFormsModule, - MatCardModule, - MatDividerModule, - SidebarModule, - MatSelectModule, - MatFormFieldModule, - FormsModule, - SelectModule, - AccordionModule, - TableModule, - TreeModule, - InputTextModule, - InputNumberModule, - PasswordModule, - ToggleSwitchModule + DynamicDialogModule ), DialogService, MessageService, From 3833ab6c1232b4854ebb98d9de88769bbdbdd160 Mon Sep 17 00:00:00 2001 From: wyattb Date: Sun, 29 Mar 2026 14:21:35 -0400 Subject: [PATCH 2/3] #199 simplify route comment to reference ticket --- angular-client/src/app/app-routing.module.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/angular-client/src/app/app-routing.module.ts b/angular-client/src/app/app-routing.module.ts index 48b991d4..b4a4f6cb 100644 --- a/angular-client/src/app/app-routing.module.ts +++ b/angular-client/src/app/app-routing.module.ts @@ -27,8 +27,7 @@ export const appRoutes = { efusesRoute }; -// Routes use loadComponent for lazy loading / route-level code splitting. -// Each page is loaded on demand rather than bundled into the initial chunk. +// Lazy-loaded routes: each page is split into its own chunk to reduce initial bundle size (#199). export const routes: Routes = [ { path: 'landing', From 0dcb395476e7805b6cf42fdafdb76e22fc71bd8c Mon Sep 17 00:00:00 2001 From: wyattb Date: Sun, 29 Mar 2026 14:28:39 -0400 Subject: [PATCH 3/3] #199 apply prettier formatting --- angular-client/src/app/app-routing.module.ts | 10 +++------- angular-client/src/main.ts | 5 +---- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/angular-client/src/app/app-routing.module.ts b/angular-client/src/app/app-routing.module.ts index b4a4f6cb..7dae9216 100644 --- a/angular-client/src/app/app-routing.module.ts +++ b/angular-client/src/app/app-routing.module.ts @@ -48,15 +48,12 @@ export const routes: Routes = [ }, { path: 'bms', - loadComponent: () => - import('src/pages/bms-debug-page/bms-debug-page.component').then((m) => m.BmsDebugPageComponent) + loadComponent: () => import('src/pages/bms-debug-page/bms-debug-page.component').then((m) => m.BmsDebugPageComponent) }, { path: 'bms/segment/:id', loadComponent: () => - import('src/pages/bms-debug-page/bms-segment-view/bms-segment-view.component').then( - (m) => m.BmsSegmentViewComponent - ) + import('src/pages/bms-debug-page/bms-segment-view/bms-segment-view.component').then((m) => m.BmsSegmentViewComponent) }, { path: 'faults', @@ -68,8 +65,7 @@ export const routes: Routes = [ }, { path: 'camera', - loadComponent: () => - import('src/pages/camera-page/camera-page.component').then((m) => m.CameraPageComponent) + loadComponent: () => import('src/pages/camera-page/camera-page.component').then((m) => m.CameraPageComponent) }, { path: 'commands', diff --git a/angular-client/src/main.ts b/angular-client/src/main.ts index 47cf57f0..1e28bb38 100644 --- a/angular-client/src/main.ts +++ b/angular-client/src/main.ts @@ -14,10 +14,7 @@ import AppContextComponent from './app/context/app-context.component'; bootstrapApplication(AppContextComponent, { providers: [ provideRouter(routes), - importProvidersFrom( - BrowserModule, - DynamicDialogModule - ), + importProvidersFrom(BrowserModule, DynamicDialogModule), DialogService, MessageService, ChipFaultPipe,