Skip to content

feat: npm packages index, per-package detail pages, and self-hosted webfonts - close #224 - #223

Merged
soroushm merged 10 commits into
mainfrom
feat/npm-packages
Jul 5, 2026
Merged

feat: npm packages index, per-package detail pages, and self-hosted webfonts - close #224#223
soroushm merged 10 commits into
mainfrom
feat/npm-packages

Conversation

@soroushm

@soroushm soroushm commented Jul 4, 2026

Copy link
Copy Markdown
Member

Summary

Adds a public /npm section showcasing the workspace's published npm packages, a detail page per package, and self-hosts the design-system webfonts.

Commits

  • feat: self-host Space Grotesk and JetBrains Mono webfonts — load the design-system fonts via @fontsource-variable (variable wght axis, latin subset) so they render for every visitor instead of only those with the fonts installed locally. Served from our own origin, within the CSP.
  • feat: add npm packages index and package detail pages/npm auto-discovers published (non-private) packages from their package.json via import.meta.glob, rendering each as a DomainCard (name, description, keyword tags, version badge). Adds the msw-server detail page (hero + rendered README), a reusable CommandSnippet (copy-to-clipboard), a DomainCard badge/children slot, copy/check icons, and header/footer links to /npm.
  • feat: add detail pages for bench, playwright-coverage, and styled-system — a detail page for every published package, hero sourced from each package.json. The index now links each card to its detail page (npm is the fallback for packages without a page). toEntry is extracted and unit-tested for both routing cases.

/npm listing

Package Card links to
@soroush.tech/bench /bench/
@soroush.tech/playwright-coverage /playwright-coverage/
@soroush.tech/styled-system /styled-system/
@soroush.tech/vite-plugin-msw-server /vite-plugin-msw-server/

Private packages (eslint-config, schema, vite-plugin-sitemap, vite-plugin-watch) are excluded automatically. Adding a package — or its detail page — updates the index with no code change.

Verification

  • pnpm lint
  • pnpm test:coverage — 1942 tests, 100% statements / functions / lines
  • pnpm build ✓ (15 sitemap URLs; all four package pages prerender)
  • e2e — all package + /npm index specs pass

Summary by CodeRabbit

  • New Features
    • Added new package documentation pages (/npm, /bench, /styled-system, /playwright-coverage, /vite-plugin-msw-server) with hero sections and rendered README content.
    • Added a copy-to-clipboard command snippet and enhanced package cards with optional custom badges and footer content.
  • Enhancements
    • Refreshed header and footer navigation labels and link destinations.
  • Documentation
    • Added/updated documentation for the new hero/README/snippet behavior.
  • Style
    • Updated UI typography to variable fonts and expanded icon support.
  • Tests
    • Added unit and end-to-end coverage for new pages and interactions.

soroushm added 3 commits July 4, 2026 22:51
Load the design-system fonts via @fontsource-variable (variable wght axis,
latin subset) from globalStyles, so they render for every visitor rather than
only those with the fonts installed. Prefer the "X Variable" family, keeping the
plain name and a generic fallback. Served from our own origin, within the CSP.
Add a /npm index that auto-discovers published (non-private) workspace packages
from their package.json via import.meta.glob, rendering each as a DomainCard
(name, description, keyword tags, version badge) linking to its detail page when
one exists, otherwise to npm. Add the vite-plugin-msw-server detail page (hero +
rendered README), a reusable CommandSnippet with copy-to-clipboard, a DomainCard
badge/children slot, copy/check icons, and header/footer links to /npm.
Give every published workspace package a detail page (hero + rendered README,
sourced from its package.json). The /npm index now links each card to its detail
page rather than npm. Extract and unit-test the toEntry helper for both the
detail-page and npm-fallback cases so the routing branch stays covered.
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds package directory pages and shared UI for package hero/readme/index rendering, updates navigation and theming for the new pages, and changes coverage/Codecov and package metadata plumbing for web test and publish workflows.

Changes

NPM Packages Directory

Layer / File(s) Summary
CommandSnippet component
apps/web/src/common/CommandSnippet/*
New command snippet with clipboard copy/reset behavior, tests, README, and barrel export.
PackageHero section
apps/web/src/section/PackageHero/*
New package hero section rendering name, tagline, install snippet, and external links.
PackageReadme section
apps/web/src/section/PackageReadme/*
New README rendering section with chrome stripping utility, tests, README, and barrel export.
PackageIndex section and data
apps/web/src/section/PackageIndex/*
New package index data model, auto-discovery, card rendering, tests, README, and barrel export.
Package detail and index page routes
apps/web/src/pages/{bench,npm,playwright-coverage,styled-system,vite-plugin-msw-server}/*
New page modules, page configs, head-data exports, package hero data, and e2e tests for the package pages.
DomainCard badge and children support
apps/web/src/common/DomainCard/*
DomainCard adds optional badge override and children footer content, with tests.
Header and Footer navigation updates
apps/web/src/common/{Header,Footer}/*
Navigation updates replace Experience with Packages and refresh footer destinations.
Font theming, icons, and build config
apps/web/src/theme/*, apps/web/{package.json,tsconfig.app.json,tsconfig.shared.json,vite.config.ts,vitest.config.ts,vite-env.d.ts}
Adds new icons, variable fonts, self-hosted font loading, and module-resolution/type support for package and raw README imports.
Coverage and release plumbing
.codecov.yml, .github/workflows/ci.*, apps/web/src/test/e2e/coverage.ts, apps/web/package.json, apps/web/scripts/coverage-check.ts, package.json, packages/*/package.json, packages/vite-plugin-msw-server/README.md, scripts/gen-publish-options.mjs, README.md
Updates web coverage gating/uploading, E2E coverage scope, package metadata, publish option generation, and package documentation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: an npm packages index, package detail pages, and self-hosted webfonts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/npm-packages

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Jul 4, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​fontsource-variable/​space-grotesk@​5.2.1086100818390
Added@​fontsource-variable/​jetbrains-mono@​5.2.8100100838390

