Skip to content

Wave 1: Upgrade core framework + build config from Angular 9 to Angular 21#421

Open
tobydrinkall wants to merge 2 commits into
masterfrom
devin/1782984123-angular20-core-upgrade
Open

Wave 1: Upgrade core framework + build config from Angular 9 to Angular 21#421
tobydrinkall wants to merge 2 commits into
masterfrom
devin/1782984123-angular20-core-upgrade

Conversation

@tobydrinkall

@tobydrinkall tobydrinkall commented Jul 2, 2026

Copy link
Copy Markdown

Summary

Foundational PR of a 4-wave Angular 9 → 21 migration (Angular 21 is the latest version supported by the repo's Node 22.12 toolchain; Angular 22 requires Node ≥22.22). This wave gets npm run build green on Angular 21; follow-up waves handle RxJS/TS API changes, component/template fixes, and test+lint modernization on top of this branch.

Key changes:

  • package.json: all @angular/* ~9.0.x^21.0.0, typescript ~3.7.5~5.9.2, zone.js ~0.10~0.16, tslib 1 → 2; karma toolchain bumped to versions required by build-angular 21 peer deps (karma 6.4, jasmine-core 5, karma-coverage replaces karma-coverage-istanbul-reporter); codelyzer removed (incompatible, ESLint migration lands in wave 4)
  • angular.json: webpack browser builder → esbuild application builder (mainbrowser, dropped removed options extractCss/aot/buildOptimizer/vendorChunk, serviceWorker now takes the ngsw config path); dev-server browserTargetbuildTarget; added development configuration + defaultConfiguration
  • Components/pipes: added standalone: false to all 11 components and the CommentPipe (standalone became the default in v19; app stays NgModule-based)
  • src/test.ts deleted; test target now uses polyfills: ["zone.js", "zone.js/testing"] (esbuild karma builder auto-discovers specs); zone.js/dist/zone deep import → zone.js
  • tsconfig: target es2022, moduleResolution bundler, skipLibCheck (needed while rxjs 6 typings remain)

Intentionally deferred: rxjs stays at 6.5 + rxjs-compat (deep rxjs/Observable imports still compile) — wave 2; Sass @import/division deprecation warnings — wave 3; ng lint (tslint builder no longer exists) and spec/karma updates — wave 4.

Verified locally: npm install and npm run build succeed.

Link to Devin session: https://app.devin.ai/sessions/11374427e0fc4cf6acdf4838f7a9651c
Requested by: @tobydrinkall


Open in Devin Review

Co-Authored-By: Toby Drinkall <toby.drinkall@cognition.ai>
@tobydrinkall tobydrinkall self-assigned this Jul 2, 2026
@devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

Open in Devin Review

Comment thread angular.json
Comment thread package.json
Comment thread tsconfig.json
Comment on lines +8 to +9
"module": "preserve",
"moduleResolution": "bundler",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Removal of emitDecoratorMetadata is safe for Ivy-based Angular

The PR removes emitDecoratorMetadata from tsconfig.json. This is correct because Angular's Ivy compiler (default since Angular 9) does not rely on TypeScript's emitDecoratorMetadata for dependency injection - it uses its own compile-time analysis instead. This removal is safe and aligns with modern Angular project configurations.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread angular.json
Co-Authored-By: Toby Drinkall <toby.drinkall@cognition.ai>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 new potential issues.

Open in Devin Review

Comment thread angular.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Deprecated tooling configs retained (tslint, protractor)

The angular.json still references @angular-devkit/build-angular:tslint (angular.json:106) and @angular-devkit/build-angular:protractor (angular.json:119). Both tslint and protractor have been deprecated for years — tslint was superseded by eslint, and protractor was removed from Angular CLI in v15. The package.json still lists tslint (package.json:47) and protractor (package.json:45) as devDependencies. While these won't break the build, ng lint and ng e2e will likely fail at runtime since the builders may not exist in @angular-devkit/build-angular v21. Consider migrating to @angular-eslint and a modern e2e framework like Cypress or Playwright.

(Refers to lines 105-129)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional: the TSLint → ESLint migration and removal of the protractor e2e setup are Wave 4 of this migration and are being handled in a follow-up PR based on this branch. This foundational PR only targets a green npm run build.

Comment thread angular.json
}
}
},
"defaultConfiguration": "production"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Build default changed from development to production

The build target now has "defaultConfiguration": "production" (angular.json:68), meaning ng build (and therefore npm run build) will produce a production build by default. Previously with the browser builder and no defaultConfiguration, ng build ran without any configuration applied. This is the standard convention for modern Angular projects but is a behavioral change worth noting — all ng build invocations will now apply production optimizations, file replacements, and service worker registration unless explicitly overridden.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread firebase.json
},
"hosting": {
"public": "dist",
"public": "dist/angular-hnpwa/browser",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Application builder output path structure change affects deployment

The application builder outputs client files to dist/angular-hnpwa/browser/ rather than the old browser builder's dist/angular-hnpwa/. The firebase.json was correctly updated to dist/angular-hnpwa/browser (firebase.json:6). However, any other deployment scripts, CI pipelines, or documentation referencing the old dist/ or dist/angular-hnpwa/ path would need updating. The CONTRIBUTING.md at step 10 references updating the dist/ directory, which is now stale but is guidance text rather than executable code.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread package.json
"rxjs-compat": "^6.5.2",
"tslib": "^1.10.0",
"tslib": "^2.6.0",
"unfetch": "^4.1.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: unfetch dependency may conflict with esbuild-based application builder

The hackernews-api.service.ts uses import fetch from 'unfetch' (src/app/shared/services/hackernews-api.service.ts:3) as a fetch polyfill. The application builder uses esbuild internally, which handles module resolution differently than webpack. The unfetch package uses a default export pattern that should work with esModuleInterop: true (added in this PR's tsconfig changes), but unfetch is also unnecessary in modern browsers — the native fetch API is available in all evergreen browsers. This dependency could be removed entirely.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration

Copy link
Copy Markdown

E2E Test Results — Angular 21 upgrade (waves 1–4 integrated)

Tested by merging all four wave branches (#421 + #422 + #423 + #424) into a local integration branch, running build/tests/lint, then clicking through the app served via ng serve.

All tests passed:

  • Top stories feed renders 30 stories with ranks/points/comment links (proves the zoneless-CD fix in Wave 3: Component/template and Sass modernization #424 — without it the app hangs on the loader) — passed
  • Feed navigation (news → newest) — passed
  • Pagination (page 2 ranks start at 31) — passed
  • Item details with nested comment threads — passed
  • Night theme toggles instantly via settings — passed
  • /user/:id error path renders "Could not load user X." — passed (upstream node-hnapi user endpoint is dead; pre-existing, not migration-related)

Top stories feed rendering on Angular 21

More evidence
Item details with comments Night theme
Item details Night theme

Shell evidence (integration branch):

  • npm run build — success (341 kB initial, lazy chunks for item-details/user)
  • npm test -- --watch=false --browsers=ChromeHeadless — 7/7 SUCCESS
  • npm run lint — all files pass (ESLint)

⚠️ Merge-order note: #422 and #423 conflict with each other in feed.component.ts, item-details.component.ts, user.component.ts, and package-lock.json (both touched the same .subscribe() calls). Resolution is trivial (keep #422's object-form subscribe + #423's const), but whichever merges second needs a rebase.

Devin session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant