Repository navigation
chore(web): apply weekly SEO and AEO research - #8392
lawrencecchen wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe change adds validated Open Graph image routes for localized and default-locale URLs, bypasses middleware processing for the image endpoint, references localized images in Article JSON-LD, bundles rendering fonts, and adds route and rendering tests. ChangesLocalized Open Graph image delivery
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant Middleware
participant OpenGraphRoute
participant openGraphImageResponse
participant renderOpenGraphImage
Client->>Middleware: Request localized Open Graph image
Middleware->>OpenGraphRoute: Continue image request
OpenGraphRoute->>openGraphImageResponse: Pass locale
openGraphImageResponse->>openGraphImageResponse: Validate locale
openGraphImageResponse->>renderOpenGraphImage: Render valid locale
renderOpenGraphImage-->>Client: Return image response
🚥 Pre-merge checks | ✅ 24 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (24 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Greptile SummaryThis PR fixes locale-aware metadata for the
Confidence Score: 5/5Scoped metadata-only change with no impact on page rendering, routing, or data; all 20 locales validated by the new test suite. The change touches only the server-side generateMetadata path — visible page copy, canonicals, hreflang alternates, and the sitemap are untouched. The new wallOfLoveSeoCopy helper follows the established pattern in audited-seo.ts and is backed by a comprehensive per-locale test that enforces content correctness and length bounds. No files require special attention. Important Files Changed
Reviews (8): Last reviewed commit: "chore(web): apply weekly SEO and AEO res..." | Re-trigger Greptile |
| export const size = { width: 1200, height: 630 }; | ||
| export const contentType = "image/png"; |
There was a problem hiding this comment.
The
size and contentType exports are left over from when this file was a Next.js file-convention metadata image (opengraph-image.tsx). Now that it is a plain utility module, neither export is consumed by any route handler or caller — they are dead code and will silently drift from the actual dimensions used by renderOpenGraphImage (width: 1200 * S, height: 630 * S).
| export const size = { width: 1200, height: 630 }; | |
| export const contentType = "image/png"; | |
| // Retained for any callers that need the canonical dimensions without | |
| // importing from seo.ts. The renderer uses width * S and height * S internally. | |
| export const size = { width: 1200, height: 630 }; | |
| export const contentType = "image/png"; |
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| type OpenGraphImageRenderer = (locale: string) => Response | Promise<Response>; | ||
|
|
||
| export async function openGraphImageResponse( | ||
| locale: string, | ||
| render: OpenGraphImageRenderer = renderOpenGraphImage, | ||
| ): Promise<Response> { | ||
| if (!routing.locales.includes(locale as Locale)) { | ||
| return new Response(null, { status: 404 }); | ||
| } | ||
|
|
||
| return render(locale); | ||
| } |
There was a problem hiding this comment.
renderOpenGraphImage exported without a caller outside this module
renderOpenGraphImage is only ever reached through the default-parameter path of openGraphImageResponse; no external file imports or calls it directly. Exporting it publicly means future callers can bypass the locale-validation guard in openGraphImageResponse and pass any arbitrary string straight to the renderer. If intentional (e.g., for future direct use), a doc comment clarifying that callers are responsible for their own validation would help; otherwise keeping it unexported (async function renderOpenGraphImage) is safer.
ee2615e to
6a25ac2
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@web/proxy.ts`:
- Around line 64-68: Update the opengraph-image bypass condition in the
middleware to match both "/opengraph-image" and "/opengraph-image/". Preserve
the existing NextResponse.next() behavior for either pathname.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: 372d1fd8-c847-4f6d-b475-9a7d15718faf
📒 Files selected for processing (6)
web/app/[locale]/components/json-ld.tsxweb/app/[locale]/opengraph-image/route.tsweb/app/lib/open-graph-image.tsxweb/app/opengraph-image/route.tsweb/proxy.tsweb/tests/opengraph-image-route.test.ts
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@web/tests/opengraph-image-route.test.ts`:
- Around line 51-66: Update the tests around renderOpenGraphImage to mock
global.fetch or the underlying font-fetching layer with a local buffer,
preventing requests to live hosts such as fonts.gstatic.com. Remove the 15_000
timeout from the test definition while preserving the existing response status,
content type, and PNG signature assertions.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: 0ef04c44-b27c-4449-bac1-a33ed24c60c1
📒 Files selected for processing (1)
web/tests/opengraph-image-route.test.ts
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@web/app/lib/open-graph-image.tsx`:
- Around line 65-73: Update readBundledFont and the Next.js file-tracing
configuration so every file under app/lib/open-graph-fonts is explicitly
included in serverless and standalone bundles. Prefer adding the appropriate
outputFileTracingIncludes entry in next.config.ts while preserving the existing
dynamic filename lookup.
- Around line 65-72: Update the production packaging configuration in
next.config.ts to explicitly include the open-graph-fonts directory in
outputFileTracingIncludes for the route using readBundledFont(), ensuring
dynamically resolved font files are present in the serverless bundle;
alternatively, move those fonts to public and update readBundledFont
accordingly.
In `@web/tests/opengraph-image-route.test.ts`:
- Around line 16-25: Replace the manual globalThis.fetch reassignment in
withoutNetwork with the test framework’s built-in spy/mock utility, configuring
fetch to reject with the existing network-blocking error and relying on
framework-managed restoration. Update the affected tests or setup hook so the
mock is established per test context and remains safely isolated during
concurrent execution.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: ea49e2fd-404d-4dc3-ab0f-a22d31164e92
⛔ Files ignored due to path filters (12)
docs/assets/main-first-image.pngis excluded by!**/*.pngweb/app/lib/open-graph-fonts/geist-regular.ttfis excluded by!**/*.ttfweb/app/lib/open-graph-fonts/geist-semibold.ttfis excluded by!**/*.ttfweb/app/lib/open-graph-fonts/noto-cjk-jp.otfis excluded by!**/*.otfweb/app/lib/open-graph-fonts/noto-cjk-kr.otfis excluded by!**/*.otfweb/app/lib/open-graph-fonts/noto-cjk-sc.otfis excluded by!**/*.otfweb/app/lib/open-graph-fonts/noto-cjk-tc.otfis excluded by!**/*.otfweb/app/lib/open-graph-fonts/noto-khmer.ttfis excluded by!**/*.ttfweb/app/lib/open-graph-fonts/noto-sans-cyrillic.ttfis excluded by!**/*.ttfweb/app/lib/open-graph-fonts/noto-thai.ttfis excluded by!**/*.ttfweb/app/lib/open-graph-fonts/tajawal.ttfis excluded by!**/*.ttfweb/bun.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
web/app/lib/open-graph-fonts/OFL.txtweb/app/lib/open-graph-fonts/SOURCES.mdweb/app/lib/open-graph-image.tsxweb/package.jsonweb/tests/opengraph-image-route.test.ts
| async function readBundledFont(filename: string): Promise<ArrayBuffer> { | ||
| const data = await readFile( | ||
| join(process.cwd(), "app", "lib", "open-graph-fonts", filename) | ||
| ); | ||
| return data.buffer.slice( | ||
| data.byteOffset, | ||
| data.byteOffset + data.byteLength | ||
| ) as ArrayBuffer; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for outputFileTracingIncludes covering the open-graph-fonts directory
rg 'outputFileTracingIncludes' next.config.ts next.config.js next.config.mjsRepository: manaflow-ai/cmux
Length of output: 320
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file list =="
git ls-files | rg '^(web/app/lib/open-graph-image\.tsx|.*next\.config\.(ts|js|mjs)|web/.*/open-graph-fonts/|app/lib/open-graph-fonts/)'
echo
echo "== open-graph-image.tsx outline =="
ast-grep outline web/app/lib/open-graph-image.tsx --view expanded || true
echo
echo "== relevant lines in open-graph-image.tsx =="
sed -n '1,220p' web/app/lib/open-graph-image.tsx | nl -ba | sed -n '1,220p'
echo
echo "== next.config files =="
for f in next.config.ts next.config.js next.config.mjs web/next.config.ts web/next.config.js web/next.config.mjs; do
if [ -f "$f" ]; then
echo "--- $f ---"
nl -ba "$f" | sed -n '1,240p'
fi
done
echo
echo "== search for outputFileTracingIncludes and open-graph-fonts =="
rg -n 'outputFileTracingIncludes|open-graph-fonts|open-graph-image' .Repository: manaflow-ai/cmux
Length of output: 1320
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== web/app/lib/open-graph-image.tsx (selected lines) =="
sed -n '1,180p' web/app/lib/open-graph-image.tsx | cat -n
echo
echo "== web/next.config.ts =="
sed -n '1,260p' web/next.config.ts | cat -n
echo
echo "== search for tracing/include rules and open-graph-fonts =="
rg -n 'outputFileTracingIncludes|open-graph-fonts|openGraphImage|open-graph-image' web/next.config.ts web/app/lib/open-graph-image.tsxRepository: manaflow-ai/cmux
Length of output: 12957
🌐 Web query:
Next.js output file tracing dynamic readFile join filename traced files serverless fonts outputFileTracingIncludes
💡 Result:
In Next.js, Output File Tracing (NFT) automatically detects dependencies for your server-side code to minimize deployment sizes [1]. When you use dynamic file system access—such as using fs.readFile with path.join(process.cwd(), ...)—the build system may fail to statically determine which files are required, often resulting in a warning that the "whole project" is being traced [2][3]. To manage these traces and ensure files like fonts or data assets are included in your serverless or standalone deployments, use the following strategies: 1. outputFileTracingIncludes: This configuration allows you to manually include files in your trace that the automatic analysis missed [1]. It is defined in your next.config.js as an object where keys are route globs and values are arrays of glob patterns [4]. Example: module.exports = { experimental: { outputFileTracingIncludes: { '/api/fonts-route': ['./public/fonts//*'], }, }, } Note that these paths are resolved relative to your project root [1][4]. 2. Handling "Whole Project" Tracing Warnings: If you see warnings about dynamic filesystem access causing the whole project to be traced, it is usually because Next.js cannot determine the scope of a file path [3]. - Scope paths: If possible, restrict the path to a specific subfolder (e.g., path.join(process.cwd, 'data', folder)) [3]. - Ignore annotations: You can use /* turbopackIgnore: true */ comments near the dynamic path usage to instruct the build system to skip tracing for that specific call [5][6][3]. Note that there are known limitations where these comments must be placed precisely on the variable argument passed to the fs function [6]. - Monorepos: If your files are outside the standard app directory, ensure your outputFileTracingRoot is correctly set (e.g., to the monorepo root) and combine it with outputFileTracingIncludes to pull in the necessary files [1][4]. 3. Deployment Considerations: These settings are critical when using output: "standalone" or deploying to serverless environments where only essential files are included [1][7]. If a file is missing at runtime (e.g., a font used in a library like canvas), verify its presence in the generated.nft.json file located in the.next/server/ directory [8][7].
Citations:
- 1: https://nextjs.org/docs/pages/api-reference/config/next-config-js/output
- 2: Turbopack build: NFT warning "unexpected file" / next.config in trace with server routes reading fs outside app/ (outputFileTracingRoot) vercel/next.js#92639
- 3: vercel/next.js@2e1eb88
- 4: https://nextjs.org/docs/15/app/api-reference/config/next-config-js/output
- 5: Turbopack: support ignore comments for NFT fs access tracing vercel/next.js#78460
- 6: Turbopack:
turbopackIgnoredoesn't silence the dynamic-fs whole-project trace forfs(path.join(...))calls vercel/next.js#95125 - 7: outputFileTracingIncludes in app directory routes vercel/next.js#55228
- 8: https://github.com/vercel/next.js/blob/canary/packages/next/src/build/collect-build-traces.ts
🌐 Web query:
@vercel/nft fs.readFile dynamic filename path.join directory traced included whole project omitted runtime ENOENT
💡 Result:
The ENOENT error occurs because @vercel/nft (Node File Trace) uses static analysis to determine which files to include in your deployment [1][2]. When you use dynamic paths—such as variables inside path.join—the static analyzer often cannot resolve the final file path at build time [3][2]. Consequently, it fails to include those files in the production bundle, resulting in the runtime ENOENT (Error NO ENTry) error [4][1]. To resolve this, you must explicitly tell Vercel to include these files in your build output. The method depends on your project structure: For Next.js projects, use the experimental outputFileTracingIncludes configuration in your next.config.js to ensure the files are bundled: module.exports = { experimental: { outputFileTracingIncludes: { '/api/your-route': ['./path/to/files//'], }, }, }; For standard Vercel Functions (outside of Next.js), you can specify includeFiles in your vercel.json: { "functions": { "api/your-route.js": { "includeFiles": "path/to/files//" } } } If you are working in a monorepo or need to access files outside the standard root directory, you may also need to configure outputFileTracingRoot in your Next.js config to allow the tracer to see files further up the directory tree [2][5]. Once these configurations are added, the files will be physically included in your deployment, allowing fs.readFile to access them successfully at runtime [1][2].
Citations:
- 1: https://community.vercel.com/t/next-js-cant-find-files-in-fs-in-vercel-deployment/2179
- 2: https://vercel.com/kb/guide/how-can-i-use-files-in-serverless-functions
- 3: Treat
fs-extraas an alias tofs/node:fsvercel/nft#293 - 4: Unable to read files from API route (Vercel) vercel/next.js#41340
- 5: https://examples.vercel.com/guides/how-can-i-use-files-in-serverless-functions
Include web/app/lib/open-graph-fonts in the production trace
readBundledFont() resolves filename dynamically, and web/next.config.ts has no outputFileTracingIncludes rule for this route, so these font files can be dropped from the serverless bundle and fail with ENOENT. Trace that folder explicitly or move the fonts to public/.
🤖 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 `@web/app/lib/open-graph-image.tsx` around lines 65 - 72, Update the production
packaging configuration in next.config.ts to explicitly include the
open-graph-fonts directory in outputFileTracingIncludes for the route using
readBundledFont(), ensuring dynamically resolved font files are present in the
serverless bundle; alternatively, move those fonts to public and update
readBundledFont accordingly.
| async function withoutNetwork<T>(operation: () => Promise<T>): Promise<T> { | ||
| const fetchImplementation = globalThis.fetch; | ||
| globalThis.fetch = () => | ||
| Promise.reject(new Error("Open Graph rendering must not use the network")); | ||
| try { | ||
| return await operation(); | ||
| } finally { | ||
| globalThis.fetch = fetchImplementation; | ||
| } | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Race condition hazard in asynchronous global state mutation.
Temporarily replacing globalThis.fetch and restoring it after an await introduces a critical concurrency hazard. If withoutNetwork is executed concurrently by multiple tests (or overlapping microtasks), an inner execution will capture the already mocked fetch as its fetchImplementation. When it finishes, it will permanently restore the mock to globalThis.fetch, bricking network requests for the remainder of the test suite.
Please replace this manual override with your testing framework's built-in mocking utilities, which safely handle isolation and cleanup per test context.
For Vitest or Jest, you can establish the mock inside the test or in a beforeEach hook:
🛠 Proposed fix using test framework spies
For Vitest:
-async function withoutNetwork<T>(operation: () => Promise<T>): Promise<T> {
- const fetchImplementation = globalThis.fetch;
- globalThis.fetch = () =>
- Promise.reject(new Error("Open Graph rendering must not use the network"));
- try {
- return await operation();
- } finally {
- globalThis.fetch = fetchImplementation;
- }
-}And in your test cases (or beforeEach), simply use:
vi.spyOn(globalThis, "fetch").mockRejectedValue(
new Error("Open Graph rendering must not use the network")
);(If you are using Jest, use jest.spyOn(globalThis, "fetch") instead).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function withoutNetwork<T>(operation: () => Promise<T>): Promise<T> { | |
| const fetchImplementation = globalThis.fetch; | |
| globalThis.fetch = () => | |
| Promise.reject(new Error("Open Graph rendering must not use the network")); | |
| try { | |
| return await operation(); | |
| } finally { | |
| globalThis.fetch = fetchImplementation; | |
| } | |
| } |
🤖 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 `@web/tests/opengraph-image-route.test.ts` around lines 16 - 25, Replace the
manual globalThis.fetch reassignment in withoutNetwork with the test framework’s
built-in spy/mock utility, configuring fetch to reject with the existing
network-blocking error and relying on framework-managed restoration. Update the
affected tests or setup hook so the mock is established per test context and
remains safely isolated during concurrent execution.
bfc153c to
b54c1d9
Compare
Automated weekly SEO and AEO research from cmuxterm-hq.
Codex researched current primary sources, audited the existing cmux web implementation, applied one scoped improvement, and passed the web typecheck and test suite.
Run: https://github.com/manaflow-ai/cmuxterm-hq/actions/runs/29641110288
Codex report
STATUS: changed
Opportunity: 18 of 20 live
/wall-of-lovelocale routes emitted English metadata despite localized page copy. This can weaken locale relevance and produce mismatched search titles/snippets.Mechanism: metadata, Open Graph, and Twitter fields now use each locale’s authored title and description through the existing bounded SEO helpers. Canonicals and hreflang remain unchanged; no visible copy or catalogs changed.
Tests and validation:
web/i18n/routing.ts.bun test tests/seo.test.ts: 34 passed.git diff --checkpassed.web/.Primary sources accessed 2026-07-18:
Residual risk: search engines may independently rewrite titles or snippets; no implementation-specific risk was identified.