From 2427c7c29cbed3a736b10d40a445de7d7b040b5c Mon Sep 17 00:00:00 2001 From: krzysdz Date: Fri, 29 May 2026 01:30:08 +0200 Subject: [PATCH 1/8] Switch from directory to file format in Astro The "file" format generates "name.html" files instead of default "name/index.html". This would make old links work, even without redirects on Cloudflare level, while still allowing the current link format with no extension. https://docs.astro.build/en/reference/configuration-reference/#buildformat --- astro.config.mjs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/astro.config.mjs b/astro.config.mjs index 46edff574c..9b99fb67ad 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -23,6 +23,9 @@ const site = NETLIFY_PREVIEW_SITE || 'https://expressjs.com'; export default defineConfig({ redirects, site, + build: { + format: 'file', + }, markdown: { // Link localization (Markdown links + raw HTML/JSX ``). Configuration — // localized sections, versioned sections, default version, and the "global" pages From f6ab6ed3dc3b2ea8b20f1c388652ec2918cbe054 Mon Sep 17 00:00:00 2001 From: krzysdz Date: Fri, 29 May 2026 01:53:39 +0200 Subject: [PATCH 2/8] Fix sidebar --- src/components/patterns/Sidebar/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 { From 1ca023cd1f42afe793cc685bb287ca36a6cc7e6f Mon Sep 17 00:00:00 2001 From: krzysdz Date: Fri, 29 May 2026 02:16:24 +0200 Subject: [PATCH 3/8] Fix language switching on home page --- src/i18n/utils.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) 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}`; } } From 720ddfce484b94e02743ce7e4ba266526c4f9318 Mon Sep 17 00:00:00 2001 From: krzysdz Date: Fri, 29 May 2026 02:52:27 +0200 Subject: [PATCH 4/8] Make link checker recognize the new structure --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b9c9a82e86..c14d645b1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ on: # Cancel in progress workflows # in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run concurrency: - group: '${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' + group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" cancel-in-progress: true permissions: @@ -36,8 +36,8 @@ jobs: - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version-file: '.nvmrc' - cache: 'npm' + node-version-file: ".nvmrc" + cache: "npm" - name: Install Node.js dependencies run: npm ci @@ -97,11 +97,11 @@ jobs: with: args: | --root-dir $PWD/dist + --fallback-extensions html --exclude-path dist/llms.txt --exclude-path dist/llms/ --exclude-private --exclude-loopback - --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 From f609fecb26427e77a5c8ec266e81b25141dc3926 Mon Sep 17 00:00:00 2001 From: krzysdz Date: Fri, 29 May 2026 14:55:08 +0200 Subject: [PATCH 5/8] Fix canonical and OG URLs --- src/layouts/Layout.astro | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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); From 9cb245cb31647ae9761f2651b7cbffa62692be8f Mon Sep 17 00:00:00 2001 From: krzysdz Date: Fri, 29 May 2026 15:00:38 +0200 Subject: [PATCH 6/8] Fix breadcrumbs --- src/utils/content.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 []; From d169078b59719c37532ad532c2ae1fe397e96793 Mon Sep 17 00:00:00 2001 From: krzysdz Date: Fri, 29 May 2026 23:19:21 +0200 Subject: [PATCH 7/8] Update 404 page pattern in exclusion list --- .lycheeignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.lycheeignore b/.lycheeignore index 96201a040e..ad2172845e 100644 --- a/.lycheeignore +++ b/.lycheeignore @@ -5,7 +5,7 @@ ^https://github\.com/ # Exclude 404 pages -dist/(.*/)?404((/index)?\.html|/)$ +dist/(.*/)?404((/index)?\.html|/)?$ # Exclude Open Collective links ^https://opencollective\.com/ From dfb020632743300d6840b425299aa114dc64de92 Mon Sep 17 00:00:00 2001 From: krzysdz Date: Wed, 17 Jun 2026 01:19:42 +0200 Subject: [PATCH 8/8] fixup! Make link checker recognize the new structure --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c14d645b1d..d8580b8542 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ on: # Cancel in progress workflows # in the scenario where we already had a run going for that PR/branch/tag but then triggered a new run concurrency: - group: "${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}" + group: '${{ github.workflow }} ✨ ${{ github.event.pull_request.head.label || github.head_ref || github.ref }}' cancel-in-progress: true permissions: @@ -36,8 +36,8 @@ jobs: - name: Set up Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version-file: ".nvmrc" - cache: "npm" + node-version-file: '.nvmrc' + cache: 'npm' - name: Install Node.js dependencies run: npm ci