Page not found
+The requested page does not exist or has moved.
+ +diff --git a/.gitignore b/.gitignore index c2b3453..5333ebe 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ site/decodal-site/dist # Astro site/decodal-site/.astro +site/decodal-site/.wrangler diff --git a/doc/develop/README.md b/doc/develop/README.md index 6d93cbd..8d3950e 100644 --- a/doc/develop/README.md +++ b/doc/develop/README.md @@ -40,6 +40,18 @@ npm --prefix site/decodal-site run build Use `npm --prefix site/decodal-site run build:wasm` when Rust bindings or formatter behavior changed. +## Documentation site delivery + +The canonical site origin is `https://decodal.hareworks.net`, configured through Astro's `site` option. A normal site build generates the sitemap, robots policy, social metadata, structured data, 404 page, and source-backed Markdown endpoints. `/llms.txt` indexes the individual English and Japanese Markdown pages; `/llms-full.txt` and `/ja/llms-full.txt` provide complete manuals. These resources are built directly from `doc/manual/source` and do not use Cloudflare's HTML-to-Markdown conversion. + +Deploy the already prepared site with: + +```sh +npm --prefix site/decodal-site run deploy +``` + +Cloudflare Pages `_redirects` handles legacy paths within the site, but not hostname redirects. In the Cloudflare account, configure a Bulk Redirect from `https://decodal-site.pages.dev/` to `https://decodal.hareworks.net/` with status `301`, subpath matching, path-suffix preservation, and query-string preservation enabled. This prevents the Pages project hostname from becoming a second public origin. + ## Syntax changes When syntax changes: diff --git a/site/decodal-site/astro.config.mjs b/site/decodal-site/astro.config.mjs index bae8db5..de20f9f 100644 --- a/site/decodal-site/astro.config.mjs +++ b/site/decodal-site/astro.config.mjs @@ -1,7 +1,47 @@ import { defineConfig } from 'astro/config'; +import sitemap from '@astrojs/sitemap'; + +const site = 'https://decodal.hareworks.net'; + +function alternateLinks(url) { + const pathname = new URL(url).pathname; + if (pathname === '/' || pathname === '/ja/') { + return [ + { url: `${site}/`, lang: 'en' }, + { url: `${site}/ja/`, lang: 'ja' }, + { url: `${site}/`, lang: 'x-default' }, + ]; + } + + const match = pathname.match(/^\/docs\/(en|ja)\/(.*)$/); + if (!match) return undefined; + const localizedPath = match[2]; + return [ + { url: `${site}/docs/en/${localizedPath}`, lang: 'en' }, + { url: `${site}/docs/ja/${localizedPath}`, lang: 'ja' }, + { url: `${site}/docs/en/${localizedPath}`, lang: 'x-default' }, + ]; +} export default defineConfig({ + site, output: 'static', + integrations: [ + sitemap({ + filter: (page) => { + const pathname = new URL(page).pathname; + return pathname !== '/docs/' + && pathname !== '/404.html' + && pathname !== '/404/' + && !pathname.endsWith('.md') + && !pathname.endsWith('.txt'); + }, + serialize(item) { + const links = alternateLinks(item.url); + return links ? { ...item, links } : item; + }, + }), + ], build: { assets: 'assets', }, diff --git a/site/decodal-site/package-lock.json b/site/decodal-site/package-lock.json index 6980613..7faeb03 100644 --- a/site/decodal-site/package-lock.json +++ b/site/decodal-site/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@astrojs/check": "^0.9.4", + "@astrojs/sitemap": "3.2.1", "@codemirror/language": "^6.12.4", "@codemirror/view": "^6.43.6", "@fontsource/ibm-plex-mono": "^5.3.0", @@ -156,6 +157,17 @@ "node": "^18.17.1 || ^20.3.0 || >=21.0.0" } }, + "node_modules/@astrojs/sitemap": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@astrojs/sitemap/-/sitemap-3.2.1.tgz", + "integrity": "sha512-uxMfO8f7pALq0ADL6Lk68UV6dNYjJ2xGUzyjjVj60JLBs5a6smtlkBYv3tQ0DzoqwS7c9n4FUx5lgv0yPo/fgA==", + "license": "MIT", + "dependencies": { + "sitemap": "^8.0.0", + "stream-replace-string": "^2.0.0", + "zod": "^3.23.8" + } + }, "node_modules/@astrojs/telemetry": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.1.0.tgz", @@ -2452,6 +2464,24 @@ "@types/unist": "*" } }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.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", @@ -2670,6 +2700,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "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", @@ -6422,6 +6458,15 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/section-matter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", @@ -6531,6 +6576,31 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "license": "MIT" }, + "node_modules/sitemap": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-8.0.3.tgz", + "integrity": "sha512-9Ew1tR2WYw8RGE2XLy7GjkusvYXy8Rg6y8TYuBuQMfIEdGcWoJpY2Wr5DzsEiL/TKCw56+YKTCCUHglorEYK+A==", + "license": "MIT", + "dependencies": { + "@types/node": "^17.0.5", + "@types/sax": "^1.2.1", + "arg": "^5.0.0", + "sax": "^1.4.1" + }, + "bin": { + "sitemap": "dist/cli.js" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/sitemap/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6568,6 +6638,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -6784,6 +6860,12 @@ "node": ">=20.18.1" } }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, "node_modules/unenv": { "version": "2.0.0-rc.24", "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", diff --git a/site/decodal-site/package.json b/site/decodal-site/package.json index 4de28d2..6ed0677 100644 --- a/site/decodal-site/package.json +++ b/site/decodal-site/package.json @@ -17,6 +17,7 @@ }, "dependencies": { "@astrojs/check": "^0.9.4", + "@astrojs/sitemap": "3.2.1", "@codemirror/language": "^6.12.4", "@codemirror/view": "^6.43.6", "@fontsource/ibm-plex-mono": "^5.3.0", diff --git a/site/decodal-site/public/_headers b/site/decodal-site/public/_headers new file mode 100644 index 0000000..3ad6394 --- /dev/null +++ b/site/decodal-site/public/_headers @@ -0,0 +1,19 @@ +/* + Content-Signal: search=yes, ai-input=yes, ai-train=no + X-Content-Type-Options: nosniff + +/*.md + Content-Type: text/markdown; charset=utf-8 + X-Robots-Tag: noindex + +/llms.txt + Content-Type: text/plain; charset=utf-8 + X-Robots-Tag: noindex + +/llms-full.txt + Content-Type: text/plain; charset=utf-8 + X-Robots-Tag: noindex + +/ja/llms-full.txt + Content-Type: text/plain; charset=utf-8 + X-Robots-Tag: noindex diff --git a/site/decodal-site/public/_redirects b/site/decodal-site/public/_redirects new file mode 100644 index 0000000..419613e --- /dev/null +++ b/site/decodal-site/public/_redirects @@ -0,0 +1,37 @@ +/docs /docs/en/ 301 +/docs/ /docs/en/ 301 +/docs/components/ /docs/en/components/ 301 +/docs/embedding/ /docs/en/embedding/ 301 +/docs/introduction/ /docs/en/introduction/ 301 +/docs/language/ /docs/en/language/ 301 +/docs/language/constraints-and-defaults/ /docs/en/language/constraints-and-defaults/ 301 +/docs/language/evaluation/ /docs/en/language/evaluation/ 301 +/docs/language/examples/ /docs/en/language/examples/ 301 +/docs/language/expression/ /docs/en/language/expression/ 301 +/docs/language/expression/arithmetic/ /docs/en/language/expression/arithmetic/ 301 +/docs/language/expression/array/ /docs/en/language/expression/array/ 301 +/docs/language/expression/ascription/ /docs/en/language/expression/ascription/ 301 +/docs/language/expression/composition/ /docs/en/language/expression/composition/ 301 +/docs/language/expression/default/ /docs/en/language/expression/default/ 301 +/docs/language/expression/function/ /docs/en/language/expression/function/ 301 +/docs/language/expression/function-call/ /docs/en/language/expression/function-call/ 301 +/docs/language/expression/identifier/ /docs/en/language/expression/identifier/ 301 +/docs/language/expression/import/ /docs/en/language/expression/import/ 301 +/docs/language/expression/let/ /docs/en/language/expression/let/ 301 +/docs/language/expression/literal/ /docs/en/language/expression/literal/ 301 +/docs/language/expression/logical-and-comparison/ /docs/en/language/expression/logical-and-comparison/ 301 +/docs/language/expression/match/ /docs/en/language/expression/match/ 301 +/docs/language/expression/object/ /docs/en/language/expression/object/ 301 +/docs/language/expression/path-reference/ /docs/en/language/expression/path-reference/ 301 +/docs/language/functions/ /docs/en/language/functions/ 301 +/docs/language/grammar/ /docs/en/language/grammar/ 301 +/docs/language/materialization-and-errors/ /docs/en/language/materialization-and-errors/ 301 +/docs/language/modules-and-imports/ /docs/en/language/modules-and-imports/ 301 +/docs/language/naming/ /docs/en/language/naming/ 301 +/docs/language/operators/ /docs/en/language/operators/ 301 +/docs/language/syntax/ /docs/en/language/syntax/ 301 +/docs/language/value/ /docs/en/language/value/ 301 +/docs/language/value/bool/ /docs/en/language/value/bool/ 301 +/docs/language/value/float/ /docs/en/language/value/float/ 301 +/docs/language/value/int/ /docs/en/language/value/int/ 301 +/docs/language/value/string/ /docs/en/language/value/string/ 301 diff --git a/site/decodal-site/public/favicon.svg b/site/decodal-site/public/favicon.svg new file mode 100644 index 0000000..bf6c737 --- /dev/null +++ b/site/decodal-site/public/favicon.svg @@ -0,0 +1,4 @@ + diff --git a/site/decodal-site/public/og-source.svg b/site/decodal-site/public/og-source.svg new file mode 100644 index 0000000..447019b --- /dev/null +++ b/site/decodal-site/public/og-source.svg @@ -0,0 +1,11 @@ + diff --git a/site/decodal-site/public/og.png b/site/decodal-site/public/og.png new file mode 100644 index 0000000..751793a Binary files /dev/null and b/site/decodal-site/public/og.png differ diff --git a/site/decodal-site/src/layouts/ManualLayout.astro b/site/decodal-site/src/layouts/ManualLayout.astro index d232ff6..ccab09a 100644 --- a/site/decodal-site/src/layouts/ManualLayout.astro +++ b/site/decodal-site/src/layouts/ManualLayout.astro @@ -11,9 +11,12 @@ const { playground = false, frontpage = false, locale, + noindex = false, + standalone = false, } = Astro.props; const pathname = Astro.url.pathname; +const site = Astro.site ?? new URL('https://decodal.hareworks.net'); const language = locale ?? 'en'; const pageDescription = description ?? (language === 'ja' ? 'Decodalは、設定、制約、デフォルトのための決定的なデータ言語です。' @@ -22,6 +25,20 @@ const docsLocale = language === 'ja' ? 'ja' : 'en'; const otherDocsLocale = docsLocale === 'ja' ? 'en' : 'ja'; const isDocsPage = pathname.startsWith('/docs/'); const localizedDocPath = active && active !== 'index' ? `${active}/` : ''; +const canonicalUrl = new URL(pathname, site).href; +const englishUrl = new URL(frontpage ? '/' : `/docs/en/${localizedDocPath}`, site).href; +const japaneseUrl = new URL(frontpage ? '/ja/' : `/docs/ja/${localizedDocPath}`, site).href; +const markdownPath = frontpage + ? language === 'ja' ? '/ja.md' : '/index.md' + : isDocsPage + ? active === 'index' + ? `/docs/${docsLocale}.md` + : `/docs/${docsLocale}/${active}.md` + : undefined; +const markdownUrl = markdownPath ? new URL(markdownPath, site).href : undefined; +const sitemapUrl = new URL('/sitemap-index.xml', site).href; +const llmsUrl = new URL('/llms.txt', site).href; +const socialImageUrl = new URL('/og.png', site).href; const homeHref = language === 'ja' ? '/ja/' : '/'; const alternateHref = frontpage ? language === 'ja' ? '/' : '/ja/' @@ -32,6 +49,53 @@ const manualLabel = language === 'ja' ? 'マニュアル' : 'Manual'; const manualHref = `/docs/${docsLocale}/introduction/`; const manualIndexHref = `/docs/${docsLocale}/`; const nav = getNav(docsLocale); +const localizedPage = !noindex && (frontpage || isDocsPage); +const structuredData = isDocsPage + ? { + '@context': 'https://schema.org', + '@type': 'TechArticle', + headline: title, + description: pageDescription, + inLanguage: language, + url: canonicalUrl, + isPartOf: { + '@type': 'WebSite', + name: 'Decodal', + url: new URL('/', site).href, + }, + } + : playground + ? { + '@context': 'https://schema.org', + '@type': 'WebApplication', + name: 'Decodal Playground', + description: pageDescription, + applicationCategory: 'DeveloperApplication', + operatingSystem: 'Web', + url: canonicalUrl, + } + : { + '@context': 'https://schema.org', + '@graph': [ + { + '@type': 'WebSite', + name: 'Decodal', + description: pageDescription, + inLanguage: language, + url: canonicalUrl, + }, + { + '@type': 'SoftwareSourceCode', + name: 'Decodal', + description: pageDescription, + codeRepository: 'https://gitea.hareworks.net/Hare/Decodal', + license: [ + 'https://opensource.org/license/mit', + 'https://www.apache.org/licenses/LICENSE-2.0', + ], + }, + ], + }; function isCurrent(path) { return pathname === path || (path !== '/' && pathname.startsWith(path)); @@ -55,9 +119,33 @@ function renderNav(items, prefix = []) { - {(frontpage || isDocsPage) && } - {(frontpage || isDocsPage) && } - {(frontpage || isDocsPage) && } + {noindex && } + {!noindex && } + + + + {markdownUrl && } + {localizedPage && } + {localizedPage && } + {localizedPage && } + + + + + + + {localizedPage && } + + + + + + + + + + + {!noindex && }
The requested page does not exist or has moved.
+ +