Skip to content

Commit 0fb0fa9

Browse files
authored
fix: avoid leading double slash (#8846)
* fix(client): prefer useLocale() over useParams() * fix(cloud-function): normalize encoded leading slash
1 parent 77df4fc commit 0fb0fa9

File tree

14 files changed

+43
-32
lines changed

14 files changed

+43
-32
lines changed

Diff for: client/src/contributor-spotlight/index.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { GetInvolved } from "../ui/molecules/get_involved";
88
import { Quote } from "../ui/molecules/quote";
99

1010
import "./index.scss";
11+
import { useLocale } from "../hooks";
1112

1213
type ContributorDetails = {
1314
sections: [string];
@@ -23,7 +24,8 @@ type ContributorDetails = {
2324
};
2425

2526
export function ContributorSpotlight(props: HydrationData<ContributorDetails>) {
26-
const { "*": slug, locale = "en-US" } = useParams();
27+
const locale = useLocale();
28+
const { "*": slug } = useParams();
2729
const baseURL = `/${locale.toLowerCase()}/community/spotlight/${slug}`;
2830
const contributorJSONUrl = `${baseURL}/index.json`;
2931

Diff for: client/src/document/hooks.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { useEffect, useRef, useState } from "react";
22
import { useLocation, useParams } from "react-router-dom";
3-
import { useIsServer } from "../hooks";
3+
import { useIsServer, useLocale } from "../hooks";
44
import { Doc } from "../../../libs/types/document";
55

66
export function useDocumentURL() {
7-
const { "*": slug, locale } = useParams();
7+
const locale = useLocale();
8+
const { "*": slug } = useParams();
89
const url = `/${locale}/docs/${slug}`;
910
// If you're in local development Express will force the trailing /
1011
// on any URL. We can't keep that if we're going to compare the current

Diff for: client/src/document/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React from "react";
2-
import { useSearchParams, useParams, useNavigate } from "react-router-dom";
2+
import { useSearchParams, useNavigate } from "react-router-dom";
33
import useSWR, { mutate } from "swr";
44

55
import { CRUD_MODE, PLACEMENT_ENABLED } from "../env";
66
import { useGA } from "../ga-context";
7-
import { useIsServer } from "../hooks";
7+
import { useIsServer, useLocale } from "../hooks";
88

99
import { useDocumentURL, useCopyExamplesToClipboard } from "./hooks";
1010
import { Doc } from "../../../libs/types/document";
@@ -65,7 +65,7 @@ export function Document(props /* TODO: define a TS interface for this */) {
6565

6666
const mountCounter = React.useRef(0);
6767
const documentURL = useDocumentURL();
68-
const { locale = "en-US" } = useParams();
68+
const locale = useLocale();
6969
const [searchParams] = useSearchParams();
7070

7171
const navigate = useNavigate();

Diff for: client/src/document/toolbar/edit-actions.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { CRUD_MODE_HOSTNAMES } from "../../env";
44
import { Source } from "../../../../libs/types/document";
55

66
import "./edit-actions.scss";
7+
import { useLocale } from "../../hooks";
78

89
export function EditActions({ source }: { source: Source }) {
910
const { folder, filename, github_url } = source;
@@ -50,7 +51,8 @@ export function EditActions({ source }: { source: Source }) {
5051
}
5152
}
5253

53-
const { locale = "en-US", "*": slug } = useParams();
54+
const locale = useLocale();
55+
const { "*": slug } = useParams();
5456

5557
if (!folder) {
5658
return null;

Diff for: client/src/flaws/index.tsx

+3-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import React, { useCallback, useEffect, useMemo, useState } from "react";
2-
import {
3-
createSearchParams,
4-
Link,
5-
useParams,
6-
useSearchParams,
7-
} from "react-router-dom";
2+
import { createSearchParams, Link, useSearchParams } from "react-router-dom";
83
import useSWR from "swr";
94

105
import "./index.scss";
116

127
import { humanizeFlawName } from "../flaw-utils";
138
import { MainContentContainer } from "../ui/atoms/page-content";
9+
import { useLocale } from "../hooks";
1410

1511
interface DocumentPopularity {
1612
value: number;
@@ -156,7 +152,7 @@ function useFiltersURL(): [Filters, (filters: Partial<Filters>) => void] {
156152
}
157153

158154
export default function AllFlaws() {
159-
const { locale = "en-US" } = useParams();
155+
const locale = useLocale();
160156
const [filters] = useFiltersURL();
161157
const [lastData, setLastData] = useState<Data | null>(null);
162158

Diff for: client/src/page-not-found/fallback-link.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import React from "react";
2-
import { useLocation, useParams } from "react-router-dom";
2+
import { useLocation } from "react-router-dom";
33
import useSWR from "swr";
44

55
import { Doc } from "../../../libs/types";
66
import NoteCard from "../ui/molecules/notecards";
77

88
import LANGUAGES_RAW from "../../../libs/languages";
99
import { RETIRED_LOCALES } from "../../../libs/constants";
10+
import { useLocale } from "../hooks";
1011

1112
const LANGUAGES = new Map(
1213
Object.entries(LANGUAGES_RAW).map(([locale, data]) => {
@@ -22,7 +23,7 @@ const LANGUAGES = new Map(
2223
// like "Did you mean: <a href=$url>$doctitle</a>?"
2324

2425
export default function FallbackLink({ url }: { url: string }) {
25-
const { locale = "en-US" } = useParams();
26+
const locale = useLocale();
2627
const location = useLocation();
2728

2829
const [fallbackCheckURL, setFallbackCheckURL] = React.useState<null | string>(

Diff for: client/src/plus/plus-docs/index.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { MDN_PLUS_TITLE } from "../../constants";
33
import StaticPage from "../../homepage/static-page";
44
import { useUserData } from "../../user-context";
55
import "./index.scss";
6+
import { useLocale } from "../../hooks";
67

78
function PlusDocsNav() {
89
const userData = useUserData();
@@ -46,7 +47,7 @@ function RelatedTopics({
4647
heading: string;
4748
items: { slug: string; title: string }[];
4849
}) {
49-
const { locale = "en-US" } = useParams();
50+
const locale = useLocale();
5051
const { pathname: locationPathname } = useLocation();
5152

5253
return (
@@ -81,7 +82,8 @@ function RelatedTopics({
8182
}
8283

8384
function PlusDocs({ ...props }) {
84-
const { locale = "en-US", "*": slug } = useParams();
85+
const locale = useLocale();
86+
const { "*": slug } = useParams();
8587

8688
return (
8789
<StaticPage

Diff for: client/src/search.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useMemo, useRef, useState } from "react";
2-
import { useNavigate, useParams } from "react-router-dom";
2+
import { useNavigate } from "react-router-dom";
33
import { useCombobox } from "downshift";
44
import useSWR from "swr";
55

@@ -52,7 +52,7 @@ function useSearchIndex(): readonly [
5252
const [shouldInitialize, setShouldInitialize] = useState(false);
5353
const [searchIndex, setSearchIndex] = useState<null | SearchIndex>(null);
5454
// Default to 'en-US' if you're on the home page without the locale prefix.
55-
const { locale = "en-US" } = useParams();
55+
const locale = useLocale();
5656

5757
const url = `/${locale}/search-index.json`;
5858

Diff for: client/src/translations/dashboard/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ import React from "react";
22
import {
33
createSearchParams,
44
Link,
5-
useParams,
65
useSearchParams,
76
useNavigate,
87
} from "react-router-dom";
98
import useSWR from "swr";
109

1110
import { MainContentContainer } from "../../ui/atoms/page-content";
11+
import { useLocale } from "../../hooks";
1212

1313
interface Data {
1414
l10nKPIs: L10nKPIs;
@@ -89,7 +89,7 @@ function getStorage(locale: string): LocaleStorageData | null {
8989
}
9090

9191
export function TranslationDashboard() {
92-
const { locale = "en-US" } = useParams();
92+
const locale = useLocale();
9393
const [searchParams] = useSearchParams();
9494
const navigate = useNavigate();
9595

Diff for: client/src/translations/differences/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from "react";
22
import {
33
createSearchParams,
44
Link,
5-
useParams,
65
useSearchParams,
76
useNavigate,
87
} from "react-router-dom";
@@ -13,6 +12,7 @@ import relativeTime from "dayjs/plugin/relativeTime";
1312
import "./index.scss";
1413

1514
import { MainContentContainer } from "../../ui/atoms/page-content";
15+
import { useLocale } from "../../hooks";
1616

1717
dayjs.extend(relativeTime);
1818

@@ -107,7 +107,7 @@ function getStorage(locale: string): LocaleStorageData | null {
107107
}
108108

109109
export function TranslationDifferences() {
110-
const { locale = "en-US" } = useParams();
110+
const locale = useLocale();
111111
const [searchParams] = useSearchParams();
112112
const navigate = useNavigate();
113113

Diff for: client/src/translations/index.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from "react";
2-
import { Link, useParams, Routes, Route } from "react-router-dom";
2+
import { Link, Routes, Route } from "react-router-dom";
33
import useSWR from "swr";
44

55
import "./index.scss";
@@ -10,6 +10,7 @@ import { MainContentContainer } from "../ui/atoms/page-content";
1010
import { TranslationDifferences } from "./differences";
1111
import { MissingTranslations } from "./missing";
1212
import { TranslationDashboard } from "./dashboard";
13+
import { useLocale } from "../hooks";
1314

1415
interface Locale {
1516
locale: string;
@@ -39,7 +40,7 @@ export default function Translations() {
3940
}
4041

4142
function PickLocale() {
42-
const { locale = "en-US" } = useParams();
43+
const locale = useLocale();
4344
React.useEffect(() => {
4445
let title = "All translations";
4546
if (locale.toLowerCase() !== "en-us") {

Diff for: client/src/translations/missing/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import React from "react";
22
import {
33
createSearchParams,
44
Link,
5-
useParams,
65
useSearchParams,
76
useNavigate,
87
} from "react-router-dom";
@@ -11,6 +10,7 @@ import dayjs from "dayjs";
1110
import relativeTime from "dayjs/plugin/relativeTime";
1211

1312
import { MainContentContainer } from "../../ui/atoms/page-content";
13+
import { useLocale } from "../../hooks";
1414

1515
dayjs.extend(relativeTime);
1616

@@ -104,7 +104,7 @@ function getStorage(locale: string): LocaleStorageData | null {
104104
}
105105

106106
export function MissingTranslations() {
107-
const { locale = "en-US" } = useParams();
107+
const locale = useLocale();
108108
const [searchParams] = useSearchParams();
109109
const navigate = useNavigate();
110110

Diff for: client/src/ui/organisms/article-actions/language-menu/index.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState } from "react";
2-
import { useLocation, useNavigate, useParams } from "react-router-dom";
2+
import { useLocation, useNavigate } from "react-router-dom";
33

44
import { useGA } from "../../../../ga-context";
55
import { Translation } from "../../../../../../libs/types/document";
@@ -8,6 +8,7 @@ import { Submenu } from "../../../molecules/submenu";
88

99
import "./index.scss";
1010
import { DropdownMenu, DropdownMenuWrapper } from "../../../molecules/dropdown";
11+
import { useLocale } from "../../../../hooks";
1112

1213
// This needs to match what's set in 'libs/constants.js' on the server/builder!
1314
const PREFERRED_LOCALE_COOKIE_NAME = "preferredlocale";
@@ -25,7 +26,7 @@ export function LanguageMenu({
2526
const ga = useGA();
2627
const { pathname } = useLocation();
2728
const navigate = useNavigate();
28-
const { locale = "en-US" } = useParams();
29+
const locale = useLocale();
2930
const [isOpen, setIsOpen] = useState<boolean>(false);
3031

3132
function translateURL(destinationLocale: string) {

Diff for: cloud-function/src/middlewares/redirect-leading-slash.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,14 @@ export async function redirectLeadingSlash(
2020
next: NextFunction
2121
) {
2222
const pathname = req.url;
23-
if (pathname.startsWith("//")) {
24-
return redirect(res, pathname.replace(/^\/+/g, "/"));
23+
const normalizedPathname = normalizeLeadingSlash(pathname);
24+
if (pathname !== normalizedPathname) {
25+
return redirect(res, normalizedPathname);
2526
}
2627

2728
next();
2829
}
30+
31+
function normalizeLeadingSlash(pathname: string): string {
32+
return pathname.replace(/^(\/|%2f)+/i, "/");
33+
}

0 commit comments

Comments
 (0)