v4: what the server and the client both state is declared once — several rules were written out in two or more files, some already drifting apart. A link's site name (its host without www.) was computed in five places, now shared/url.ts's siteHost; dumpFileVersion, which the frontend and the server had to keep equal by hand, is one function in shared/dump.ts; the Markdown flattening of the chat's reply previews and the digest's quotes is shared/markdown.ts's markdownToPlainText, so the digest also keeps an image's alt text; an attachment's URL and how to find one in a text are shared/attachment.ts, which the editor, Markdown and attachment-service read, its id pattern the new UUID_RE that the file and thumbnail routes check ids with. On the server, the Bandcamp, YouTube playlist and link preview caches use one TtlCache (api/lib/ttl-cache.ts) instead of three copies of the same expiry and eviction; the link previews' theme-color and the emails' theme tokens read colours with one parser (api/lib/css-color.ts), and a transparent theme-color now gives no tint; the WebSocket route sends through ws-service's send, the welcome included, so it is type-checked; fetch.ts strips an IPv6 literal's brackets with public-address's bareHostname; and a category's slug is checked by the same validateSlug as a dump's, with its own length. The migrations keep their frozen copies, as they document. 272 backend tests and 677 frontend tests pass, and deno check, tsc, eslint and doc:lint are clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
28
shared/markdown.ts
Normal file
28
shared/markdown.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* Flattens the Markdown users write into one line of plain text, where a
|
||||
* quote of it can't render markup: the chat's reply previews and the digest
|
||||
* email's quotes of a comment or a note.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the Markdown `md` as one line of plain text. Images become their alt
|
||||
* text, or `imageLabel` without one, and links their text. It also takes a
|
||||
* body cut short, possibly inside a link, as the server cuts a chat message's
|
||||
* `replyToBody`: the unfinished link then shows as its text, without half a
|
||||
* URL.
|
||||
*/
|
||||
export function markdownToPlainText(md: string, imageLabel: string): string {
|
||||
return md
|
||||
// An image or link cut short at the end loses its unfinished target.
|
||||
.replace(/!\[([^\]]*)\]\([^)]*$/, (_, alt: string) => alt || imageLabel)
|
||||
.replace(/(^|[^!])\[([^\]]*)\]\([^)]*$/, "$1$2")
|
||||
.replace(/!\[[^\]]*$/, "")
|
||||
.replace(/!\[([^\]]*)\]\([^)]*\)/g, (_, alt: string) => alt || imageLabel)
|
||||
.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1")
|
||||
.replace(/^\s{0,3}(#{1,6}|>|[-*+]|\d+\.)\s+/gm, "")
|
||||
.replace(/```\w*|\*\*|__|~~|[*`]/g, "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
}
|
||||
Reference in New Issue
Block a user