feat(www): add sitemap and robots.txt (#5)

This commit is contained in:
Naomi Chopra 2025-05-10 01:45:28 +05:30 committed by GitHub
parent 8925c28333
commit 77d03529cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 223 additions and 2 deletions

5
apps/www/.gitignore vendored
View file

@ -25,4 +25,7 @@ yarn-error.log*
# others
.env*.local
.vercel
next-env.d.ts
next-env.d.ts
# sitemap
/public/sitemap.xml

5
apps/www/app/robots.txt Normal file
View file

@ -0,0 +1,5 @@
User-Agent: *
Allow: /
Disallow: /private/
Sitemap: https://amical.ai/sitemap.xml

View file

@ -3,7 +3,8 @@
"version": "0.0.0",
"private": true,
"scripts": {
"build": "next build",
"build": "pnpm build:sitemap && next build",
"build:sitemap": "pnpm exec tsx ./scripts/generate-sitemap.mts",
"dev": "next dev --turbo",
"start": "next start",
"serve": "pnpm dlx serve out -p 3000",
@ -36,9 +37,11 @@
"@types/node": "22.15.12",
"@types/react": "^19.1.3",
"@types/react-dom": "^19.1.3",
"globby": "^14.1.0",
"postcss": "^8.5.3",
"server": "^1.0.41",
"tailwindcss": "^4.1.5",
"tsx": "^4.19.4",
"tw-animate-css": "^1.2.9",
"typescript": "^5.8.3"
}

View file

@ -0,0 +1,129 @@
import fs from 'fs';
import { globby } from 'globby';
import prettier from 'prettier';
import path from 'path';
async function generate() {
const prettierConfig = await prettier.resolveConfig('./.prettierrc');
// Ensure directories exist
fs.mkdirSync('public', { recursive: true });
fs.mkdirSync('out', { recursive: true });
// Get static pages
const pages = await globby([
'app/**/page.tsx',
'!app/**/_*/**',
'!app/**/api/**',
'!app/docs/**', // Exclude docs directory as we'll handle it separately
]);
// Get doc pages from the build output
const docPages = await globby(['out/docs/**/*.html'])
.then(pages => pages
.map(page => page
.replace('out', '')
.replace('/index.html', '')
.replace('.html', '')
)
.filter(page => !page.includes('/_'))
);
// Get blog pages from the build output
const blogPages = await globby(['out/blog/**/*.html'])
.then(pages => pages
.map(page => page
.replace('out', '')
.replace('/index.html', '')
.replace('.html', '')
)
.filter(page => !page.includes('/_') && !page.includes('/blog/index'))
);
const baseUrl = 'https://amical.ai';
const sitemap = `
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${[
// Add static pages
...pages.map((page) => {
const path = page
.replace('app', '')
.replace('/page.tsx', '')
.replace('/(home)', '')
.replace(/\[\[\.\.\..*?\]\]/, '');
// Skip dynamic routes with parameters
if (path.includes('[') || path.includes(']')) {
return '';
}
const route = path === '' ? '' : path;
return `
<url>
<loc>${baseUrl}${route}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
`;
}),
// Add docs index page
`
<url>
<loc>${baseUrl}/docs</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
`,
// Add doc pages
...docPages.map((path) => `
<url>
<loc>${baseUrl}${path}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
`),
// Add blog index page
`
<url>
<loc>${baseUrl}/blog</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
`,
// Add blog pages
...blogPages.map((path) => `
<url>
<loc>${baseUrl}${path}</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<changefreq>weekly</changefreq>
<priority>0.7</priority>
</url>
`),
]
.filter(Boolean)
.join('')}
</urlset>
`;
const formatted = await prettier.format(sitemap, {
...prettierConfig,
parser: 'html',
});
fs.writeFileSync('public/sitemap.xml', formatted);
fs.writeFileSync('out/sitemap.xml', formatted);
console.log('✅ Generated sitemap.xml');
}
generate().catch((err) => {
console.error(err);
process.exit(1);
});

81
pnpm-lock.yaml generated
View file

@ -93,6 +93,9 @@ importers:
'@types/react-dom':
specifier: ^19.1.3
version: 19.1.3(@types/react@19.1.3)
globby:
specifier: ^14.1.0
version: 14.1.0
postcss:
specifier: ^8.5.3
version: 8.5.3
@ -102,6 +105,9 @@ importers:
tailwindcss:
specifier: ^4.1.5
version: 4.1.5
tsx:
specifier: ^4.19.4
version: 4.19.4
tw-animate-css:
specifier: ^1.2.9
version: 1.2.9
@ -1076,6 +1082,10 @@ packages:
'@shikijs/vscode-textmate@10.0.2':
resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==}
'@sindresorhus/merge-streams@2.3.0':
resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
engines: {node: '>=18'}
'@socket.io/component-emitter@3.1.2':
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
@ -2191,6 +2201,11 @@ packages:
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
fumadocs-core@15.3.0:
resolution: {integrity: sha512-6+j2qGntJknzQUyes5BQI3U5EE4GCIb1Pi31eAYZ8GLxMP1p1bh8nAyEeW6oZNZmqfstJoVLQNnw+vwuHJJHUw==}
peerDependencies:
@ -2268,6 +2283,9 @@ packages:
resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
engines: {node: '>= 0.4'}
get-tsconfig@4.10.0:
resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==}
get-uri@6.0.4:
resolution: {integrity: sha512-E1b1lFFLvLgak2whF2xDBcOy6NLVGZBqqjJjsIhvopKfWWEi64pLVTWWehV8KlLerZkfNTA95sTe2OdJKm1OzQ==}
engines: {node: '>= 14'}
@ -2302,6 +2320,10 @@ packages:
resolution: {integrity: sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==}
engines: {node: '>=8'}
globby@14.1.0:
resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==}
engines: {node: '>=18'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
@ -2431,6 +2453,10 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
ignore@7.0.4:
resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==}
engines: {node: '>= 4'}
image-size@2.0.2:
resolution: {integrity: sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==}
engines: {node: '>=16.x'}
@ -3297,6 +3323,10 @@ packages:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
engines: {node: '>=8'}
path-type@6.0.0:
resolution: {integrity: sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==}
engines: {node: '>=18'}
picocolors@1.0.1:
resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==}
@ -3553,6 +3583,9 @@ packages:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'}
resolve-pkg-maps@1.0.0:
resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
resolve@1.22.10:
resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
engines: {node: '>= 0.4'}
@ -3741,6 +3774,10 @@ packages:
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
engines: {node: '>=8'}
slash@5.1.0:
resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==}
engines: {node: '>=14.16'}
smart-buffer@4.2.0:
resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==}
engines: {node: '>= 6.0.0', npm: '>= 3.0.0'}
@ -3973,6 +4010,11 @@ packages:
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
engines: {node: '>=0.6.x'}
tsx@4.19.4:
resolution: {integrity: sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==}
engines: {node: '>=18.0.0'}
hasBin: true
turbo-darwin-64@2.5.3:
resolution: {integrity: sha512-YSItEVBUIvAGPUDpAB9etEmSqZI3T6BHrkBkeSErvICXn3dfqXUfeLx35LfptLDEbrzFUdwYFNmt8QXOwe9yaw==}
cpu: [x64]
@ -4073,6 +4115,10 @@ packages:
undici-types@6.21.0:
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
unicorn-magic@0.3.0:
resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==}
engines: {node: '>=18'}
unified@11.0.5:
resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
@ -5058,6 +5104,8 @@ snapshots:
'@shikijs/vscode-textmate@10.0.2': {}
'@sindresorhus/merge-streams@2.3.0': {}
'@socket.io/component-emitter@3.1.2': {}
'@standard-schema/spec@1.0.0': {}
@ -6463,6 +6511,9 @@ snapshots:
fs.realpath@1.0.0: {}
fsevents@2.3.3:
optional: true
fumadocs-core@15.3.0(@types/react@19.1.3)(next@15.3.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@formatjs/intl-localematcher': 0.6.1
@ -6593,6 +6644,10 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.3.0
get-tsconfig@4.10.0:
dependencies:
resolve-pkg-maps: 1.0.0
get-uri@6.0.4:
dependencies:
basic-ftp: 5.0.5
@ -6640,6 +6695,15 @@ snapshots:
merge2: 1.4.1
slash: 3.0.0
globby@14.1.0:
dependencies:
'@sindresorhus/merge-streams': 2.3.0
fast-glob: 3.3.3
ignore: 7.0.4
path-type: 6.0.0
slash: 5.1.0
unicorn-magic: 0.3.0
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
@ -6830,6 +6894,8 @@ snapshots:
ignore@5.3.2: {}
ignore@7.0.4: {}
image-size@2.0.2: {}
import-fresh@3.3.1:
@ -7984,6 +8050,8 @@ snapshots:
path-type@4.0.0: {}
path-type@6.0.0: {}
picocolors@1.0.1: {}
picocolors@1.1.1: {}
@ -8337,6 +8405,8 @@ snapshots:
resolve-from@4.0.0: {}
resolve-pkg-maps@1.0.0: {}
resolve@1.22.10:
dependencies:
is-core-module: 2.16.1
@ -8656,6 +8726,8 @@ snapshots:
slash@3.0.0: {}
slash@5.1.0: {}
smart-buffer@4.2.0: {}
snake-case@2.1.0:
@ -8900,6 +8972,13 @@ snapshots:
tsscmp@1.0.6: {}
tsx@4.19.4:
dependencies:
esbuild: 0.25.4
get-tsconfig: 4.10.0
optionalDependencies:
fsevents: 2.3.3
turbo-darwin-64@2.5.3:
optional: true
@ -9009,6 +9088,8 @@ snapshots:
undici-types@6.21.0: {}
unicorn-magic@0.3.0: {}
unified@11.0.5:
dependencies:
'@types/unist': 3.0.3