Skip to content

Commit 8b9a66f

Browse files
committed
Automatically discover meta description
1 parent 9336ee4 commit 8b9a66f

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

src/core/text.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,19 @@ export function formatLineRange(
4747
lines: result,
4848
};
4949
}
50+
51+
export function truncateText(text: string, length: number) {
52+
if (text.length < length) {
53+
return text;
54+
} else {
55+
// Since we'll insert elips, trim an extra space
56+
const clipLength = length - 1;
57+
const clipped = text.substring(0, clipLength);
58+
const lastSpace = clipped.lastIndexOf(" ");
59+
if (lastSpace > 0) {
60+
return clipped.substring(0, lastSpace) + "…";
61+
} else {
62+
return clipped + "…";
63+
}
64+
}
65+
}

src/project/types/website/util/discover-meta.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,19 @@ const kMdPreviewClassRegex = RegExp(kPreviewClassPattern);
2727
const kMdNamedImageRegex = RegExp(kMdNamedImagePattern);
2828
const kMarkdownImg = /!\[[^\]]*\]\((.*?)(?:\".*\")?\)(?:\{(?:[^\|]*)\})?/;
2929

30+
export function findDescription(doc: Document): string | undefined {
31+
const paras = doc.querySelectorAll(
32+
"main.content > p, main.content > section > p",
33+
);
34+
for (const para of paras) {
35+
const paraEl = para as Element;
36+
if (paraEl.innerText) {
37+
return paraEl.innerText;
38+
}
39+
}
40+
return undefined;
41+
}
42+
3043
export function findPreviewImg(
3144
doc: Document,
3245
): string | undefined {

src/project/types/website/website-meta.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
createMarkdownPipeline,
3737
MarkdownPipeline,
3838
} from "./website-pipeline-md.ts";
39-
import { findPreviewImg } from "./util/discover-meta.ts";
39+
import { findDescription, findPreviewImg } from "./util/discover-meta.ts";
4040
import { isAbsoluteRef } from "../../../core/http.ts";
4141
import {
4242
kHtmlEmptyPostProcessResult,
@@ -45,6 +45,7 @@ import { HtmlPostProcessResult } from "../../../command/render/types.ts";
4545
import { imageSize } from "../../../core/image.ts";
4646
import { writeMetaTag } from "../../../format/html/format-html-shared.ts";
4747
import { joinUrl } from "../../../core/url.ts";
48+
import { truncateText } from "../../../core/text.ts";
4849

4950
const kCard = "card";
5051

@@ -53,6 +54,7 @@ interface SocialMetadataProvider {
5354
prefix: string;
5455
metadata: Metadata;
5556
filter?: (key: string) => string;
57+
resolveValue?: (key: string, value: string) => string;
5658
resolveDefaults?: (finalMetadata: Metadata) => void;
5759
}
5860

@@ -101,6 +103,14 @@ export function metadataHtmlPostProcessor(
101103
}
102104
return key;
103105
},
106+
resolveValue: (key: string, value: string) => {
107+
// Limit to 300 chars for Open Graph
108+
if ([kDescription].includes(key)) {
109+
return truncateText(value, 300);
110+
}
111+
112+
return value;
113+
},
104114
};
105115

106116
// The twitter card provider
@@ -122,6 +132,14 @@ export function metadataHtmlPostProcessor(
122132

123133
return key;
124134
},
135+
resolveValue: (key: string, value: string) => {
136+
// Limit to 200 chars for Twitter
137+
if ([kDescription].includes(key)) {
138+
return truncateText(value, 200);
139+
}
140+
141+
return value;
142+
},
125143
resolveDefaults: (finalMetadata: Metadata) => {
126144
if (finalMetadata[kCardStyle] === undefined) {
127145
finalMetadata[kCardStyle] = finalMetadata[kImage]
@@ -156,6 +174,11 @@ export function metadataHtmlPostProcessor(
156174
metadata[kImage] = findPreviewImg(doc);
157175
}
158176

177+
// cook up a description if one is not provided
178+
if (metadata[kDescription] === undefined) {
179+
metadata[kDescription] = findDescription(doc);
180+
}
181+
159182
// Convert image to absolute href and add height and width
160183
resolveImageMetadata(source, project, format, metadata);
161184

@@ -167,11 +190,18 @@ export function metadataHtmlPostProcessor(
167190
// Append the metadata
168191
Object.keys(metadata).forEach((key) => {
169192
if (metadata[key] !== undefined) {
193+
// Resolve the value
170194
const data = metadata[key] as string;
195+
const value = provider.resolveValue
196+
? provider.resolveValue(key, data)
197+
: data;
198+
199+
// Filter the key
171200
if (provider.filter) {
172201
key = provider.filter(key);
173202
}
174-
writeMetaTag(`${provider.prefix}:${key}`, data, doc);
203+
204+
writeMetaTag(`${provider.prefix}:${key}`, value, doc);
175205
}
176206
});
177207
});

0 commit comments

Comments
 (0)