Skip to content

feature: scaffold React + TypeScript app alongside Angular (migration PR 1/N) - #707

Open
charityquinn-cognition wants to merge 2 commits into
masterfrom
devin/1787927999-react-scaffold
Open

feature: scaffold React + TypeScript app alongside Angular (migration PR 1/N)#707
charityquinn-cognition wants to merge 2 commits into
masterfrom
devin/1787927999-react-scaffold

Conversation

@charityquinn-cognition

@charityquinn-cognition charityquinn-cognition commented Aug 28, 2026

Copy link
Copy Markdown

Summary

First PR of the Angular → React migration: adds a Vite React + TypeScript app under react-app/ that builds and renders a placeholder page. No Angular code is touched or removed; feature components come in later stacked PRs.

The React app is a separate npm project (react-app/package.json, its own node_modules) rather than sharing the root package.json. Reason: Vite 8 / React 19 need TypeScript ~6, while the Angular app pins typescript ~3.7.5 — a single manifest can't satisfy both, and merging them would break npm run build/ng serve for the Angular app that still has to work during the migration. The final PR of the series deletes the Angular sources and moves react-app/ to the repo root.

What's in it:

  • react-app/ Vite + React 19 + TS scaffold, npm run dev|build|lint|format.
  • ESLint flat config (@eslint/js, typescript-eslint, react-hooks, react-refresh, eslint-config-prettier) and Prettier configured with the Angular app's existing settings (trailingComma: es5, tabWidth: 4, singleQuote: true, printWidth: 120).
  • Global styles ported from src/styles.scss + src/app/shared/scss/{_media,_theme_variables,_themes}.scss into react-app/src/styles/, imported from main.tsx. Deprecated Sass APIs were modernized so the build is warning-free:
-@import "./media";                                   // themes partial
-$skull-size / 8
-darken($theme-amoledblack-text-color, 33%)
+@use './media' as *;
+math.div($skull-size, 8)
+color.adjust($theme-amoledblack-text-color, $lightness: -33%)
  • index.html carries over the PWA/social meta, skip link and .app-loader markup; the Angular app-root:empty loader selector becomes #root:empty.
  • Static assets copied to react-app/public/: assets/icons/*, assets/images/*, favicon.ico, manifest.json.
  • Placeholder App renders inside the default theme wrapper so the ported theme SCSS is exercised.

npm run lint, npm run format:check and npm run build all pass in react-app/; dev server renders:

React scaffold placeholder

Devin-Org: engineering

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


Devin Review

Status Commit
⚪ Not started

Run Devin Review

Devin Review (Staging)
Devin Review

Adds a Vite React+TS app in react-app/ with ESLint, Prettier (matching the
Angular app's prettier settings), SCSS support, the ported global stylesheet
and theme partials, static assets, and a placeholder App component.

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

@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 3 potential issues.

Devin Review

Comment thread react-app/index.html Outdated
<meta name="twitter:title" content="Angular 2 HN" />
<meta name="twitter:description" content="A Hacker News client built with React, TypeScript and Vite" />
<meta name="twitter:creator" content="@hdjirdeh" />
<meta name="twitter:image" content="assets/images/logo-loading.png" />

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: Carried-over broken social-image reference

The twitter:image and og:image tags point at assets/images/logo-loading.png, which does not exist under public/assets/images (only cog.svg, logo-header.png, logo.svg). This is copied verbatim from the original Angular index.html, so the broken reference is pre-existing, not introduced here.

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.

Correct, logo-loading.png is missing on master too — carried over verbatim on purpose. Picking a replacement social image is a content decision, so I'd rather not change it silently here; flagging it for the PWA/metadata PR at the end of the migration series.

<browserconfig>
<msapplication>
<tile>
<square150x150logo src="/mstile-150x150.png"/>

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: Pre-existing wrong tile-image path

square150x150logo points at /mstile-150x150.png, but the file lives at /assets/icons/mstile-150x150.png, so the tile image will not load. This is copied verbatim from the original Angular asset, so the issue is pre-existing.

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.

Agreed, and pre-existing — the file is a byte-for-byte copy of the Angular asset. Leaving it as-is so this PR stays a pure port; the tile path (and the browserconfig.xml reference itself) is better fixed in the PWA PR at the end of the series.

Comment on lines +236 to +249
@include theme(
amoledblack,
$theme-amoledblack-body-background-color,
$theme-amoledblack-body-background-color,
$theme-amoledblack-body-background-color,
$theme-amoledblack-text-color,
$theme-amoledblack-text-color,
color.adjust($theme-amoledblack-text-color, $lightness: -33%),
$theme-amoledblack-body-background-color,
$theme-amoledblack-subtext-color,
$theme-amoledblack-secondary-color,
$theme-amoledblack-body-background-color,
$theme-amoledblack-secondary-color
);

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: SCSS port matches the Angular original

The ported theme mixin, all three @include argument lists (12 args each), math.div($skull-size, 8), and color.adjust(..., $lightness: -33%) (replacing darken(..., 33%)) all match the original faithfully, with @import swapped for correctly-namespaced @use. No behavior change.

Devin Review

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

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

Copy link
Copy Markdown

Runtime verification — React scaffold

Tested end-to-end locally: Vite dev server (:5173), production build + preview (:4173), lint/format, and an Angular regression pass.

Verified working

React dev server renders the ported default theme — #header computes to rgb(185, 43, 39) (#b92b27), logo loads, zero console errors, zero failed resource loads.

React scaffold on Vite dev server

  • npm run build exits 0 with no sass deprecation warnings; built CSS contains the theme rules (19× b92b27, .default .wrapper{...#f5f5f5} + mobile #fff override) and the #root:empty+.app-loader selector (no stale app-root:empty).
  • npm run preview renders identically; /manifest.json, /favicon.ico, icon PNGs and logo.svg all return 200.
  • npm run lint and npm run format:check both pass.
768px mobile breakpoint + #root:empty loader

At 500px width .wrapper background flips #f5f5f5#fff per the ported _media.scss mobile-only query. Clearing #root makes the loader visible (opacity: 1, z-index: 100), confirming the app-root:empty#root:empty rename works.

app-loader shown when #root is empty

Issue found and fixed: relative asset paths broke on nested routes

On /story/123 (SPA fallback) assets/images/logo.svg resolved to /story/assets/images/logo.svg and the logo rendered broken — the fallback returns index.html with HTTP 200, so it did not surface as a 404. Fixed in 33c9f25 by switching index.html and App.tsx to root-absolute /assets/... (matching manifest.json/favicon.ico).

Broken logo on nested route /story/123, before the fix

Regression: Angular app unaffected

ng serve on :4200 still renders the HN story list, and root npm run build exits 0. Note the root app needs NODE_OPTIONS=--openssl-legacy-provider on Node 17+ (pre-existing, unrelated to this PR).

Regression: Angular app on :4200

Recording of the run is attached in the Devin session: https://app.devin.ai/sessions/8edab78f01c5451f9d61c114b94b7484

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