Branch feature/release-0.0.23/version-16-17
Angular 16 to 17 upgrade
The official 16 → 17 code work is in. What is left is proving the runtime checklist in a running app, plus deciding which build system that proof runs against.
Decisions that stayed locked
- Stay on NgModules. No standalone rewrite.
- Hydration stays off. The app never had
provideClientHydration. - Keep the custom Express path in
server.ts: white-label dist detection, helmet, compression, prerender fallback, trailing-slash 301s, referral?key=. @cotufa82/ng-ivsstays. Install withlegacy-peer-deps.functions/is leftover Firebase SSR and out of scope.firebase.jsonis hosting-only.- Do not invent
environment.dev.ts. That file was deleted in 2025.npm run build-devalready failed before 17.
What landed
Commit e9051eab15 is the upgrade. Later commits on the same branch changed the client builder. Treat those as follow-on, not as part of the official 16 → 17 guide.
Toolchain
- Node pinned to
20.19.6in.nvmrcand the unused Dockerfile. - Angular 17.3.12, CLI 17.3.17, TypeScript 5.4.5, Zone.js 0.14.10.
- Material / CDK 17.3.10, Angular ESLint 17.5.3, typescript-eslint 7.2, ESLint 8.57.
Angular-bound libraries
- NgRx 17.2.0, PrimeNG 17.18.15, AngularFire 17.1.0, ng-bootstrap 16, ngx-bootstrap 12, ng-select 12, ngx-stripe 17, Font Awesome 0.14, ZXing 17.
- Older packages still compile: ngx-intl-tel-input 15, ngx-toastr 14, ngx-quill 16, ngx-mask 16. Leave them unless a screen breaks.
@types/nodeis still 18 while runtime is Node 20. Harmless unless a Node 20 type is needed.
SSR
- Removed
@nguniversal/*. Added@angular/ssr17.3.17. server.tsnow renders withCommonEngine. Custom Express logic stayed.REQUEST/RESPONSElive insrc/app/core/express.tokens.ts.NotFoundComponentimportsRESPONSEfrom there so 404 can still set status.serverless.tsusesrenderModulefrom@angular/platform-server.- In
angular.json:browserTargetbecamebuildTarget.guessRoutesbecamediscoverRoutes. Prerender and SSR-dev-server builders moved to@angular-devkit/build-angular.
Compile fixes Angular 17 forced
- Firebase
provideFirebaseApp,provideMessaging, andprovideRemoteConfigmoved fromAppModule.importstoproviders. 17 rejectsEnvironmentProvidersin imports. post-price.component.htmlescaped@{{name}}to@{{name}}because the new control-flow parser treats@as syntax.TrailingSlashUrlSerializer.parse()wraps bad URLs in try/catch. 17 droppedmalformedUriErrorHandler.
Same-branch follow-on, not the 16 → 17 guide
- Committed HEAD uses
browser-esbuildfor the client, plus two crash fixes: Quill loaded as ESM, and leaf components no longer importCommonService. - The working tree currently swaps that for the
applicationbuilder and folds SSR / prerender intong build. That is unfinished local work. Do not mix its commands into the 17 upgrade proof until someone decides to ship it.
Official checklist
| Item | Status | Note |
|---|---|---|
| Node 18.13+, TypeScript 5.2+, Zone 0.14 | Done | Node 20.19.6, TypeScript 5.4.5, Zone 0.14.10 |
ng update core, CLI, Material 17 |
Done | Peers forced only for IVS. Workspace / SSR schematics were applied by hand so they would not overwrite server.ts |
Router options on forRoot |
Done | Already had paramsInheritanceStrategy: 'always' and urlUpdateStrategy: 'deferred'. No old Router setters to move |
Handle bad URLs in UrlSerializer.parse |
Done | Try/catch in trailing-slash-url.serializer.ts |
Zone deep imports, AnimationDriver.NOOP, Signals .mutate() |
N/A | Already on zone.js/node and zone.js/testing. The other APIs are unused |
Disable hydration / loadComponent param inheritance |
N/A | Hydration was never on. No loadComponent routes |
NgSwitch now uses === |
Verify | Compiler will not catch "1" vs 1. About 21 templates |
| Absolute redirect loops | Verify | Wallet bank-details redirect and marketing slug /music → /music-artists |
| Destroyed-component styles | Verify | Default is now strip styles on destroy. Only set REMOVE_STYLES_ON_COMPONENT_DESTROY if a modal or overlay goes blank |
ngDoCheck on factory-created components |
Likely N/A | Custom multiselect has ngDoCheck but is not created through a component factory |
Verification already done
These passed after the upgrade, on Node 20.19.6, against the webpack browser + separate server targets:
ng build --configuration=developmentng run TopFans:server:developmentng run TopFans:serverless:production
If you are verifying committed browser-esbuild or the uncommitted application builder, rerun those builds. Do not treat the original webpack pass as proof of the current tree.
Verification still required
This is the remaining 16 → 17 work. None of it needs more ng update.
1. NgSwitch strict equality
Open the console and look for Angular NgSwitch warnings. Then click through screens that switch on mixed types:
- Post unlock state:
post-card,post-detail,profile - Payments:
post-payment,merchandise-detail, live-stream paywall - Wallet transaction detail (sale type and transaction type)
- Dynamic form builder and table cell types
- Marketing layout previews (
layoutId,viewMode) - Create-plan subscription selection, mailer type dialog, share dialog
2. Absolute redirects
- Hit
/wallet/current-balance-home/add-bank-details. It must land on/wallet/current-balanceonce, not bounce. - If
music-artistsis a published marketing slug, hit/music. Same rule: one redirect, no loop.
3. Styles after a component is destroyed
Open and close stacked modals, toasts, and overlays (PrimeNG 17 and Material 17 both changed). If a later dialog looks unstyled, that is this flag. Do not flip REMOVE_STYLES_ON_COMPONENT_DESTROY until you see it.
4. Live SSR smoke
Serve the SSR bundle and check the responses, not just the HTML:
- Unknown route returns HTTP 404 from
NotFoundComponent - A trailing-slash URL 301s to the canonical path
- A referral URL with
?key=is rendered, not served from prerendered HTML - A route in
prerender.routes.txtis served as static HTML; a route that is not falls back toCommonEngine
5. Builds the upgrade did not run
- Production browser build
- Full prerender for one env that still has a real environment file: qa, staging, or prod
- One generated white-label project: browser build, SSR, tenant colors, tenant URLs, white-label-only guard
6. Visual and product smoke
- PrimeNG 17 and Material 17: tables, dialogs, datepickers, toasts
- Auth in and out, creator / dashboard / white-label guards
- Stripe test payment: success, decline, cancel
- Audio play / pause / seek / minimize. One video. One live stream if fixtures exist
ng lint OOMs on this repo. ng test was not run. Neither is a 17-specific blocker, but do not claim they passed.
npm run build-dev fails because src/environments/environment.dev.ts is gone. Use --configuration=development (uses environment.ts) or qa / staging / prod.
Commands
Put nvm Node 20 first. Cursor’s helper Node 22 will otherwise win on PATH.
export PATH="$HOME/.nvm/versions/node/v20.19.6/bin:$PATH" nvm use node --max_old_space_size=10048 ./node_modules/@angular/cli/bin/ng build --configuration=development node --max_old_space_size=10048 ./node_modules/@angular/cli/bin/ng run TopFans:server:development node --max_old_space_size=10048 ./node_modules/@angular/cli/bin/ng run TopFans:serverless:production
For a production-shaped pass against committed HEAD:
node --max_old_space_size=10048 ./node_modules/@angular/cli/bin/ng build --configuration=production node --max_old_space_size=10048 ./node_modules/@angular/cli/bin/ng run TopFans:prerender:qa
If the uncommitted application builder is what you are shipping, those ng run TopFans:server / prerender targets are gone. ng build --configuration=qa then builds client, server, and prerender together. Confirm that before QA copies the old scripts.
Out of scope
- Updating
@cotufa82/ng-ivs - Cleaning or deploying
functions/(Universal 16 + platform-server 18, unused) - Turning on hydration
- Migrating the remaining old ngx-* majors
- The
applicationbuilder, unless you explicitly take that as a separate task
Open questions
- Ship committed
browser-esbuild, revert to webpackbrowser, or finish the uncommittedapplicationbuilder first? Verification commands change with that answer. - Is anyone still deploying
exports.ssrfromfunctions/? If yes, that leftover is a production landmine, not a docs footnote.