diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 35c2fb19b1..9ca03ded07 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -93,10 +93,10 @@ jobs: with: args: | --root-dir $PWD/dist + --fallback-extensions html --exclude-path dist/llms.txt --exclude-path dist/llms/ --exclude-private - --remap "https://expressjs\.com\/((?:[^\/]+\/)*[^\/\.]+)\/?$ file://$PWD/dist/\$1/index.html" - --remap "https://expressjs\.com\/(.*\.html)$ file://$PWD/dist/\$1" + --remap "^https://expressjs\.com\/ file://$PWD/dist/" dist/ fail: true diff --git a/.lycheeignore b/.lycheeignore index 29bed0cc5d..8eb2faa0bf 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -10,8 +10,7 @@ https://www.npmjs.org/.* https://github.com/.* # Exclude 404 pages -dist/.*/404/index.html$ -dist/404/index.html$ +dist/(.*/)?404(\.html|/)?$ # Exclude Open Collective links https://opencollective.com/.* diff --git a/astro.config.mjs b/astro.config.mjs index 557730f332..2ca7ab9029 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -21,6 +21,9 @@ const site = NETLIFY_PREVIEW_SITE || 'https://expressjs.com'; export default defineConfig({ redirects, site, + build: { + format: 'file', + }, markdown: { rehypePlugins: [ rehypeSlug, diff --git a/src/components/patterns/Sidebar/utils.ts b/src/components/patterns/Sidebar/utils.ts index 973e41120f..f9943cc200 100644 --- a/src/components/patterns/Sidebar/utils.ts +++ b/src/components/patterns/Sidebar/utils.ts @@ -12,7 +12,7 @@ export type SubmenuData = { }; export function normalizePath(path: string): string { - return path.replace(/\/$/, ''); + return path.replace(/(?:\/|\.html)$/, ''); } export function isVersioned(versioned: VersionPrefix[] | undefined, version: string): boolean { diff --git a/src/i18n/utils.ts b/src/i18n/utils.ts index 562188a1f8..739408ad26 100644 --- a/src/i18n/utils.ts +++ b/src/i18n/utils.ts @@ -1,7 +1,8 @@ import { ui, defaultLang, languages } from './locales'; export function getLangFromUrl(url: URL) { - const [, lang] = url.pathname.split('/'); + const strippedPathname = url.pathname.replace(/\.html$/, ''); + const [, lang] = strippedPathname.split('/'); if (lang in ui) return lang as keyof typeof ui; return defaultLang; } @@ -34,7 +35,7 @@ export function getLanguageCodes(): string[] { */ export function createLanguagePathRegex(): RegExp { const codes = getLanguageCodes().join('|'); - return new RegExp(`^/(${codes})/`); + return new RegExp(`^/(${codes})(?:/|$)`); } /** @@ -43,9 +44,10 @@ export function createLanguagePathRegex(): RegExp { export function replaceLanguageInPath(path: string, newLang: string): string { const langRegex = createLanguagePathRegex(); - if (langRegex.test(path)) { - return path.replace(langRegex, `/${newLang}/`); + const strippedPath = path.replace(/\.html$/, ''); + if (langRegex.test(strippedPath)) { + return strippedPath.replace(langRegex, `/${newLang}/`); } else { - return `/${newLang}${path}`; + return `/${newLang}${strippedPath}`; } } diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index bfe6e71174..d53ff2f5e5 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -24,9 +24,9 @@ const pageTitle = title ? `${title} · Express.js` : 'Express.js · Node.js web application framework'; -const canonicalUrl = new URL(Astro.url.pathname, Astro.site || Astro.url.origin); +const pathname = Astro.url.pathname.replace(/\.html$/, ''); +const canonicalUrl = new URL(pathname, Astro.site || Astro.url.origin); -const pathname = Astro.url.pathname.replace(/\/$/, ''); const segments = pathname.split('/').filter(Boolean); const langSegment = segments[0] || 'en'; const restSegments = segments.slice(1); diff --git a/src/utils/content.ts b/src/utils/content.ts index e656027b59..bf755936fd 100644 --- a/src/utils/content.ts +++ b/src/utils/content.ts @@ -74,7 +74,7 @@ function resolveLabel(t: (key: string) => string, labelKey?: string, segment?: s * Build breadcrumbs */ export function buildBreadcrumbs(pathname: string, t: (key: string) => string): BreadcrumbItem[] { - const segments = pathname.replace(/^\/|\/$/g, '').split('/'); + const segments = pathname.replace(/^\/|\/$|\.html$/g, '').split('/'); // Need at least lang + one content segment if (segments.length < 2) return [];