feat: body links, outbound rules and image attributes in the post body

- renderJournalBody runs the UI-SPEC L7 order on the sanitized body:
  h1 demotion, the CMS phrase auto-links, the outbound rules and lazy,
  async images, with vue-seo-layer's autolink, applyOutbound and
  imageAttrs; PostBody feeds it the page's resolve answer (autolinks,
  autolink_max, outbound, url) and the seo.links.newTabHint option
- the sanitizer keeps a link rel of only nofollow, sponsored and ugc
  (the Blog Editor's link form, as the Journal plugin's policy does);
  target is never kept, the outbound rules add it
- editor-corpus.json copied again from sm-journal-plugin (link-rel and
  image-decorative entries)
- smoke:ssr script; expectations for auto-links, both new-tab modes and
  the hint switched off
This commit is contained in:
Jakub Zych
2026-11-04 13:46:54 +01:00
parent 289d031201
commit 1010a603f0
10 changed files with 255 additions and 21 deletions

View File

@@ -131,7 +131,9 @@ test('the sinks expand after sanitizing and the payload transforms never expand'
const read = (path: string) => readFileSync(new URL(path, import.meta.url), 'utf8')
for (const sink of ['../app/components/Journal/PostBody.vue', '../app/components/Journal/CategoryHeader.vue']) {
const source = read(sink)
assert.match(source, /expandEmbeds\(sanitizeJournalHtml\(/, `${sink} must render expandEmbeds(sanitizeJournalHtml(...))`)
// renderJournalBody sanitizes first, then adds only escaped link and
// image markup (htmlGate.test.ts), so it counts as the sanitizer here.
assert.match(source, /expandEmbeds\(\s*(sanitizeJournalHtml|renderJournalBody)\(/, `${sink} must render expandEmbeds(sanitizeJournalHtml(...)) or expandEmbeds(renderJournalBody(...))`)
assert.equal((source.match(/expandEmbeds\(/g) ?? []).length, 1, `${sink} expands once`)
}
for (const transform of ['../app/utils/postView.ts', '../app/journal-pages/CategoryPage.vue']) {