implement fourth age of posting

adds proper linebreaks to posts after 2023-05-10T15:00:00-04:00

https://cohost.org/staff/post/1477549-cohost-patch-notes-v
This commit is contained in:
Jill 2023-05-18 06:03:26 +03:00
parent c1ebc92daf
commit d4710c3392
Signed by: oat
GPG Key ID: 33489AA58A955108
7 changed files with 94 additions and 20 deletions

47
package-lock.json generated
View File

@ -21,6 +21,7 @@
"rehype-raw": "^6.1.1", "rehype-raw": "^6.1.1",
"rehype-sanitize": "^5.0.1", "rehype-sanitize": "^5.0.1",
"rehype-stringify": "^9.0.3", "rehype-stringify": "^9.0.3",
"remark-breaks": "^3.0.3",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-parse": "^10.0.1", "remark-parse": "^10.0.1",
"remark-rehype": "^10.1.0", "remark-rehype": "^10.1.0",
@ -2778,6 +2779,19 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/mdast-util-newline-to-break": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-1.0.0.tgz",
"integrity": "sha512-491LcYv3gbGhhCrLoeALncQmega2xPh+m3gbsIhVsOX4sw85+ShLFPvPyibxc1Swx/6GtzxgVodq+cGa/47ULg==",
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-find-and-replace": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdast-util-phrasing": { "node_modules/mdast-util-phrasing": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz",
@ -4121,6 +4135,20 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/remark-breaks": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-3.0.3.tgz",
"integrity": "sha512-C7VkvcUp1TPUc2eAYzsPdaUh8Xj4FSbQnYA5A9f80diApLZscTDeG7efiWP65W8hV2sEy3JuGVU0i6qr5D8Hug==",
"dependencies": {
"@types/mdast": "^3.0.0",
"mdast-util-newline-to-break": "^1.0.0",
"unified": "^10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-gfm": { "node_modules/remark-gfm": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz",
@ -7084,6 +7112,15 @@
"mdast-util-to-markdown": "^1.3.0" "mdast-util-to-markdown": "^1.3.0"
} }
}, },
"mdast-util-newline-to-break": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-1.0.0.tgz",
"integrity": "sha512-491LcYv3gbGhhCrLoeALncQmega2xPh+m3gbsIhVsOX4sw85+ShLFPvPyibxc1Swx/6GtzxgVodq+cGa/47ULg==",
"requires": {
"@types/mdast": "^3.0.0",
"mdast-util-find-and-replace": "^2.0.0"
}
},
"mdast-util-phrasing": { "mdast-util-phrasing": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz", "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-3.0.1.tgz",
@ -7944,6 +7981,16 @@
"unified": "^10.0.0" "unified": "^10.0.0"
} }
}, },
"remark-breaks": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-3.0.3.tgz",
"integrity": "sha512-C7VkvcUp1TPUc2eAYzsPdaUh8Xj4FSbQnYA5A9f80diApLZscTDeG7efiWP65W8hV2sEy3JuGVU0i6qr5D8Hug==",
"requires": {
"@types/mdast": "^3.0.0",
"mdast-util-newline-to-break": "^1.0.0",
"unified": "^10.0.0"
}
},
"remark-gfm": { "remark-gfm": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-3.0.1.tgz",

View File

@ -36,6 +36,7 @@
"rehype-raw": "^6.1.1", "rehype-raw": "^6.1.1",
"rehype-sanitize": "^5.0.1", "rehype-sanitize": "^5.0.1",
"rehype-stringify": "^9.0.3", "rehype-stringify": "^9.0.3",
"remark-breaks": "^3.0.3",
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"remark-parse": "^10.0.1", "remark-parse": "^10.0.1",
"remark-rehype": "^10.1.0", "remark-rehype": "^10.1.0",

View File

@ -140,7 +140,7 @@
</a> </a>
</div> </div>
<div class="comment-text"> <div class="comment-text">
{@html renderCommentMarkdown(data.comment.body)} {@html renderCommentMarkdown(data.comment.body, new Date(data.comment.postedAtISO))}
</div> </div>
</div> </div>
</article> </article>

View File

@ -9,6 +9,7 @@ import remarkRehype from 'remark-rehype';
import rehypeHighlight from 'rehype-highlight' import rehypeHighlight from 'rehype-highlight'
import rehypeStringify from 'rehype-stringify'; import rehypeStringify from 'rehype-stringify';
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize'; import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
import remarkBreaks from 'remark-breaks';
import glsl from 'highlight.js/lib/languages/glsl' import glsl from 'highlight.js/lib/languages/glsl'
import deepmerge from 'deepmerge'; import deepmerge from 'deepmerge';
import { compile } from 'html-to-text'; import { compile } from 'html-to-text';
@ -17,6 +18,11 @@ const convert = compile({
wordwrap: false, wordwrap: false,
}); });
// todo: convert to cohost-like age ruleset system?
// previous age schemas didn't really affect anything since they were exclusively
// sanitization related, but fourth age adds linebreaks, so now i have to
// actually bother with it
const THIRD_AGE_SCHEMA = deepmerge(defaultSchema, { const THIRD_AGE_SCHEMA = deepmerge(defaultSchema, {
attributes: { attributes: {
"*": ["style"], "*": ["style"],
@ -24,15 +30,24 @@ const THIRD_AGE_SCHEMA = deepmerge(defaultSchema, {
tagNames: ["video", "audio", "aside"], // consistency with current rules, tagNames: ["video", "audio", "aside"], // consistency with current rules,
}); });
const LINEBREAK_CUTOFF = new Date("2023-05-10T15:00:00-04:00");
const externalRel = ['nofollow', 'noopener', 'noreferrer']; const externalRel = ['nofollow', 'noopener', 'noreferrer'];
/** /**
* @param {string} src * @param {string} src
* @param {boolean} [xhtml] * @param {boolean} [xhtml]
* @param {Date} [date]
*/ */
export function renderPostMarkdown(src, xhtml) { export function renderPostMarkdown(src, xhtml, date) {
return unified() let stack = unified()
.use(remarkParse) .use(remarkParse);
if ((date || new Date()) > LINEBREAK_CUTOFF) {
stack = stack.use(remarkBreaks);
}
return stack
.use(remarkGfm, { .use(remarkGfm, {
singleTilde: false, singleTilde: false,
}) })
@ -71,8 +86,9 @@ export function renderPostMarkdown(src, xhtml) {
/** /**
* @param {StorageBlock[]} blocks * @param {StorageBlock[]} blocks
* @param {Date} [date]
*/ */
export function renderPostSummaryMarkdown(blocks) { export function renderPostSummaryMarkdown(blocks, date) {
const origBlocks = blocks.filter(block => block.type === 'markdown'); const origBlocks = blocks.filter(block => block.type === 'markdown');
const readmoreIndex = origBlocks.findIndex( const readmoreIndex = origBlocks.findIndex(
(block) => block.markdown.content === "---" (block) => block.markdown.content === "---"
@ -80,16 +96,23 @@ export function renderPostSummaryMarkdown(blocks) {
if (readmoreIndex > -1) { if (readmoreIndex > -1) {
origBlocks.splice(readmoreIndex); origBlocks.splice(readmoreIndex);
} }
return renderPostMarkdown(origBlocks.map(b => b.markdown.content).join('\n\n')); return renderPostMarkdown(origBlocks.map(b => b.markdown.content).join('\n\n'), false, date);
} }
/** /**
* @param {string} src * @param {string} src
* @param {Date} [date]
* @returns string * @returns string
*/ */
export function renderCommentMarkdown(src) { export function renderCommentMarkdown(src, date) {
return unified() let stack = unified()
.use(remarkParse) .use(remarkParse);
if ((date || new Date()) > LINEBREAK_CUTOFF) {
stack = stack.use(remarkBreaks);
}
return stack
.use(remarkGfm, { .use(remarkGfm, {
singleTilde: false, singleTilde: false,
}) })
@ -112,25 +135,28 @@ export function renderCommentMarkdown(src) {
/** /**
* @param {string} src * @param {string} src
* @param {Date} [date]
* @returns string * @returns string
*/ */
export function renderPlaintext(src) { export function renderPlaintext(src, date) {
const renderedBody = renderCommentMarkdown(src); const renderedBody = renderCommentMarkdown(src, date);
return convert(renderedBody); return convert(renderedBody);
} }
/** /**
* @param {string} src * @param {string} src
* @param {Date} [date]
* @returns string * @returns string
*/ */
export function renderPostPlaintext(src) { export function renderPostPlaintext(src, date) {
const renderedBody = renderPostMarkdown(src); const renderedBody = renderPostMarkdown(src, false, date);
return convert(renderedBody); return convert(renderedBody);
} }
/** /**
* @param {StorageBlock[]} blocks * @param {StorageBlock[]} blocks
* @param {Date} [date]
* @returns string * @returns string
*/ */
export function renderPostSummaryPlaintext(blocks) { export function renderPostSummaryPlaintext(blocks, date) {
const renderedBody = renderPostSummaryMarkdown(blocks); const renderedBody = renderPostSummaryMarkdown(blocks, date);
return convert(renderedBody); return convert(renderedBody);
} }

View File

@ -206,7 +206,7 @@
<header> <header>
<h2 class="title">{post.headline}</h2> <h2 class="title">{post.headline}</h2>
<div class="post preview excerpt prose"> <div class="post preview excerpt prose">
{@html renderPostSummaryMarkdown(post.blocks)} {@html renderPostSummaryMarkdown(post.blocks, post.publishedAt ? new Date(post.publishedAt) : undefined)}
</div> </div>
</header> </header>
</a> </a>

View File

@ -33,7 +33,7 @@
// metadata // metadata
const canonicalURL = `${config.siteURL}/${getPostSlug(post)}/`; const canonicalURL = `${config.siteURL}/${getPostSlug(post)}/`;
const summary = renderPostSummaryPlaintext(post.blocks); const summary = renderPostSummaryPlaintext(post.blocks, post.publishedAt ? new Date(post.publishedAt) : undefined);
const image = getPostImages(post)[0]; const image = getPostImages(post)[0];
</script> </script>
@ -128,7 +128,7 @@
</div> </div>
{/if} {/if}
<div class="post"> <div class="post">
{@html renderPostMarkdown(post.plainTextBody)} {@html renderPostMarkdown(post.plainTextBody, false, post.publishedAt ? new Date(post.publishedAt) : undefined)}
</div> </div>
</div> </div>

View File

@ -31,7 +31,7 @@ const xml = (/** @type {Post[]} */ posts) => minifyXML(`
.map(post => ` .map(post => `
<item> <item>
<title>${post.headline}</title> <title>${post.headline}</title>
<description>${renderPostSummaryPlaintext(post.blocks)}</description> <description>${renderPostSummaryPlaintext(post.blocks, post.publishedAt ? new Date(post.publishedAt) : undefined)}</description>
<link>${config.siteURL}/${getPostSlug(post)}/</link> <link>${config.siteURL}/${getPostSlug(post)}/</link>
<dc:creator>${post.postingProject.displayName}</dc:creator> <dc:creator>${post.postingProject.displayName}</dc:creator>
<pubDate>${getPostPublishDate(post)?.toUTCString()}</pubDate> <pubDate>${getPostPublishDate(post)?.toUTCString()}</pubDate>
@ -39,7 +39,7 @@ const xml = (/** @type {Post[]} */ posts) => minifyXML(`
`<content url="${getPostImages(post)[0].fileURL}" medium="image" />` `<content url="${getPostImages(post)[0].fileURL}" medium="image" />`
: ''} : ''}
<content:encoded><![CDATA[ <content:encoded><![CDATA[
${renderPostMarkdown(post.plainTextBody, true)} ${renderPostMarkdown(post.plainTextBody, true, post.publishedAt ? new Date(post.publishedAt) : undefined)}
]]></content:encoded> ]]></content:encoded>
</item> </item>
`)} `)}