View full report

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Bundle Report

Changes will increase total bundle size by 231.9kB (0.3%) ⬆️. This is within the configured threshold ✅

Detailed changes
Bundle name Size Change
@soroush.tech/dev-esm 38.37MB 243.99kB (0.64%) ⬆️
@soroush.tech/prod-esm* 38.11MB -12.09kB (-0.03%) ⬇️

ℹ️ *Bundle size includes cached data from a previous commit

Affected Assets, Files, and Routes:

view changes for bundle: @soroush.tech/dev-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
chunks/chunk-DFK571_t.js (New) 123.43kB 123.43kB 100.0% 🚀
assets/static/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2 (New) 40.4kB 40.4kB 100.0% 🚀
entries/src_pages_bench.mjs (New) 27.04kB 27.04kB 100.0% 🚀
assets/static/space-grotesk-latin-wght-normal.BhU9QXUp.woff2 (New) 22.29kB 22.29kB 100.0% 🚀
entries/src_pages_domain.mjs -3.32kB 20.84kB -13.73%
assets/static/space-grotesk-latin-ext-wght-normal.D9tNdqV9.woff2 (New) 18.94kB 18.94kB 100.0% 🚀
entries/src_pages_vite-plugin-msw-server.mjs (New) 16.7kB 16.7kB 100.0% 🚀
entries/src_pages_playwright-coverage.mjs (New) 15.59kB 15.59kB 100.0% 🚀
assets/static/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2 (New) 15.2kB 15.2kB 100.0% 🚀
entry.mjs 4.16kB 14.9kB 38.73% ⚠️
chunks/chunk-Ch1LaOhl.js (New) 14.71kB 14.71kB 100.0% 🚀
entries/src_pages_npm.mjs (New) 12.54kB 12.54kB 100.0% 🚀
assets/static/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2 (New) 12.11kB 12.11kB 100.0% 🚀
entries/src_pages_styled-system.mjs (New) 11.47kB 11.47kB 100.0% 🚀
assets/static/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2 (New) 9.0kB 9.0kB 100.0% 🚀
entries/src_pages_article_-id.mjs -2.8kB 7.72kB -26.63%
assets/static/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2 (New) 7.5kB 7.5kB 100.0% 🚀
chunks/chunk-C2HvQSGy.js (New) 7.39kB 7.39kB 100.0% 🚀
assets/static/space-grotesk-vietnamese-wght-normal.D0rl6rjA.woff2 (New) 6.71kB 6.71kB 100.0% 🚀
chunks/chunk-P9VsHwTH.js (New) 5.66kB 5.66kB 100.0% 🚀
assets/static/style-*.CK-TMAhB.css (New) 4.64kB 4.64kB 100.0% 🚀
chunks/chunk-CSwGLjj_.js (New) 4.1kB 4.1kB 100.0% 🚀
chunks/chunk-BojCXiLb.js (New) 3.68kB 3.68kB 100.0% 🚀
chunks/chunk-BI2ICEc1.js (New) 2.96kB 2.96kB 100.0% 🚀
chunks/chunk-BdHOu1n3.js (New) 2.55kB 2.55kB 100.0% 🚀
chunks/chunk-DbH2TN0n.js (New) 2.13kB 2.13kB 100.0% 🚀
chunks/chunk-DO691X9f.js (New) 2.02kB 2.02kB 100.0% 🚀
chunks/chunk-D5vW5r6B.js (New) 1.88kB 1.88kB 100.0% 🚀
chunks/chunk-D_ircHbL.js (New) 1.88kB 1.88kB 100.0% 🚀
chunks/chunk-DvZyc2rX.js (New) 1.74kB 1.74kB 100.0% 🚀
chunks/chunk-CtN_8xQs.js (New) 1.6kB 1.6kB 100.0% 🚀
chunks/chunk-DoF_atvC.js (New) 1.43kB 1.43kB 100.0% 🚀
chunks/chunk-BlSIA_CC.js (New) 1.19kB 1.19kB 100.0% 🚀
assets/static/style-*.BR45LvNo.css (New) 1.14kB 1.14kB 100.0% 🚀
chunks/chunk-BaUDxtIl.js (New) 11 bytes 11 bytes 100.0% 🚀
chunks/chunk-C-LiQ0LH.js (New) 11 bytes 11 bytes 100.0% 🚀
chunks/chunk-BZY8_Tpo.js (Deleted) -122.38kB 0 bytes -100.0% 🗑️
chunks/chunk-Bcg9B4mW.js (Deleted) -14.71kB 0 bytes -100.0% 🗑️
chunks/chunk-CvroyTJQ.js (Deleted) -4.1kB 0 bytes -100.0% 🗑️
chunks/chunk-wKIBFOU8.js (Deleted) -2.55kB 0 bytes -100.0% 🗑️
chunks/chunk-BXMXLukt.js (Deleted) -2.13kB 0 bytes -100.0% 🗑️
chunks/chunk-BRPTQ5sz.js (Deleted) -1.88kB 0 bytes -100.0% 🗑️
chunks/chunk-Y-tDli3Q.js (Deleted) -1.74kB 0 bytes -100.0% 🗑️
chunks/chunk-CRYh7c0Q.js (Deleted) -1.6kB 0 bytes -100.0% 🗑️
chunks/chunk-BW0lkAk2.js (Deleted) -1.43kB 0 bytes -100.0% 🗑️
chunks/chunk-DjPYFvdQ.js (Deleted) -1.19kB 0 bytes -100.0% 🗑️

