Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions apps/guides/app/layout.og-metadata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, expect, it } from 'vitest';
import { siteConfig } from '../lib/config';
import { metadata } from './metadata';
Comment on lines +1 to +3

describe('guides metadata', () => {
it('sets absolute og/twitter image metadata', () => {
const expected = new URL('/opengraph-image', siteConfig.url).toString();
expect(metadata.openGraph?.images?.[0]).toMatchObject({
url: expected,
width: 1200,
height: 630,
alt: 'PackRat Guides | Hiking & Outdoor Adventures',
});
expect(metadata.twitter?.images).toEqual([expected]);
});
});
44 changes: 2 additions & 42 deletions apps/guides/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ import Footer from 'guides-app/components/footer';
import Header from 'guides-app/components/header';
import { QueryProvider } from 'guides-app/components/providers/query-provider';
import { ThemeProvider } from 'guides-app/components/theme-provider';
import { siteConfig } from 'guides-app/lib/config';
import type { Metadata } from 'next';
import { Mona_Sans as FontSans } from 'next/font/google';
import type React from 'react';
import { metadata as appMetadata } from './metadata';
import './globals.css';

const fontSans = FontSans({
Expand All @@ -15,46 +14,7 @@ const fontSans = FontSans({
weight: ['400', '500', '600', '700'],
});

export const metadata: Metadata = {
title: {
default: 'PackRat Guides | Hiking & Outdoor Adventures',
template: '%s | PackRat Guides',
},
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
keywords: [
'hiking guides',
'outdoor adventures',
'trail guides',
'camping',
'backpacking',
'gear reviews',
'wilderness skills',
'outdoor planning',
],
authors: [{ name: 'PackRat Team', url: 'https://packrat.world' }],
creator: 'PackRat Team',
metadataBase: new URL(siteConfig.url),
openGraph: {
type: 'website',
locale: 'en_US',
url: siteConfig.url,
siteName: 'PackRat Guides',
title: 'PackRat Guides | Hiking & Outdoor Adventures',
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
},
twitter: {
card: 'summary_large_image',
title: 'PackRat Guides | Hiking & Outdoor Adventures',
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
creator: '@packratai',
},
icons: {
icon: [{ url: '/PackRatGuides.ico', type: 'image/x-icon' }],
shortcut: '/favicon-16x16.png',
apple: '/apple-touch-icon.png',
},
};

export const metadata = appMetadata;
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
Expand Down
51 changes: 51 additions & 0 deletions apps/guides/app/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import type { Metadata } from 'next';
import { siteConfig } from '../lib/config';

export const metadata: Metadata = {
title: {
default: 'PackRat Guides | Hiking & Outdoor Adventures',
template: '%s | PackRat Guides',
},
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
keywords: [
'hiking guides',
'outdoor adventures',
'trail guides',
'camping',
'backpacking',
'gear reviews',
'wilderness skills',
'outdoor planning',
],
authors: [{ name: 'PackRat Team', url: 'https://packrat.world' }],
creator: 'PackRat Team',
metadataBase: new URL(siteConfig.url),
openGraph: {
type: 'website',
locale: 'en_US',
url: siteConfig.url,
siteName: 'PackRat Guides',
title: 'PackRat Guides | Hiking & Outdoor Adventures',
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
images: [
{
url: new URL('/opengraph-image', siteConfig.url).toString(),
width: 1200,
height: 630,
alt: 'PackRat Guides | Hiking & Outdoor Adventures',
},
],
},
twitter: {
card: 'summary_large_image',
title: 'PackRat Guides | Hiking & Outdoor Adventures',
description: 'Expert hiking and outdoor guides to help you prepare for your next adventure',
creator: '@packratai',
images: [new URL('/opengraph-image', siteConfig.url).toString()],
},
icons: {
icon: [{ url: '/PackRatGuides.ico', type: 'image/x-icon' }],
shortcut: '/favicon-16x16.png',
apple: '/apple-touch-icon.png',
},
};
16 changes: 16 additions & 0 deletions apps/landing/app/layout.og-metadata.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, expect, it } from 'vitest';
import { siteConfig } from '../config/site';
import { metadata } from './metadata';
Comment on lines +1 to +3

describe('landing metadata', () => {
it('sets absolute og/twitter image metadata', () => {
const expected = new URL(siteConfig.ogImage, siteConfig.url).toString();
expect(metadata.openGraph?.images?.[0]).toMatchObject({
Comment on lines +6 to +8
url: expected,
width: 1200,
height: 630,
alt: siteConfig.name,
});
expect(metadata.twitter?.images).toEqual([expected]);
});
});
36 changes: 2 additions & 34 deletions apps/landing/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@ import { cn } from '@packrat/web-ui/lib/utils';
import MainNav from 'landing-app/components/main-nav';
import SiteFooter from 'landing-app/components/site-footer';
import { ThemeProvider } from 'landing-app/components/theme-provider';
import { siteConfig } from 'landing-app/config/site';
import type { Metadata } from 'next';
import { Mona_Sans as FontSans } from 'next/font/google';
import type React from 'react';
import { metadata as appMetadata } from './metadata';
import './globals.css';

const fontSans = FontSans({
Expand All @@ -14,38 +13,7 @@ const fontSans = FontSans({
weight: ['400', '500', '600', '700'],
});

export const metadata: Metadata = {
title: {
default: siteConfig.name,
template: `%s | ${siteConfig.name}`,
},
description: siteConfig.description,
keywords: siteConfig.keywords,
authors: [{ name: siteConfig.author, url: siteConfig.url }],
creator: siteConfig.author,
metadataBase: new URL(siteConfig.url),
openGraph: {
type: 'website',
locale: 'en_US',
url: siteConfig.url,
title: siteConfig.name,
description: siteConfig.description,
siteName: siteConfig.name,
},
twitter: {
card: 'summary_large_image',
title: siteConfig.name,
description: siteConfig.description,
creator: siteConfig.twitterHandle,
},
icons: {
icon: '/PackRat.ico',
shortcut: '/favicon-16x16.png',
apple: '/apple-touch-icon.png',
},
manifest: `${siteConfig.url}/site.webmanifest`,
};

export const metadata = appMetadata;
export default function RootLayout({
children,
}: Readonly<{
Expand Down
43 changes: 43 additions & 0 deletions apps/landing/app/metadata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { Metadata } from 'next';
import { siteConfig } from '../config/site';

export const metadata: Metadata = {
title: {
default: siteConfig.name,
template: `%s | ${siteConfig.name}`,
},
description: siteConfig.description,
keywords: siteConfig.keywords,
authors: [{ name: siteConfig.author, url: siteConfig.url }],
creator: siteConfig.author,
metadataBase: new URL(siteConfig.url),
openGraph: {
type: 'website',
locale: 'en_US',
url: siteConfig.url,
title: siteConfig.name,
description: siteConfig.description,
siteName: siteConfig.name,
images: [
{
url: new URL(siteConfig.ogImage, siteConfig.url).toString(),
width: 1200,
height: 630,
alt: siteConfig.name,
Comment on lines +21 to +26
},
],
},
twitter: {
card: 'summary_large_image',
title: siteConfig.name,
description: siteConfig.description,
creator: siteConfig.twitterHandle,
images: [new URL(siteConfig.ogImage, siteConfig.url).toString()],
},
Comment on lines +30 to +36
icons: {
icon: '/PackRat.ico',
shortcut: '/favicon-16x16.png',
apple: '/apple-touch-icon.png',
},
manifest: `${siteConfig.url}/site.webmanifest`,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use new URL() for consistent URL construction.

String interpolation can produce malformed URLs if siteConfig.url has a trailing slash (resulting in //site.webmanifest). Use the same pattern as lines 23 and 35 for consistency and correctness.

🔧 Proposed fix
-  manifest: `${siteConfig.url}/site.webmanifest`,
+  manifest: new URL('/site.webmanifest', siteConfig.url).toString(),
📝 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.

Suggested change
manifest: `${siteConfig.url}/site.webmanifest`,
manifest: new URL('/site.webmanifest', siteConfig.url).toString(),
🤖 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/landing/app/metadata.ts` at line 42, The manifest URL is built via
string interpolation which can produce double slashes; replace the interpolated
manifest: `${siteConfig.url}/site.webmanifest` with construction using new URL
so it correctly resolves regardless of trailing slash (e.g., use new
URL('/site.webmanifest', siteConfig.url).toString() or .href), mirroring the
approach used elsewhere in this file (see other usages referencing
siteConfig.url).

};