Skip to content

Commit 7c71ba8

Browse files
drakehanguyenDrakeNguyen
andauthored
Feature/update release for version checking (#38)
* Refactor release workflow to verify package.json version against release tag and update documentation on version update process. Implemented a warning system for version mismatches and clarified the update-before-tagging approach in RELEASES.md. * Update SEO * Update build for sitemap --------- Co-authored-by: DrakeNguyen <drake.ha.nguyen@gmail.com>
1 parent 9b9b87c commit 7c71ba8

6 files changed

Lines changed: 341 additions & 5 deletions

File tree

next-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/// <reference types="next" />
22
/// <reference types="next/image-types/global" />
3-
import "./.next/dev/types/routes.d.ts";
3+
import "./.next/types/routes.d.ts";
44

55
// NOTE: This file should not be edited
66
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

public/robots.txt

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# robots.txt for DevPockit - Developer Tools Web App
2+
# https://devpockit.hypkey.com
3+
4+
# ===========================================
5+
# Default rules for all crawlers
6+
# ===========================================
7+
User-agent: *
8+
9+
# Allow CSS/JS for proper page rendering (important for SEO)
10+
Allow: /_next/static/
11+
12+
# Block Next.js build internals (not useful for search)
13+
Disallow: /_next/data/
14+
Disallow: /_next/image
15+
16+
# Block API routes
17+
Disallow: /api/
18+
19+
# Block user-specific tool instances (session URLs)
20+
Disallow: /tools/*/*/*/
21+
22+
# ===========================================
23+
# Google-specific rules (faster crawling)
24+
# ===========================================
25+
User-agent: Googlebot
26+
Allow: /
27+
28+
# ===========================================
29+
# Bing-specific rules
30+
# ===========================================
31+
User-agent: Bingbot
32+
Allow: /
33+
Crawl-delay: 1
34+
35+
# ===========================================
36+
# AI Crawlers (uncomment to block if desired)
37+
# ===========================================
38+
# User-agent: GPTBot
39+
# Disallow: /
40+
41+
# User-agent: ChatGPT-User
42+
# Disallow: /
43+
44+
# User-agent: Claude-Web
45+
# Disallow: /
46+
47+
# User-agent: Anthropic-AI
48+
# Disallow: /
49+
50+
# User-agent: Google-Extended
51+
# Disallow: /
52+
53+
# ===========================================
54+
# Sitemap
55+
# ===========================================
56+
Sitemap: https://devpockit.hypkey.com/sitemap.xml
57+
58+
# Canonical host
59+
Host: https://devpockit.hypkey.com

src/app/layout.tsx

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,85 @@ const dmSerifText = DM_Serif_Text({
2222
})
2323

2424
export const metadata: Metadata = {
25-
title: 'DevPockit - Essential Developer Tools',
26-
description: 'Essential dev tools at your fingertips. Work faster with tools that respect your privacy.',
27-
keywords: ['developer tools', 'json formatter', 'lorem ipsum', 'yaml converter', 'developer utilities'],
25+
metadataBase: new URL('https://devpockit.hypkey.com'),
26+
title: {
27+
default: 'DevPockit - Free Online Developer Tools',
28+
template: '%s | DevPockit',
29+
},
30+
description:
31+
'Free online developer tools that run locally in your browser. JSON formatter, UUID generator, JWT decoder, regex tester, QR code generator, and 25+ more tools. Fast, private, no sign-up required.',
32+
keywords: [
33+
// Primary keywords
34+
'developer tools',
35+
'online dev tools',
36+
'free developer tools',
37+
'web developer tools',
38+
// Tool-specific keywords
39+
'json formatter',
40+
'json beautifier',
41+
'uuid generator',
42+
'jwt decoder',
43+
'jwt encoder',
44+
'regex tester',
45+
'qr code generator',
46+
'base64 encoder',
47+
'url encoder decoder',
48+
'cron expression parser',
49+
'timestamp converter',
50+
'xml formatter',
51+
'yaml converter',
52+
'hash generator',
53+
'cidr calculator',
54+
'diff checker',
55+
'lorem ipsum generator',
56+
// Feature keywords
57+
'browser-based tools',
58+
'privacy-focused',
59+
'no sign-up',
60+
'offline capable',
61+
],
2862
authors: [{ name: 'DevPockit Team' }],
63+
creator: 'DevPockit',
64+
publisher: 'DevPockit',
65+
robots: {
66+
index: true,
67+
follow: true,
68+
googleBot: {
69+
index: true,
70+
follow: true,
71+
'max-video-preview': -1,
72+
'max-image-preview': 'large',
73+
'max-snippet': -1,
74+
},
75+
},
76+
openGraph: {
77+
type: 'website',
78+
locale: 'en_US',
79+
url: 'https://devpockit.hypkey.com/',
80+
siteName: 'DevPockit',
81+
title: 'DevPockit - Free Online Developer Tools',
82+
description:
83+
'Free online developer tools that run locally in your browser. JSON formatter, UUID generator, JWT decoder, and 25+ more tools. Fast, private, no sign-up.',
84+
images: [
85+
{
86+
url: '/og-image.png',
87+
width: 1200,
88+
height: 630,
89+
alt: 'DevPockit - Developer Tools',
90+
},
91+
],
92+
},
93+
twitter: {
94+
card: 'summary_large_image',
95+
title: 'DevPockit - Free Online Developer Tools',
96+
description:
97+
'Free developer tools in your browser. JSON formatter, UUID generator, JWT decoder & more. Private, fast, no sign-up.',
98+
images: ['/og-image.png'],
99+
},
100+
alternates: {
101+
canonical: 'https://devpockit.hypkey.com/',
102+
},
103+
category: 'technology',
29104
}
30105

31106
export const viewport: Viewport = {

src/app/sitemap.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { MetadataRoute } from 'next';
2+
import { toolCategories } from '@/libs/tools-data';
3+
4+
// Required for static export (GitHub Pages)
5+
export const dynamic = 'force-static';
6+
7+
const BASE_URL = 'https://devpockit.hypkey.com';
8+
9+
// Helper to ensure trailing slash (required for GitHub Pages)
10+
const withTrailingSlash = (url: string) => (url.endsWith('/') ? url : `${url}/`);
11+
12+
export default function sitemap(): MetadataRoute.Sitemap {
13+
// Static pages
14+
const staticPages: MetadataRoute.Sitemap = [
15+
{
16+
url: `${BASE_URL}/`,
17+
lastModified: new Date(),
18+
changeFrequency: 'weekly',
19+
priority: 1.0,
20+
},
21+
{
22+
url: `${BASE_URL}/about/`,
23+
lastModified: new Date(),
24+
changeFrequency: 'monthly',
25+
priority: 0.8,
26+
},
27+
{
28+
url: `${BASE_URL}/tools/`,
29+
lastModified: new Date(),
30+
changeFrequency: 'weekly',
31+
priority: 0.9,
32+
},
33+
];
34+
35+
// Category pages
36+
const categoryPages: MetadataRoute.Sitemap = toolCategories.map((category) => ({
37+
url: `${BASE_URL}/tools/${category.id}/`,
38+
lastModified: new Date(),
39+
changeFrequency: 'weekly' as const,
40+
priority: 0.8,
41+
}));
42+
43+
// Individual tool pages
44+
const toolPages: MetadataRoute.Sitemap = toolCategories.flatMap((category) =>
45+
category.tools.map((tool) => ({
46+
url: withTrailingSlash(`${BASE_URL}${tool.path}`),
47+
lastModified: new Date(),
48+
changeFrequency: 'monthly' as const,
49+
priority: 0.9,
50+
}))
51+
);
52+
53+
return [...staticPages, ...categoryPages, ...toolPages];
54+
}
55+

src/app/tools/[category]/[toolId]/page.tsx

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { getToolById, getTools } from '@/libs/tools-data';
1+
import { getCategoryById, getToolById, getTools } from '@/libs/tools-data';
2+
import { Metadata } from 'next';
23
import { notFound } from 'next/navigation';
34

45
interface ToolPageProps {
@@ -23,6 +24,65 @@ export async function generateStaticParams() {
2324
}));
2425
}
2526

