Skip to content

Commit 1d55527

Browse files
committed
feat(www): add sitemap and robots.txt
1 parent 8925c28 commit 1d55527

File tree

5 files changed

+218
-2
lines changed

5 files changed

+218
-2
lines changed

apps/www/.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,7 @@ yarn-error.log*
2525
# others
2626
.env*.local
2727
.vercel
28-
next-env.d.ts
28+
next-env.d.ts
29+
30+
# sitemap
31+
/public/sitemap.xml

apps/www/app/robots.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
User-Agent: *
2+
Allow: /
3+
Disallow: /private/
4+
5+
Sitemap: https://amical.ai/sitemap.xml

apps/www/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"version": "0.0.0",
44
"private": true,
55
"scripts": {
6-
"build": "next build",
6+
"build": "pnpm build:sitemap && next build",
7+
"build:sitemap": "pnpm exec tsx ./scripts/generate-sitemap.mts",
78
"dev": "next dev --turbo",
89
"start": "next start",
910
"serve": "pnpm dlx serve out -p 3000",
@@ -36,9 +37,11 @@
3637
"@types/node": "22.15.12",
3738
"@types/react": "^19.1.3",
3839
"@types/react-dom": "^19.1.3",
40+
"globby": "^14.1.0",
3941
"postcss": "^8.5.3",
4042
"server": "^1.0.41",
4143
"tailwindcss": "^4.1.5",
44+
"tsx": "^4.19.4",
4245
"tw-animate-css": "^1.2.9",
4346
"typescript": "^5.8.3"
4447
}

apps/www/scripts/generate-sitemap.mts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import fs from 'fs';
2+
import { globby } from 'globby';
3+
import prettier from 'prettier';
4+
5+
async function generate() {
6+
const prettierConfig = await prettier.resolveConfig('./.prettierrc');
7+
8+
// Get static pages
9+
const pages = await globby([
10+
'app/**/page.tsx',
11+
'!app/**/_*/**',
12+
'!app/**/api/**',
13+
'!app/docs/**', // Exclude docs directory as we'll handle it separately
14+
]);
15+
16+
// Get doc pages from the build output
17+
const docPages = await globby(['out/docs/**/*.html'])
18+
.then(pages => pages
19+
.map(page => page
20+
.replace('out', '')
21+
.replace('/index.html', '')
22+
.replace('.html', '')
23+
)
24+
.filter(page => !page.includes('/_'))
25+
);
26+
27+
// Get blog pages from the build output
28+
const blogPages = await globby(['out/blog/**/*.html'])
29+
.then(pages => pages
30+
.map(page => page
31+
.replace('out', '')
32+
.replace('/index.html', '')
33+
.replace('.html', '')
34+
)
35+
.filter(page => !page.includes('/_') && !page.includes('/blog/index'))
36+
);
37+
38+
const baseUrl = 'https://amical.ai';
39+
40+
const sitemap = `
41+
<?xml version="1.0" encoding="UTF-8"?>
42+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
43+
${[
44+
// Add static pages
45+
...pages.map((page) => {
46+
const path = page
47+
.replace('app', '')
48+
.replace('/page.tsx', '')
49+
.replace('/(home)', '')
50+
.replace(/\[\[\.\.\..*?\]\]/, '');
51+
52+
// Skip dynamic routes with parameters
53+
if (path.includes('[') || path.includes(']')) {
54+
return '';
55+
}
56+
57+
const route = path === '' ? '' : path;
58+
59+
return `
60+
<url>
61+
<loc>${baseUrl}${route}</loc>
62+
<lastmod>${new Date().toISOString()}</lastmod>
63+
<changefreq>daily</changefreq>
64+
<priority>0.7</priority>
65+
</url>
66+
`;
67+
}),
68+
// Add docs index page
69+
`
70+
<url>
71+
<loc>${baseUrl}/docs</loc>
72+
<lastmod>${new Date().toISOString()}</lastmod>
73+
<changefreq>daily</changefreq>
74+
<priority>0.7</priority>
75+
</url>
76+
`,
77+
// Add doc pages
78+
...docPages.map((path) => `
79+
<url>
80+
<loc>${baseUrl}${path}</loc>
81+
<lastmod>${new Date().toISOString()}</lastmod>
82+
<changefreq>daily</changefreq>
83+
<priority>0.7</priority>
84+
</url>
85+
`),
86+
// Add blog index page
87+
`
88+
<url>
89+
<loc>${baseUrl}/blog</loc>
90+
<lastmod>${new Date().toISOString()}</lastmod>
91+
<changefreq>weekly</changefreq>
92+
<priority>0.8</priority>
93+
</url>
94+
`,
95+
// Add blog pages
96+
...blogPages.map((path) => `
97+
<url>
98+
<loc>${baseUrl}${path}</loc>
99+
<lastmod>${new Date().toISOString()}</lastmod>
100+
<changefreq>weekly</changefreq>
101+
<priority>0.7</priority>
102+
</url>
103+
`),
104+
]
105+
.filter(Boolean)
106+
.join('')}
107+
</urlset>
108+
`;
109+
110+
const formatted = await prettier.format(sitemap, {
111+
...prettierConfig,
112+
parser: 'html',
113+
});
114+
115+
fs.writeFileSync('public/sitemap.xml', formatted);
116+
fs.writeFileSync('out/sitemap.xml', formatted);
117+
118+
console.log('✅ Generated sitemap.xml');
119+
}
120+
121+
generate().catch((err) => {
122+
console.error(err);
123+
process.exit(1);
124+
});

pnpm-lock.yaml

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)