Files in chunks/chunk-DFK571_t.js:

  • ./src/common/Footer/Footer.tsx → Total Size: 4.09kB

  • ./src/common/Header/Header.tsx → Total Size: 3.43kB

Files in entries/src_pages_bench.mjs:

  • ./src/pages/bench/+Page.tsx → Total Size: 318 bytes

  • ./src/pages/bench/+data.ts → Total Size: 197 bytes

  • ./src/pages/bench/bench.data.ts → Total Size: 346 bytes

Files in entries/src_pages_npm.mjs:

  • ./src/pages/npm/+data.ts → Total Size: 195 bytes

  • ./src/pages/npm/+Page.tsx → Total Size: 237 bytes

Files in chunks/chunk-P9VsHwTH.js:

  • ./src/common/CommandSnippet/index.ts → Total Size: 0 bytes

  • ./src/common/CommandSnippet/CommandSnippet.tsx → Total Size: 1.55kB

  • ./src/common/CommandSnippet/const.ts → Total Size: 183 bytes

Files in chunks/chunk-BojCXiLb.js:

  • ./src/common/DomainCard/DomainCard.tsx → Total Size: 3.4kB
view changes for bundle: @soroush.tech/prod-esm

Assets Changed:

Asset Name Size Change Total Size Change (%)
chunks/chunk-ChTCOu4n.js (New) 119.69kB 119.69kB 100.0% 🚀
entries/src_pages_about.mjs -2 bytes 39.28kB -0.01%
entries/src_pages_index.mjs -1 bytes 19.59kB -0.01%
chunks/chunk-Dsvd7k3d.js (New) 14.71kB 14.71kB 100.0% 🚀
entry.mjs -724 bytes 10.02kB -6.74%
entries/src_pages_articles.mjs -1 bytes 7.71kB -0.01%
chunks/chunk-BjZjYDoQ.js (New) 4.1kB 4.1kB 100.0% 🚀
chunks/chunk-BYa9bSv6.js (New) 2.55kB 2.55kB 100.0% 🚀
chunks/chunk-CZlYX0oM.js (New) 2.15kB 2.15kB 100.0% 🚀
chunks/chunk-C6MbF95j.js (New) 1.88kB 1.88kB 100.0% 🚀
chunks/chunk-Ylz6ONWR.js (New) 1.74kB 1.74kB 100.0% 🚀
chunks/chunk-GbFgCIxn.js (New) 1.62kB 1.62kB 100.0% 🚀
chunks/chunk-BEs1Uawn.js (New) 1.43kB 1.43kB 100.0% 🚀
chunks/chunk-SLYXZi_K.js (New) 1.19kB 1.19kB 100.0% 🚀
chunks/chunk-Co7oTrUT.js (Deleted) -122.5kB 0 bytes -100.0% 🗑️
chunks/chunk-m_fPxBgp.js (Deleted) -14.71kB 0 bytes -100.0% 🗑️
entries/src_pages_error.mjs (Deleted) -8.55kB 0 bytes -100.0% 🗑️
chunks/chunk-CN5ggE8z.js (Deleted) -4.1kB 0 bytes -100.0% 🗑️
chunks/chunk-Dayq6ZOV.js (Deleted) -2.55kB 0 bytes -100.0% 🗑️
chunks/chunk-D99maFA_.js (Deleted) -2.15kB 0 bytes -100.0% 🗑️
chunks/chunk-AhJt6oTq.js (Deleted) -1.88kB 0 bytes -100.0% 🗑️
chunks/chunk-C_clvvZt.js (Deleted) -1.74kB 0 bytes -100.0% 🗑️
chunks/chunk-CdL89D4l.js (Deleted) -1.62kB 0 bytes -100.0% 🗑️
chunks/chunk-r1dcPfwu.js (Deleted) -1.43kB 0 bytes -100.0% 🗑️
chunks/chunk-8-VOm9Xs.js (Deleted) -1.19kB 0 bytes -100.0% 🗑️

