Skip to content

Commit ad5daca

Browse files
committed
feat: improve SEO
1 parent 154bbac commit ad5daca

File tree

6 files changed

+1155
-737
lines changed

6 files changed

+1155
-737
lines changed

apps/app/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
"react-dom": "18.2.0",
2929
"react-focus-lock": "2.9.2",
3030
"rehype-prism-plus": "1.5.0",
31+
"remark": "14.0.2",
32+
"strip-markdown": "5.0.0",
3133
"tailwind-merge": "1.8.1"
3234
},
3335
"devDependencies": {

apps/app/src/app/(main-layout)/questions/p/[questionId]/head.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { HeadTags } from "../../../../../components/HeadTags";
2+
import { stripMarkdown } from "../../../../../lib/markdown";
23
import { getQuestionById } from "../../../../../services/questions.service";
34
import { Params } from "../../../../../types";
45

@@ -15,5 +16,8 @@ export default async function Head({ params }: { params: Params<"questionId"> })
1516
id: questionId,
1617
});
1718

18-
return <HeadTags title={data.question} />;
19+
const textForTitle = await stripMarkdown(data.question, { stripCode: true });
20+
const textForDescription = await stripMarkdown(data.question, { stripCode: false });
21+
22+
return <HeadTags title={textForTitle} description={textForDescription} />;
1923
}

apps/app/src/components/HeadTags.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1+
import { hellip } from "../utils/utils";
2+
13
type HeadTagsProps = Readonly<{
24
title?: string;
35
description?: string;
46
}>;
57

8+
const titleSuffix = ` • DevFAQ.pl`;
9+
const maxTitleLength = 60 - titleSuffix.length;
10+
const maxDescriptionLength = 160;
11+
612
export const HeadTags = ({
713
title = "",
814
description = "DevFAQ.pl — największa baza pytań z programowania tworzona przez społeczność. DevFAQ.pl jest serwisem internetowym służącym do udostępniania i wymiany pytań rekrutacyjnych na stanowiska developerów.",
915
}: HeadTagsProps) => {
10-
const formattedTitle = title.trim() ? `${title} • DevFAQ.pl` : `DevFAQ.pl`;
16+
const shortTitle = hellip(title, maxTitleLength);
17+
const shortDescription = hellip(description, maxDescriptionLength);
18+
19+
const formattedShortTitle = shortTitle.trim() ? `${shortTitle}${titleSuffix}` : `DevFAQ.pl`;
20+
1121
return (
1222
<>
13-
<title>{formattedTitle}</title>
14-
<meta name="description" content={description} />
23+
<title>{formattedShortTitle}</title>
24+
<meta name="description" itemProp="description" content={description} />
1525
<meta name="viewport" content="width=device-width, initial-scale=1" />
1626
<meta property="og:type" content="website" />
1727
<meta
@@ -35,8 +45,8 @@ export const HeadTags = ({
3545
<meta name="format-detection" content="telephone=no" />
3646
<meta name="apple-mobile-web-app-title" content="DevFAQ.pl" />
3747

38-
<meta property="og:title" itemProp="title name" content={formattedTitle} />
39-
<meta property="og:description" itemProp="description" content={description} />
48+
<meta property="og:title" itemProp="title name" content={formattedShortTitle} />
49+
<meta property="og:description" content={shortDescription} />
4050
</>
4151
);
4252
};

apps/app/src/lib/markdown.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,32 @@
11
import rehypePrism from "rehype-prism-plus";
2+
import strip from "strip-markdown";
3+
24
import { serialize } from "next-mdx-remote/serialize";
5+
import { remark } from "remark";
36

47
export const serializeSource = (source: string) =>
58
serialize(source, {
69
mdxOptions: {
710
rehypePlugins: [rehypePrism],
11+
jsx: false,
812
format: "md",
913
},
1014
});
15+
16+
export const stripMarkdown = async (source: string, { stripCode }: { stripCode: boolean }) => {
17+
const codeRegex = /(?:\n|^)```\w*\n(.*?)\n```(?:\n|$)/gs;
18+
19+
try {
20+
const vfile = await remark()
21+
.use(strip, { keep: ["code"] })
22+
.process(source);
23+
return String(vfile)
24+
.replaceAll(codeRegex, stripCode ? "\n" : "\n$1\n")
25+
.replaceAll(/\s+/g, " ")
26+
.replaceAll(/\\/g, "")
27+
.trim();
28+
} catch (err) {
29+
console.error(err);
30+
return "";
31+
}
32+
};

apps/app/src/utils/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,26 @@
11
export const range = (start: number, end: number) =>
22
Array.from({ length: end - start }, (_, idx) => idx + start);
3+
4+
export const hellip = (text: string, maxLength: number) => {
5+
text = text.trim();
6+
7+
if (text.length <= maxLength) {
8+
return text;
9+
}
10+
const partText = text.split(" ").reduce(
11+
(acc, word) => {
12+
if (acc.done) {
13+
return acc;
14+
}
15+
16+
const newResult = acc.result + " " + word;
17+
if (newResult.length >= maxLength) {
18+
return { done: true, result: acc.result };
19+
}
20+
return { done: false, result: newResult };
21+
},
22+
{ done: false, result: "" },
23+
);
24+
25+
return partText.result.trim() + "…";
26+
};

0 commit comments

Comments
 (0)