What changed
Before this PR, each page needed three files. A component declared with standalone: false, an NgModule that declared it, and a routing module with a single route. Now the component lists its own imports, and the router loads it directly.
| Page | URLs | Now loads with | Component imports |
|---|---|---|---|
advertisement-home |
/advertise |
loadComponent |
CommonModule, SharedModule, FaqComponent, ButtonComponent |
global |
/global/category-creator-listing |
loadChildren with global.routes.ts |
CommonModule, SharedModule |
marketing-pro-page |
/pro, /refer, /apps/* |
loadComponent, generated |
CommonModule, MarketingPageSharedModule, FaqComponent |
advertisement-home dropped six unused imports
Its module imported form, select, modal, accordion, multiselect, and video modules. The template uses none of them. It uses buttons, the FAQ, a circle icon, and two directives from SharedModule.
imports: [
FormsModule,
NgSelectModule,
ModalModule,
AccordionModule,
NgMultiSelectDropDownModule.forRoot(),
NgIvsModule,
SharedModule,
CommonModule,
FaqComponent,
ButtonComponent,
AdvertisementHomeRoutingModule
]
@Component({
selector: 'app-advertisement-home',
imports: [
CommonModule,
SharedModule,
FaqComponent,
ButtonComponent
]
})
If the template had needed one of the dropped modules, the build would have failed. Standalone components don't get CUSTOM_ELEMENTS_SCHEMA from anywhere, so an unknown tag is a compile error. The build passed.
global kept a routes file
The other pages had a child route with an empty path, so the component could move up to the parent route. global's child route has a real path, category-creator-listing. The app route stays global with loadChildren, and the child moved into a plain Routes array. link-in-bio-fan-preview did the same in PR 10826.
- import('./modules/global/global.module').then((m) => m.GlobalModule)
+ import('./modules/global/global.routes').then((m) => m.GLOBAL_ROUTES)
The module also imported RouterModule, but the template has no router directives, so the component doesn't import it.
Marketing-pro routes are generated
scripts/prefetch-marketing-pages.mjs runs before every QA, staging, and prod build. It fetches published pro pages and writes marketing-pro-page-routes.generated.ts. Pro slugs can be nested, like apps/music-artists-fan-club, so the script builds a tree. Parent segments get children, and each leaf loads the page. The PR changes the leaf template in the script and the committed generated file together.
I found a stale file along the way. The committed routes list two /apps/* pages, but the committed data file only has apps/music-band-fan-club. So on a local build without a fresh prefetch, the pro pages load the component, find no data, and redirect to not-found. This was already true before the PR. Deployed builds regenerate both files, so they don't hit it.
What stayed the same
- Route data. The marketing-pro component finds
marketingProSlugby walking uproute.parent. The data now sits on the component's own route, one step closer. - Case-insensitive URLs. The
appsparent haschildren, so it matches a prefix. Each leaf has nochildrenarray and must match the rest of the URL. That was true for the oldloadChildrenleaves too. - The category listing's data. The search bar puts the chosen category in
DataService, then navigates.DataServiceis a root singleton with aBehaviorSubject, so the standalone component reads the same instance. - Initial bundle. 12.30 MB on both the base branch and this branch.
How it was tested
I built the base branch and this branch with ng build --configuration=qa --prerender=false, started the SSR server from each, and requested the same URLs.
| URL | Base | This PR |
|---|---|---|
/advertise | 200, app-advertisement-home | same |
/Advertise | 404 | same |
/global/category-creator-listing | 200, app-creator-category-listing | same |
/global | 200, falls through to a later route | same |
/apps/music-artists-fan-club | 404, stale data | same |
/apps/sports-teams-nil-collectives-membership | 404, stale data | same |
Then I ran npm run prefetch-pages-qa to get fresh data and rebuilt. The script wrote loadComponent for all six pro routes, top-level and nested. None of the three generated route files uses loadChildren anymore.
| URL, fresh QA data | Result |
|---|---|
/pro | 200, page, FAQ, contact form |
/refer | 200, page only |
/apps/music-artists-fan-club | 200, page, FAQ, contact form |
/Apps/Music-Artists-Fan-Club | 200, page, FAQ, contact form |
/apps/music-band-fan-club | 200, page, FAQ, contact form |
/apps/sports-teams-nil-collectives-membership | 200, page, FAQ, contact form |
/refer hiding the FAQ and contact form is intentional. The template skips both when the slug is refer. I reverted the regenerated data afterward, so the PR doesn't include it.
Affected areas
All three are public pages. Nobody needs to log in to test them.
| Area | Risk | Why |
|---|---|---|
| Advertise page | Medium | Six imports removed. The build proves the template compiles, but only clicking proves the buttons and FAQ behave after hydration. |
| Category creator listing | Medium | Reached from the header search bar, with state passed through a service. It has search, infinite scroll, and a back button. |
| Marketing-pro pages | Low | Same imports as before, minus the module wrapper. The route shape changed in a generated file. |
| Prefetch script | Low | Runs before every deployed build. Already exercised against QA. |
Verification checklist
Test each page two ways. Load the URL directly, which renders on the server and then hydrates. Then reach it by clicking from another page, which makes the browser fetch the lazy chunk. My curl test only covered the first way. Checks are saved in this browser.
Medium Advertise page, /advertise
Medium Category creator listing, /global/category-creator-listing
Low Marketing-pro pages, /pro, /refer, /apps/*
Low Prefetch script
On every page
Not tested
- Nobody has clicked through these pages in a browser yet. That's what the checklist is for.
- No white-label project was built.
What's next
No single-component feature modules are left. The remaining ones have 3 to 75 components each, and every one of them imports SharedModule. I'd make SharedModule's 147 declarations standalone before converting those, so each new component can import only what it uses.