Files in chunks/chunk-ChTCOu4n.js:

  • ./src/common/Header/Header.tsx → Total Size: 3.44kB

  • ./src/common/Footer/Footer.tsx → Total Size: 4.01kB

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/common/Footer/Footer.test.tsx (1)

63-101: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Missing test for the new "NPM Packages" footer link.

CONNECTIVITY in Footer.tsx now includes a new { href: '/npm/', label: 'NPM Packages' } entry, but no corresponding assertion was added here alongside the Experience Graph, GitHub Repository, and LinkedIn tests. As per coding guidelines, pnpm test:coverage must verify 100% coverage on all touched files.

✅ Suggested addition
     it('renders LinkedIn link', () => {
       renderWithTheme(<Footer />)
       expect(screen.getByRole('link', { name: 'LinkedIn' })).toHaveAttribute(
         'href',
         'https://www.linkedin.com/in/masoud-soroush-4139b152'
       )
+    })
+
+    it('renders NPM Packages link', () => {
+      renderWithTheme(<Footer />)
+      expect(screen.getByRole('link', { name: 'NPM Packages' })).toHaveAttribute('href', '/npm/')
     })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/common/Footer/Footer.test.tsx` around lines 63 - 101, The Footer
test suite is missing coverage for the new NPM Packages link added in
Footer.tsx. Update Footer.test.tsx by adding an assertion in the existing
connectivity describe block using renderWithTheme and getByRole for the “NPM
Packages” link, verifying its href matches the CONNECTIVITY entry (/npm/). Keep
the test alongside the existing Experience Graph, GitHub Repository, and
LinkedIn link assertions.

Source: Coding guidelines

🧹 Nitpick comments (8)
apps/web/src/common/Footer/Footer.tsx (1)

28-28: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out dead code.

Leftover commented-out link entry should be deleted rather than kept in the source.

🧹 Suggested cleanup
-
-  // { href: '/wiki/', label: 'Technical Wiki' }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/common/Footer/Footer.tsx` at line 28, The Footer link list
contains commented-out dead code that should be removed. Delete the leftover
commented Technical Wiki entry from the Footer component’s link configuration,
and keep the footer items in the relevant data structure clean and uncommented.
apps/web/src/common/CommandSnippet/CommandSnippet.tsx (1)

20-27: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Unmounted-state update via stale setTimeout.

If the component unmounts before COPIED_RESET_MS elapses, the pending setTimeout still calls setCopied on an unmounted component. React no longer warns/errors for this, but it's wasted work and a latent leak pattern if this logic grows. Consider clearing the timeout on unmount via a ref + useEffect cleanup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/common/CommandSnippet/CommandSnippet.tsx` around lines 20 - 27,
The CommandSnippet handleCopy flow leaves a pending setTimeout that can call
setCopied after unmount, so add cleanup around the timeout in
CommandSnippet.tsx. Update the CommandSnippet component to store the timeout id
in a ref, clear any existing timeout before scheduling a new one, and add a
useEffect cleanup to cancel it on unmount so the copied-reset logic cannot run
against an unmounted component.
apps/web/src/section/PackageHero/PackageHero.tsx (1)

22-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stale "promote once a second page consumes it" note.

Per the PR stack, PackageHero is consumed by five package pages (bench, npm, playwright-coverage, styled-system, vite-plugin-msw-server) added later in this same stack. This comment will be inaccurate as soon as those pages land — worth updating or removing now to avoid drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/section/PackageHero/PackageHero.tsx` around lines 22 - 26, The
JSDoc note in PackageHero is now stale because PackageHero is already used by
multiple package pages in this stack. Update or remove the “promote once a
second package page consumes it” wording in the PackageHero component comment so
it reflects the current shared usage across the package pages, and keep the
description aligned with the actual role of PackageHero.
apps/web/vitest.config.ts (1)

45-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider deduplicating the alias array.

The packages alias entry (and the pre-existing msw/src entries) is now duplicated identically between the top-level resolve.alias and the unit project's resolve.alias. Extracting a shared constant would prevent future drift between the two lists.

♻️ Example refactor
+const testAliases = [
+  { find: /^msw$/, replacement: resolve(__dirname, 'node_modules/msw') },
+  { find: 'src', replacement: resolve(__dirname, './src') },
+  { find: 'packages', replacement: resolve(__dirname, '../../packages') },
+]
+
 export default defineConfig({
   resolve: {
-    alias: [
-      { find: /^msw$/, replacement: resolve(__dirname, 'node_modules/msw') },
-      { find: 'src', replacement: resolve(__dirname, './src') },
-      { find: 'packages', replacement: resolve(__dirname, '../../packages') },
-    ],
+    alias: testAliases,
   },
   ...
       {
         resolve: {
-          alias: [
-            { find: /^msw$/, replacement: resolve(__dirname, 'node_modules/msw') },
-            { find: 'src', replacement: resolve(__dirname, './src') },
-            { find: 'packages', replacement: resolve(__dirname, '../../packages') },
-          ],
+          alias: testAliases,
         },

Also applies to: 79-83

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/vitest.config.ts` around lines 45 - 49, The alias list in
vitest.config.ts is duplicated between the top-level resolve.alias and the unit
project’s resolve.alias, which risks drift. Extract the shared alias entries
into a single constant and reuse it in both places, keeping the existing msw,
src, and packages mappings centralized and consistent.
apps/web/src/section/PackageIndex/PackageIndex.data.ts (2)

