this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Enforce title from original page

+75 -3
+2 -2
src/page.ts
··· 5 5 import { WeightedDiGraph, KruskalMST, Edge } from 'js-graph-algorithms'; 6 6 import { extractContent, extractLinks, parseBody } from "./dom"; 7 7 import { fetchHtml } from "./fetch"; 8 - import { htmlToMarkdown, sanitizeHtml } from "./unified"; 8 + import { htmlToMarkdown, sanitizeHtml, transferTitle } from "./unified"; 9 9 import { rewriteMarkdown } from './rewrite'; 10 10 import { makeCacheFileHelper } from './path'; 11 11 ··· 143 143 const markdown = await extractContentToMarkdown(this.url, html); 144 144 if (!markdown) return (this.#content = null); 145 145 const rewritten = await rewriteMarkdown(this.url, markdown); 146 - return (this.#content = rewritten); 146 + return (this.#content = await transferTitle(markdown, rewritten)); 147 147 } 148 148 } 149 149
+7
src/types.d.ts
··· 1 + declare module 'remark-title' { 2 + interface TitleOptions { 3 + title: string; 4 + } 5 + export default function remarkTitle(opts: TitleOptions): (tree: Root) => undefined; 6 + export type Root = import('mdast').Root; 7 + }
+66 -1
src/unified.ts
··· 1 - import type { List, ListItem, Root } from 'mdast'; 1 + import type { List, ListItem, PhrasingContent, Root } from 'mdast'; 2 2 import { unified } from 'unified'; 3 3 import { visit } from 'unist-util-visit'; 4 4 import { defaultSchema as defaultSanitizeSchema } from 'hast-util-sanitize'; ··· 12 12 import remarkGfm from 'remark-gfm'; 13 13 import remarkParse from 'remark-parse'; 14 14 import remarkSqueezeParagraphs from 'remark-squeeze-paragraphs'; 15 + import remarkTitle from 'remark-title'; 15 16 16 17 export async function sanitizeHtml(html: string): Promise<string> { 17 18 const vfile = await unified() ··· 158 159 ); 159 160 return md.toString(); 160 161 } 162 + 163 + function extractTitle(markdown: string): string | null { 164 + let depth: number | null = null; 165 + let title: string | null = null; 166 + const toString = (nodes: PhrasingContent[]): string => 167 + nodes.map((node) => { 168 + switch (node.type) { 169 + case 'break': 170 + return '\n'; 171 + case 'delete': 172 + case 'emphasis': 173 + case 'link': 174 + case 'strong': 175 + return toString(node.children); 176 + case 'inlineCode': 177 + return `\`${node.value}\``; 178 + case 'text': 179 + return node.value; 180 + case 'footnoteReference': 181 + case 'html': 182 + case 'image': 183 + case 'imageReference': 184 + case 'linkReference': 185 + default: 186 + return ''; 187 + } 188 + }).join(''); 189 + const tree = unified() 190 + .use(remarkParse, { fragment: true }) 191 + .use(remarkGfm, { 192 + tablePipeAlign: false, 193 + tableCellPadding: false, 194 + }) 195 + .parse(markdown); 196 + visit(tree, function (node) { 197 + if (node.type !== 'heading') 198 + return; 199 + if (!depth || node.depth < depth) 200 + title = toString(node.children); 201 + }); 202 + return title; 203 + } 204 + 205 + export async function transferTitle(from: string, to: string): Promise<string> { 206 + const title = extractTitle(from); 207 + if (!title) return to; 208 + const md = await unified() 209 + .use(remarkParse, { fragment: true }) 210 + .use(remarkGfm, { 211 + tablePipeAlign: false, 212 + tableCellPadding: false, 213 + }) 214 + .use(remarkTitle, { 215 + title, 216 + }) 217 + .use(remarkStringify, { 218 + bullet: '-', 219 + incrementListMarker: false, 220 + ruleSpaces: false, 221 + tightDefinitions: true, 222 + }) 223 + .process(to); 224 + return md.toString(); 225 + }