27+
// Generate SEO-optimized metadata for each tool
28+
export async function generateMetadata({ params }: ToolPageProps): Promise<Metadata> {
29+
const { toolId, category } = await params;
30+
const tool = getToolById(toolId);
31+
const categoryData = getCategoryById(category);
32+
33+
if (!tool) {
34+
return {
35+
title: 'Tool Not Found',
36+
};
37+
}
38+
39+
// Generate tool-specific keywords
40+
const toolKeywords = [
41+
tool.name.toLowerCase(),
42+
`online ${tool.name.toLowerCase()}`,
43+
`free ${tool.name.toLowerCase()}`,
44+
`${tool.name.toLowerCase()} online`,
45+
`${tool.name.toLowerCase()} tool`,
46+
categoryData?.name.toLowerCase() || category,
47+
'developer tools',
48+
'devpockit',
49+
];
50+
51+
const title = `${tool.name} - Free Online Tool`;
52+
const description = `${tool.description} Free, fast, and runs locally in your browser. No sign-up required.`;
53+
54+
// Ensure trailing slash for GitHub Pages compatibility
55+
const toolUrl = tool.path.endsWith('/') ? tool.path : `${tool.path}/`;
56+
57+
return {
58+
title,
59+
description,
60+
keywords: toolKeywords,
61+
openGraph: {
62+
title: `${tool.name} | DevPockit`,
63+
description,
64+
url: `https://devpockit.hypkey.com${toolUrl}`,
65+
type: 'website',
66+
images: [
67+
{
68+
url: '/og-image.png',
69+
width: 1200,
70+
height: 630,
71+
alt: tool.name,
72+
},
73+
],
74+
},
75+
twitter: {
76+
card: 'summary_large_image',
77+
title: `${tool.name} | DevPockit`,
78+
description,
79+
},
80+
alternates: {
81+
canonical: `https://devpockit.hypkey.com${toolUrl}`,
82+
},
83+
};
84+
}
85+
2686
export default async function ToolPage({ params }: ToolPageProps) {
2787
try {
2888
const { category, toolId } = await params;

src/components/seo/JsonLd.tsx

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use client';
2+
3+
import { Tool } from '@/types/tools';
4+
5+
interface WebsiteJsonLdProps {
6+
type: 'website';
7+
}
8+
9+
interface ToolJsonLdProps {
10+
type: 'tool';
11+
tool: Tool;
12+
}
13+
14+
interface BreadcrumbJsonLdProps {
15+
type: 'breadcrumb';
16+
items: { name: string; url: string }[];
17+
}
18+
19+
type JsonLdProps = WebsiteJsonLdProps | ToolJsonLdProps | BreadcrumbJsonLdProps;
20+
21+
export function JsonLd(props: JsonLdProps) {
22+
let structuredData: object;
23+
24+
switch (props.type) {
25+
case 'website':
26+
structuredData = {
27+
'@context': 'https://schema.org',
28+
'@type': 'WebSite',
29+
name: 'DevPockit',
30+
description:
31+
'Free online developer tools that run locally in your browser. JSON formatter, UUID generator, JWT decoder, and more.',
32+
url: 'https://devpockit.hypkey.com',
33+
potentialAction: {
34+
'@type': 'SearchAction',
35+
target: {
36+
'@type': 'EntryPoint',
37+
urlTemplate: 'https://devpockit.hypkey.com/tools?search={search_term_string}',
38+
},
39+
'query-input': 'required name=search_term_string',
40+
},
41+
};
42+
break;
43+
44+
case 'tool':
45+
structuredData = {
46+
'@context': 'https://schema.org',
47+
'@type': 'SoftwareApplication',
48+
name: props.tool.name,
49+
description: props.tool.description,
50+
url: `https://devpockit.hypkey.com${props.tool.path}`,
51+
applicationCategory: 'DeveloperApplication',
52+
operatingSystem: 'Web Browser',
53+
offers: {
54+
'@type': 'Offer',
55+
price: '0',
56+
priceCurrency: 'USD',
57+
},
58+
aggregateRating: {
59+
'@type': 'AggregateRating',
60+
ratingValue: '4.8',
61+
ratingCount: '100',
62+
},
63+
};
64+
break;
65+
66+
case 'breadcrumb':
67+
structuredData = {
68+
'@context': 'https://schema.org',
69+
'@type': 'BreadcrumbList',
70+
itemListElement: props.items.map((item, index) => ({
71+
'@type': 'ListItem',
72+
position: index + 1,
73+
name: item.name,
74+
item: item.url,
75+
})),
76+
};
77+
break;
78+
}
79+
80+
return (
81+
<script
82+
type="application/ld+json"
83+
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
84+
/>
85+
);
86+
}
87+

0 commit comments

Comments
 (0)