18-25: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

keywords typed as required but not guaranteed by real package.json files.

PackageJson.keywords is declared as required string[], but nothing validates that every workspace package.json actually has this field. If one is missing, toEntry passes undefined through, and any downstream .map over keywords (e.g., rendering tags) will throw at runtime.

🛡️ Proposed defensive default
 export const toEntry = (pkg: PackageJson, hasPage: boolean): PackageEntry => {
   const slug = pkg.name.split('/')[1]
   return {
     name: pkg.name,
     description: pkg.description,
     version: pkg.version,
-    keywords: pkg.keywords,
+    keywords: pkg.keywords ?? [],
     href: hasPage ? `/${slug}/` : `https://www.npmjs.com/package/${pkg.name}`,
     target: hasPage ? undefined : '_blank',
   }
 }

Also applies to: 31-41

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/section/PackageIndex/PackageIndex.data.ts` around lines 18 - 25,
The PackageJson shape used by PackageIndex.data.ts assumes keywords is always
present, but real package.json entries may omit it and cause downstream failures
in toEntry and any keyword rendering. Update the PackageJson interface and the
toEntry logic to treat keywords as optional and default it to an empty array
when absent, using the existing PackageJson and toEntry symbols to keep the
index card data safe.

43-53: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Fragile positional path parsing for package/page discovery.

The ../../../../../ depth (line 46) and path.split('/')[3] (line 52) are magic-index assumptions tied to this file's exact location and route depth. Either would silently break discovery (not a build error) if the file moves or src/pages nesting changes.

Consider deriving the slug more defensively, e.g. via a regex capture instead of a fixed split index:

const pagedSlugs = new Set(
  Object.keys(import.meta.glob('/src/pages/*/+Page.tsx'))
    .map((path) => path.match(/^\/src\/pages\/([^/]+)\/\+Page\.tsx$/)?.[1])
    .filter((slug): slug is string => Boolean(slug))
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/section/PackageIndex/PackageIndex.data.ts` around lines 43 - 53,
The package/page discovery in PackageIndex.data.ts relies on fragile positional
path parsing, so replace the hardcoded package glob depth and the
path.split-based slug extraction with a more defensive approach. Update the
packageJsons/import.meta.glob usage and the pagedSlugs derivation in
PackageIndex.data.ts to infer the package slug via pattern matching or another
location-independent method, using the existing packageJsons and pagedSlugs
symbols to keep discovery stable if the file moves or page nesting changes.
apps/web/src/pages/bench/+data.ts (1)

1-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated data() boilerplate across all package pages.

This exact data(pageContext) => ({ meta: pageSocialMeta(pageContext) }) implementation is repeated identically in styled-system/+data.ts and (per the PR stack) in npm, playwright-coverage, and vite-plugin-msw-server as well. Consider extracting a single shared helper (e.g. src/renderer/head) that each page's +data.ts simply re-exports, so the logic has one source of truth.

