Skip to content

feature: migrate Angular 9 app to Vite + React 18 + TypeScript - #759

Closed
charityquinn-cognition wants to merge 5 commits into
masterfrom
devin/1788974057-react-migration
Closed

charityquinn-cognition wants to merge 5 commits into
masterfrom
devin/1788974057-react-migration

Conversation

@charityquinn-cognition

@charityquinn-cognition charityquinn-cognition commented Sep 9, 2026

Copy link
Copy Markdown

Summary

Full frontend rewrite from Angular 9 to Vite + React 18 + TypeScript + react-router-dom v6 + sass, keeping the same HN API (https://node-hnapi.herokuapp.com), URL structure, theming and PWA behaviour.

Structure

  • index.html (root) ports all meta/manifest/icon tags, #skip, .app-loader/noscript and GA snippet; <app-root><div id="root"> (#root:empty + .app-loader keeps the pre-boot loader working).
  • src/services/hackerNewsApi.ts: plain fetch functions fetchFeed / fetchItemContent / fetchPollContent / fetchUser. Poll options are fetched with Promise.allSettled over story.id + i; fulfilled options are kept and poll_votes_count is summed over them, so one failed option request doesn't hide the story (the Angular code's fire-and-forget subscriptions behaved the same way).
  • src/context/SettingsContext.tsx: SettingsProvider + useSettings() with the same settings shape, localStorage keys, and prefers-color-scheme listener (night/default, persisted on first load when no saved theme, as before).
  • Routes in App.tsx: //news/1; /{news,newest,show,ask,jobs}/:page<Feed feedType=…>; /item/:id, /user/:id via React.lazy. GA set page/send pageview fired from a useLocation effect.
  • Components: components/core/{Header,Footer,Settings}, components/shared/{Loader,ErrorMessage}, views/feed/{Feed,Item}, views/item-details/{ItemDetails,Comment} (recursive), views/user/User. comment pipe → utils/comment.ts#formatCommentCount.
  • All API HTML (comments, item text, poll options, user bio) goes through utils/sanitize.ts#sanitizeHtml (DOMPurify) before dangerouslySetInnerHTML, replacing the sanitization Angular's [innerHTML] did implicitly.

Styling — Angular emulated view-encapsulation kept each component's SCSS local; React styles are global, so each ported .scss is wrapped in a root class (.app-feed, .app-item, .app-comment, …) applied to the component root, preserving the original cascade. :host >>> x.app-<comp> x. @import@use … as *, / division → math.div. Theme class names (.wrapper, #header, .subtext, …) are unchanged so _themes.scss applies as before.

PWA@angular/service-worker/ngsw-config.json replaced by vite-plugin-pwa (autoUpdate, Workbox precache of js/css/html/icons via globPatterns, includeManifestIcons: false to avoid conflicting precache entries, navigateFallback: /index.html); manifest content copied into the plugin config (emits manifest.webmanifest), public/manifest.json + public/assets retained so existing links resolve.

Removedangular.json, karma.conf.js, tslint.json, ngsw-config.json, browserslist, e2e/, src/app, src/environments, polyfills/test shims, yarn.lock, all @angular/*, zone.js, rxjs, unfetch deps. Added ESLint (Vite react-ts preset); npm run build runs tsc --noEmit && vite build.

Behavioural notes

  • [class.head-margin]="item.text" referenced a field that never existed on Story (so it never applied); it now keys off item.content (the actual API field), which is the evident intent. content?: string added to Story.
  • .travis.yml bumped to Node 20 and no longer installs @angular/cli. firebase.json still deploys dist/.
  • README and CONTRIBUTING updated for npm run dev / build / preview.
  • Adds .agents/skills/angular-to-react-migration/SKILL.md documenting this migration recipe for reuse.

Known open item — Snyk flags two moderate advisories in react-router@6 (GHSA-wrjc-x8rr-h8h6 open redirect via <Link>, GHSA-337j-9hxr-rhxg SSR-only). The only fix is react-router-dom@7; kept v6 per the requested stack pending a decision.

Verified locally: npm run lint, npm run build (dist contains index.html, manifest.webmanifest, sw.js with 29 unique precache entries, icons), and /news/1, /jobs/1, /item/:id (nested comments) render without console errors.

Devin-Org: engineering

Link to Devin session: https://app.devin.ai/sessions/045c0d5f0c904882950ce7f810c8e332
Open in Devin Desktop: https://app.devin.ai/desktop/session/045c0d5f0c904882950ce7f810c8e332?variant=devin
Requested by: @charityquinn-cognition


Devin Review

Co-Authored-By: Charity Quinn <charity.quinn@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: Charity Quinn <charity.quinn@cognition.ai>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 2 commits September 9, 2026 19:44
…UTING for Vite

Co-Authored-By: Charity Quinn <charity.quinn@cognition.ai>
Co-Authored-By: Charity Quinn <charity.quinn@cognition.ai>
devin-ai-integration[bot]

This comment was marked as resolved.

…nstall

Co-Authored-By: Charity Quinn <charity.quinn@cognition.ai>
@devin-ai-integration

Copy link
Copy Markdown

Runtime verification — 6833920

Verified in the browser (recording in session): //news/1; all five feeds with active-nav highlighting, jobs paragraph, pagination (page 2 starts at 31, Prev/More, scroll-to-top); item detail with nested comments, collapse/expand, Ask HN internal links; poll /item/126809 renders 3 options with proportional bars; user API 404 → error state, browser Back and mobile back chevron work; Default/Night/AMOLED themes, font size, spacing and open-in-new-tab apply live and persist across reload; production build: manifest.webmanifest + manifest.json 200, /sw.js activates; no uncaught exceptions.

Poll and nested comments Persisted settings
Poll rendering Persisted settings

Not covered: a successful /user/:id profile (karma/created) — every live user tried returned 404 from node-hnapi.herokuapp.com.

Written by Devin

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