Skip to content

Migrate from Angular 9 to React 18 + TypeScript + Vite - #648

Open
devanshi-gpta wants to merge 6 commits into
masterfrom
devin/1787076559-react-migration
Open

Migrate from Angular 9 to React 18 + TypeScript + Vite#648
devanshi-gpta wants to merge 6 commits into
masterfrom
devin/1787076559-react-migration

Conversation

@devanshi-gpta

@devanshi-gpta devanshi-gpta commented Aug 18, 2026

Copy link
Copy Markdown

Summary

Full rewrite of the Hacker News PWA from Angular 9 (Angular CLI, RxJS, @angular/service-worker) to React 18 + TypeScript on Vite, keeping the SCSS, theming, routes, URLs and PWA behaviour identical. The Angular sources, angular.json, karma/protractor stack and .travis.yml are gone; the app is now index.htmlsrc/main.tsxsrc/App.tsx.

Behaviour-preserving mapping:

Angular React
HackerNewsAPIService (Observables, lazyFetch) src/api/hackerNewsApi.ts — plain async functions + AbortController
SettingsService (BehaviorSubject + localStorage) src/context/SettingsContext.tsx + useSettings()
app.routes.ts w/ loadChildren src/routes.tsx w/ React.lazy + Suspense
comment.pipe.ts src/utils/formatCommentCount.ts
[innerHTML] dangerouslySetInnerHTML
ngsw-worker.js + ngsw-config.json vite-plugin-pwa (Workbox), manifest inlined in vite.config.ts

Poll aggregation in fetchItemContent keeps the original semantics, just parallelised:

const options = await Promise.all(story.poll.map((_, i) => fetchPollContent(story.id + i + 1, signal)));
story.poll = options;
story.poll_votes_count = options.reduce((total, o) => total + o.points, 0);

Two notes on things that are not obvious from the diff:

  • The PWA config initially precached the manifest icons twice (includeAssets + globPatterns), which made Workbox throw on conflicting entries and install a service worker with an empty cache — i.e. offline silently did not work. Fixed with globIgnores plus a trimmed includeAssets.
  • Playwright page.route stubs do not apply to requests issued through the service worker, so e2e/pwa.spec.ts asserts the precached shell renders plus the feed's error state instead of stubbed stories.

Testing: 62 Vitest/Testing Library tests (MSW-backed for anything touching the API) and 16 Playwright specs run against a production build, covering the redirect to /news/1, all five feeds with pagination, item details with nested comment collapse/expand, polls, user pages, theme persistence across reload, and service-worker/offline. CI (.github/workflows/ci.yml) runs typecheck, lint, unit tests, build and e2e.

Feed Item details
news feed item details, night theme

Link to Devin session: https://app.devin.ai/sessions/682e0e9ed3c5406ab13970ac8d15d795
Requested by: @devanshi-gpta


Devin Review

Status Commit
⚪ Not started

Run Devin Review

Open in Devin Review (Staging)

devin-ai-integration Bot and others added 4 commits August 18, 2026 18:13
Co-Authored-By: Devanshi Gupta <devanshi.gupta@cognition.ai>
Co-Authored-By: Devanshi Gupta <devanshi.gupta@cognition.ai>
Fix conflicting workbox precache entries so the service worker installs and the app works offline.

Co-Authored-By: Devanshi Gupta <devanshi.gupta@cognition.ai>
@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

Co-Authored-By: Devanshi Gupta <devanshi.gupta@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown

Runtime test pass — React 18 + Vite migration

Tested the production build (npm run build + vite preview on :4173) in a real browser against the live node-hnapi API, plus all automated suites. Everything specified works; two caveats below.

PWA / offline (production build)

Service worker activated and controlling /sw.js, workbox precache with 28 assets, manifest.webmanifest served as application/manifest+json (name "Angular 2 HN", 4 icons).

With the preview server killed and all network requests aborted, a reload still renders the app shell from precache with the graceful offline notice; restoring the network brings back all 30 stories.

Offline: app shell served from precache

Feeds, pagination, item details, polls
  • / -> /news/1; news / newest / show / ask / jobs all load distinct content
  • jobs feed shows the YC header text and no points/user/comment counts
  • More > -> /news/2 with numbering starting at 31; < Prev -> back to 1
  • item header + nested comment tree render; [-]/[+] collapse hides the comment and its subtree and restores it
  • poll /item/126809 renders option bars proportional to the 73 / 49 / 179 vote counts

Poll bars proportional to votes
Page 2 numbering starts at 31

Settings popup

All three themes apply (AMOLED background is pure rgb(0,0,0)), font size and list spacing change rendering, "open links in a new tab" adds target="_blank" rel="noopener" and genuinely opens a new tab, and every setting survives a reload via localStorage.

AMOLED theme

Automated checks + e2e flake (fixed)

npm test 62 · npm run typecheck · npm run lint · npm run test:e2e 16 specs — all green.

e2e/user.spec.ts "navigating to a user page from a comment" was genuinely flaky at ~1/30 (--repeat-each=15 --workers=4 -> 29 passed / 1 failed): the active service worker intercepted the request before Playwright's route stub. Fixed in d6f75c9 by setting serviceWorkers: 'block' in playwright.config.ts with test.use({ serviceWorkers: 'allow' }) scoped to the PWA spec — --repeat-each=6 --workers=4 now gives 96/96.

Caveats

  • User profile pages could not be verified: the live API returned 404 for every username tried (pg, dang, tyre, mixmax) during the whole session. The route resolves and the app shows a graceful "Could not load user X" screen, but the profile rendering path is unproven — worth a re-check when the upstream API recovers.
  • Visual comparison vs. the original Angular styling found nothing off (header, numbered list, grey subtext, red links, skull error state, footer, mobile back arrow all match).

Full recording: https://app.devin.ai/attachments/fa744560-08d6-4ae7-a9c8-e746cedfa33e/rec-6318187d-6d0b-49f4-b45c-5f52d85f042d-edited.mp4

Tested by Devin.

…ories

Co-Authored-By: Devanshi Gupta <devanshi.gupta@cognition.ai>
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