♻️ Example consolidation
// src/renderer/head.ts (or similar shared module)
export function pageData(pageContext: PageContext): HeadMeta {
  return { meta: pageSocialMeta(pageContext) }
}
-import type { PageContext } from 'vike/types'
-import { pageSocialMeta, type HeadMeta } from 'src/renderer/head'
-
-export function data(pageContext: PageContext): HeadMeta {
-  return { meta: pageSocialMeta(pageContext) }
-}
+export { pageData as data } from 'src/renderer/head'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/pages/bench/`+data.ts around lines 1 - 6, The page-level data()
implementation is duplicated across multiple package pages, so consolidate this
boilerplate into a shared helper in the head/renderer area and have each
+data.ts re-export or delegate to it. Update the bench page’s data(pageContext)
and the matching styled-system, npm, playwright-coverage, and
vite-plugin-msw-server pages to use the shared function so
pageSocialMeta(pageContext) and the HeadMeta return shape live in one source of
truth.
apps/web/src/pages/bench/bench.data.ts (1)

1-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting a shared repoUrl builder.

This hardcoded GitHub URL template (.../tree/main/packages/<name>) will likely repeat across all five per-package *.data.ts files. Centralizing it avoids five-way drift if the org/branch ever changes.

♻️ Proposed helper
+// e.g. src/section/PackageHero/utils.ts
+export function repoUrlFor(dir: string) {
+  return `https://github.com/soroush-tech/soroush.tech/tree/main/packages/${dir}`
+}
-  repoUrl: 'https://github.com/soroush-tech/soroush.tech/tree/main/packages/bench',
+  repoUrl: repoUrlFor('bench'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/pages/bench/bench.data.ts` around lines 1 - 11, The `hero`
object in `bench.data.ts` hardcodes a package-specific GitHub repo URL template
that will be duplicated across the per-package data files, so extract a shared
`repoUrl` builder and use it here instead of repeating the string. Update the
`hero` definition to call that shared helper for the `repoUrl` field, and
reference the existing `hero` export and any shared utility you introduce so the
package pages all stay in sync when the org, branch, or base path changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/web/src/common/CommandSnippet/CommandSnippet.tsx`:
- Around line 22-27: The CommandSnippet copy handler does not guard against
missing clipboard support or write failures. Update handleCopy in
CommandSnippet.tsx to first check navigator.clipboard/writeText availability,
then handle writeText rejections in the promise chain so the click handler does
not throw or leave an unhandled rejection. If the copy fails, keep the copied
state false and add appropriate fallback/user feedback handling around the
command copy action.

In `@apps/web/src/section/PackageReadme/README.md`:
- Line 14: Fix the markdownlint MD038 issue in the stripReadmeChrome
documentation text by removing the trailing space inside the inline code span
that shows the heading marker. Update the sentence referencing stripReadmeChrome
so the backticked `#` marker has no extra space, keeping the wording otherwise
the same.

---

Outside diff comments:
In `@apps/web/src/common/Footer/Footer.test.tsx`:
- Around line 63-101: The Footer test suite is missing coverage for the new NPM
Packages link added in Footer.tsx. Update Footer.test.tsx by adding an assertion
in the existing connectivity describe block using renderWithTheme and getByRole
for the “NPM Packages” link, verifying its href matches the CONNECTIVITY entry
(/npm/). Keep the test alongside the existing Experience Graph, GitHub
Repository, and LinkedIn link assertions.

---

Nitpick comments:
In `@apps/web/src/common/CommandSnippet/CommandSnippet.tsx`:
- Around line 20-27: The CommandSnippet handleCopy flow leaves a pending
setTimeout that can call setCopied after unmount, so add cleanup around the
timeout in CommandSnippet.tsx. Update the CommandSnippet component to store the
timeout id in a ref, clear any existing timeout before scheduling a new one, and
add a useEffect cleanup to cancel it on unmount so the copied-reset logic cannot
run against an unmounted component.

In `@apps/web/src/common/Footer/Footer.tsx`:
- Line 28: The Footer link list contains commented-out dead code that should be
removed. Delete the leftover commented Technical Wiki entry from the Footer
component’s link configuration, and keep the footer items in the relevant data
structure clean and uncommented.

In `@apps/web/src/pages/bench/`+data.ts:
- Around line 1-6: The page-level data() implementation is duplicated across
multiple package pages, so consolidate this boilerplate into a shared helper in
the head/renderer area and have each +data.ts re-export or delegate to it.
Update the bench page’s data(pageContext) and the matching styled-system, npm,
playwright-coverage, and vite-plugin-msw-server pages to use the shared function
so pageSocialMeta(pageContext) and the HeadMeta return shape live in one source
of truth.

In `@apps/web/src/pages/bench/bench.data.ts`:
- Around line 1-11: The `hero` object in `bench.data.ts` hardcodes a
package-specific GitHub repo URL template that will be duplicated across the
per-package data files, so extract a shared `repoUrl` builder and use it here
instead of repeating the string. Update the `hero` definition to call that
shared helper for the `repoUrl` field, and reference the existing `hero` export
and any shared utility you introduce so the package pages all stay in sync when
the org, branch, or base path changes.

In `@apps/web/src/section/PackageHero/PackageHero.tsx`:
- Around line 22-26: The JSDoc note in PackageHero is now stale because
PackageHero is already used by multiple package pages in this stack. Update or
remove the “promote once a second package page consumes it” wording in the
PackageHero component comment so it reflects the current shared usage across the
package pages, and keep the description aligned with the actual role of
PackageHero.

In `@apps/web/src/section/PackageIndex/PackageIndex.data.ts`:
- Around line 18-25: The PackageJson shape used by PackageIndex.data.ts assumes
keywords is always present, but real package.json entries may omit it and cause
downstream failures in toEntry and any keyword rendering. Update the PackageJson
interface and the toEntry logic to treat keywords as optional and default it to
an empty array when absent, using the existing PackageJson and toEntry symbols
to keep the index card data safe.
- Around line 43-53: The package/page discovery in PackageIndex.data.ts relies
on fragile positional path parsing, so replace the hardcoded package glob depth
and the path.split-based slug extraction with a more defensive approach. Update
the packageJsons/import.meta.glob usage and the pagedSlugs derivation in
PackageIndex.data.ts to infer the package slug via pattern matching or another
location-independent method, using the existing packageJsons and pagedSlugs
symbols to keep discovery stable if the file moves or page nesting changes.

In `@apps/web/vitest.config.ts`:
- Around line 45-49: The alias list in vitest.config.ts is duplicated between
the top-level resolve.alias and the unit project’s resolve.alias, which risks
drift. Extract the shared alias entries into a single constant and reuse it in
both places, keeping the existing msw, src, and packages mappings centralized
and consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9ced6822-79e3-435c-a366-636e84874e88

📥 Commits

Reviewing files that changed from the base of the PR and between 978a501 and 9010577.

⛔ Files ignored due to path filters (3)
  • apps/web/src/assets/icons/check.svg is excluded by !**/*.svg
  • apps/web/src/assets/icons/content_copy.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml, !pnpm-lock.yaml
📒 Files selected for processing (71)
  • apps/web/package.json
  • apps/web/src/common/CommandSnippet/CommandSnippet.test.tsx
  • apps/web/src/common/CommandSnippet/CommandSnippet.tsx
  • apps/web/src/common/CommandSnippet/README.md
  • apps/web/src/common/CommandSnippet/const.ts
  • apps/web/src/common/CommandSnippet/index.ts
  • apps/web/src/common/DomainCard/DomainCard.test.tsx
  • apps/web/src/common/DomainCard/DomainCard.tsx
  • apps/web/src/common/Footer/Footer.test.tsx
  • apps/web/src/common/Footer/Footer.tsx
  • apps/web/src/common/Header/Header.test.tsx
  • apps/web/src/common/Header/Header.tsx
  • apps/web/src/pages/bench/+Page.tsx
  • apps/web/src/pages/bench/+config.ts
  • apps/web/src/pages/bench/+data.test.ts
  • apps/web/src/pages/bench/+data.ts
  • apps/web/src/pages/bench/bench.data.test.ts
  • apps/web/src/pages/bench/bench.data.ts
  • apps/web/src/pages/bench/bench.e2e.ts
  • apps/web/src/pages/npm/+Page.tsx
  • apps/web/src/pages/npm/+config.ts
  • apps/web/src/pages/npm/+data.test.ts
  • apps/web/src/pages/npm/+data.ts
  • apps/web/src/pages/npm/npm.e2e.ts
  • apps/web/src/pages/playwright-coverage/+Page.tsx
  • apps/web/src/pages/playwright-coverage/+config.ts
  • apps/web/src/pages/playwright-coverage/+data.test.ts
  • apps/web/src/pages/playwright-coverage/+data.ts
  • apps/web/src/pages/playwright-coverage/playwright-coverage.data.test.ts
  • apps/web/src/pages/playwright-coverage/playwright-coverage.data.ts
  • apps/web/src/pages/playwright-coverage/playwright-coverage.e2e.ts
  • apps/web/src/pages/styled-system/+Page.tsx
  • apps/web/src/pages/styled-system/+config.ts
  • apps/web/src/pages/styled-system/+data.test.ts
  • apps/web/src/pages/styled-system/+data.ts
  • apps/web/src/pages/styled-system/styled-system.data.test.ts
  • apps/web/src/pages/styled-system/styled-system.data.ts
  • apps/web/src/pages/styled-system/styled-system.e2e.ts
  • apps/web/src/pages/vite-plugin-msw-server/+Page.tsx
  • apps/web/src/pages/vite-plugin-msw-server/+config.ts
  • apps/web/src/pages/vite-plugin-msw-server/+data.test.ts
  • apps/web/src/pages/vite-plugin-msw-server/+data.ts
  • apps/web/src/pages/vite-plugin-msw-server/vite-plugin-msw-server.data.test.ts
  • apps/web/src/pages/vite-plugin-msw-server/vite-plugin-msw-server.data.ts
  • apps/web/src/pages/vite-plugin-msw-server/vite-plugin-msw-server.e2e.ts
  • apps/web/src/section/PackageHero/PackageHero.test.tsx
  • apps/web/src/section/PackageHero/PackageHero.tsx
  • apps/web/src/section/PackageHero/README.md
  • apps/web/src/section/PackageHero/index.ts
  • apps/web/src/section/PackageIndex/PackageIndex.data.test.ts
  • apps/web/src/section/PackageIndex/PackageIndex.data.ts
  • apps/web/src/section/PackageIndex/PackageIndex.test.tsx
  • apps/web/src/section/PackageIndex/PackageIndex.tsx
  • apps/web/src/section/PackageIndex/README.md
  • apps/web/src/section/PackageIndex/index.ts
  • apps/web/src/section/PackageReadme/PackageReadme.test.tsx
  • apps/web/src/section/PackageReadme/PackageReadme.tsx
  • apps/web/src/section/PackageReadme/README.md
  • apps/web/src/section/PackageReadme/index.ts
  • apps/web/src/section/PackageReadme/utils.test.ts
  • apps/web/src/section/PackageReadme/utils.ts
  • apps/web/src/theme/Icon/icons.ts
  • apps/web/src/theme/Typography/Typography.test.tsx
  • apps/web/src/theme/globalStyles.ts
  • apps/web/src/theme/themes.ts
  • apps/web/src/vite-env.d.ts
  • apps/web/tsconfig.app.json
  • apps/web/tsconfig.shared.json
  • apps/web/vite.config.ts
  • apps/web/vitest.config.ts
  • packages/vite-plugin-msw-server/README.md

Comment thread apps/web/src/common/CommandSnippet/CommandSnippet.tsx
Comment thread apps/web/src/section/PackageReadme/README.md Outdated
@soroushm soroushm changed the title feat: npm packages index, per-package detail pages, and self-hosted webfonts feat: npm packages index, per-package detail pages, and self-hosted webfonts - close #224 Jul 4, 2026
Address PR review and Codecov patch coverage:

- CommandSnippet: guard navigator.clipboard?.writeText (insecure/unsupported
  contexts) and catch write rejections so the handler never throws or leaves an
  unhandled rejection, with unit tests for both paths.
- PackageReadme README: drop the trailing space inside a code span (MD038).
- e2e coverage: include the package-page components (PackageHero, PackageReadme,
  PackageIndex, CommandSnippet). They have no Storybook/browser tests, so those
  coverage flags reported them uncovered; the real-browser e2e render now covers
  them via the package pages.
soroushm added 3 commits July 5, 2026 02:10
Reframe the package around its headline job — deterministic SSR in end-to-end
tests — across every surface where it's described:

- README: e2e-first hero callout, a real framework support matrix (dev SSR vs.
  build SSG per framework), a "Testing with Playwright" snippet, FAQ entries for
  Nuxt/SvelteKit/Remix, and links to the runnable examples repo.
- package.json: bump to 1.0.0 and rewrite the npm description to lead with e2e.
- web: update the package page meta description and hero tagline to match.

Also point the published packages' homepage fields at their soroush.tech pages
instead of the GitHub README (patch-bumping bench, playwright-coverage, and
styled-system, which change only the homepage).
…sses

Splitting web coverage into unit/browser/storybook Codecov flags failed patch
coverage for simple JSX components: each flag runs `all: true` over the whole
tree, so a tier that never executes a file reports its lines at 0, and Codecov's
cross-flag union leaves those lines red even though another tier covers them.

Add a merged `web` upload — one V8 pass over unit + browser + storybook (as
`test:coverage` does locally) — that reports each file once with real execution
data. Scope the patch status to `web`/`e2e`/`api` + the package flags so the
per-tier flags stay uploaded for visibility but no longer gate. `project` is left
at its default. The package entries in the patch-flags list are generated by
`gen:publish-options`, alongside the existing flag and component blocks.
@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
packages/vite-plugin-msw-server/README.md (1)

10-15: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fix heading-level skip flagged by markdownlint.

Line 10 jumps from the # @soroush.tech/... h1 straight to an h3 blockquote heading (### 🎭 Made for end-to-end tests), skipping h2.

📝 Suggested fix
-> ### 🎭 Made for end-to-end tests
+> ## 🎭 Made for end-to-end tests
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vite-plugin-msw-server/README.md` around lines 10 - 15, The README
heading hierarchy skips from the top-level title to an h3, which triggers the
markdownlint heading-level rule. Update the introductory section in README so
the “Made for end-to-end tests” heading uses the next proper level (an h2) and
keeps the surrounding Playwright/Cypress copy unchanged. Use the existing
heading text in the README block near the intro to locate it.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/vite-plugin-msw-server/README.md`:
- Around line 10-15: The README heading hierarchy skips from the top-level title
to an h3, which triggers the markdownlint heading-level rule. Update the
introductory section in README so the “Made for end-to-end tests” heading uses
the next proper level (an h2) and keeps the surrounding Playwright/Cypress copy
unchanged. Use the existing heading text in the README block near the intro to
locate it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: decf16bb-5334-44ce-8da9-a0c67d14b900

📥 Commits

Reviewing files that changed from the base of the PR and between f53cf19 and 8c827c7.

📒 Files selected for processing (20)
  • .codecov.yml
  • .github/workflows/ci.md
  • .github/workflows/ci.yml
  • README.md
  • apps/web/package.json
  • apps/web/scripts/coverage-check.ts
  • apps/web/src/pages/bench/bench.e2e.ts
  • apps/web/src/pages/playwright-coverage/playwright-coverage.e2e.ts
  • apps/web/src/pages/styled-system/styled-system.e2e.ts
  • apps/web/src/pages/vite-plugin-msw-server/+config.ts
  • apps/web/src/pages/vite-plugin-msw-server/vite-plugin-msw-server.data.ts
  • apps/web/src/pages/vite-plugin-msw-server/vite-plugin-msw-server.e2e.ts
  • apps/web/src/test/e2e/coverage.ts
  • package.json
  • packages/bench/package.json
  • packages/playwright-coverage/package.json
  • packages/styled-system/package.json
  • packages/vite-plugin-msw-server/README.md
  • packages/vite-plugin-msw-server/package.json
  • scripts/gen-publish-options.mjs
✅ Files skipped from review due to trivial changes (7)
  • apps/web/src/pages/vite-plugin-msw-server/vite-plugin-msw-server.data.ts
  • apps/web/scripts/coverage-check.ts
  • packages/bench/package.json
  • packages/styled-system/package.json
  • apps/web/src/pages/vite-plugin-msw-server/+config.ts
  • packages/vite-plugin-msw-server/package.json
  • packages/playwright-coverage/package.json
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/web/src/pages/vite-plugin-msw-server/vite-plugin-msw-server.e2e.ts
  • apps/web/src/pages/playwright-coverage/playwright-coverage.e2e.ts
  • apps/web/src/pages/styled-system/styled-system.e2e.ts
  • apps/web/src/pages/bench/bench.e2e.ts
  • apps/web/src/test/e2e/coverage.ts

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