From f8c7ee598a0efbd47cc058abc007db73ff18cccd Mon Sep 17 00:00:00 2001 From: Branimir Georgiev Date: Sat, 25 Apr 2026 15:07:27 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20add=20SEO=20optimization=20=E2=80=94=20?= =?UTF-8?q?meta=20tags,=20sitemap,=20structured=20data=20(#123)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add page-specific meta descriptions via frontmatter - Add Open Graph and Twitter Card meta tags - Add canonical URLs - Install @astrojs/sitemap integration - Add robots.txt - Add JSON-LD structured data (Course on home, Article on chapters) Co-Authored-By: Claude Opus 4.6 (1M context) --- astro-site/astro.config.mjs | 2 + astro-site/package-lock.json | 67 ++++++++++++++++++++++++++ astro-site/package.json | 3 +- astro-site/public/robots.txt | 4 ++ astro-site/src/content.config.ts | 1 + astro-site/src/layouts/DocLayout.astro | 32 ++++++++++-- astro-site/src/pages/[...slug].astro | 29 ++++++++++- astro-site/src/pages/index.astro | 24 ++++++++- chapters/01-introduction.md | 1 + chapters/02-building-blocks.md | 1 + chapters/03-branching-and-merging.md | 1 + chapters/04-remote-repositories.md | 1 + chapters/05-nested-repositories.md | 1 + chapters/06-expert-topics.md | 1 + chapters/07-playbook.md | 1 + chapters/08-appendix.md | 1 + chapters/09-glossary.md | 1 + 17 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 astro-site/public/robots.txt diff --git a/astro-site/astro.config.mjs b/astro-site/astro.config.mjs index 4dac08b..8d36cde 100644 --- a/astro-site/astro.config.mjs +++ b/astro-site/astro.config.mjs @@ -1,5 +1,6 @@ // @ts-check import { defineConfig } from 'astro/config'; +import sitemap from '@astrojs/sitemap'; import { remarkRewriteLinks } from './src/plugins/remark-rewrite-links.ts'; // https://astro.build/config @@ -7,6 +8,7 @@ export default defineConfig({ site: 'https://braboj.github.io', base: '/tutorial-git/', trailingSlash: 'always', + integrations: [sitemap()], markdown: { syntaxHighlight: false, remarkPlugins: [remarkRewriteLinks], diff --git a/astro-site/package-lock.json b/astro-site/package-lock.json index 9406a9e..a6aa241 100644 --- a/astro-site/package-lock.json +++ b/astro-site/package-lock.json @@ -8,6 +8,7 @@ "name": "astro-site", "version": "0.0.1", "dependencies": { + "@astrojs/sitemap": "^3.7.2", "astro": "^6.1.8" }, "engines": { @@ -70,6 +71,17 @@ "node": ">=22.12.0" } }, + "node_modules/@astrojs/sitemap": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.7.2.tgz", + "integrity": "sha512-PqkzkcZTb5ICiyIR8VoKbIAP/laNRXi5tw616N1Ckk+40oNB8Can1AzVV56lrbC5GKSZFCyJYUVYqVivMisvpA==", + "license": "MIT", + "dependencies": { + "sitemap": "^9.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^4.3.6" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.1.tgz", @@ -1566,6 +1578,24 @@ "@types/unist": "*" } }, + "node_modules/@types/node": { + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/sax": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", + "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -1603,6 +1633,12 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -4108,6 +4144,25 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "license": "MIT" }, + "node_modules/sitemap": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-9.0.1.tgz", + "integrity": "sha512-S6hzjGJSG3d6if0YoF5kTyeRJvia6FSTBroE5fQ0bu1QNxyJqhhinfUsXi9fH3MgtXODWvwo2BDyQSnhPQ88uQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.9.2", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/esm/cli.js" + }, + "engines": { + "node": ">=20.19.5", + "npm": ">=10.8.2" + } + }, "node_modules/smol-toml": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", @@ -4139,6 +4194,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/stream-replace-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", + "integrity": "sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==", + "license": "MIT" + }, "node_modules/stringify-entities": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", @@ -4283,6 +4344,12 @@ "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", "license": "MIT" }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", diff --git a/astro-site/package.json b/astro-site/package.json index e4c0ecf..7cba261 100644 --- a/astro-site/package.json +++ b/astro-site/package.json @@ -12,6 +12,7 @@ "astro": "astro" }, "dependencies": { + "@astrojs/sitemap": "^3.7.2", "astro": "^6.1.8" } -} \ No newline at end of file +} diff --git a/astro-site/public/robots.txt b/astro-site/public/robots.txt new file mode 100644 index 0000000..21d7118 --- /dev/null +++ b/astro-site/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://braboj.github.io/tutorial-git/sitemap-index.xml diff --git a/astro-site/src/content.config.ts b/astro-site/src/content.config.ts index e33a8e2..34ece89 100644 --- a/astro-site/src/content.config.ts +++ b/astro-site/src/content.config.ts @@ -9,6 +9,7 @@ const docs = defineCollection({ }), schema: z.object({ title: z.string(), + description: z.string().optional(), section: z.string(), order: z.number(), }), diff --git a/astro-site/src/layouts/DocLayout.astro b/astro-site/src/layouts/DocLayout.astro index 0a344a4..50c10ae 100644 --- a/astro-site/src/layouts/DocLayout.astro +++ b/astro-site/src/layouts/DocLayout.astro @@ -8,11 +8,17 @@ import site from "../data/site.json"; interface Props { title: string; + description?: string; headings?: { depth: number; slug: string; text: string }[]; currentPath?: string; + jsonLd?: Record; } -const { title, headings = [], currentPath = "/" } = Astro.props; +const { title, description, headings = [], currentPath = "/", jsonLd } = Astro.props; +const pageDescription = description || site.description; +const pageTitle = `${title} — ${site.title}`; +const canonicalUrl = new URL(Astro.url.pathname, Astro.site); +const ogImage = new URL("/tutorial-git/images/og-banner.png", Astro.site); --- @@ -20,14 +26,34 @@ const { title, headings = [], currentPath = "/" } = Astro.props; - {title} — {site.title} - + {pageTitle} + + + + + + + + + + + + + + + + + + + {jsonLd && ( +