feat(export): add Notion, Bilibili, Mastodon, Bluesky, Markdown round-trip targets - #19
Conversation
250a549 to
0f19aa9
Compare
|
Pushed 8a40fe2 addressing the review feedback.
|
|
Pushed b48e5b3 — adds 50 vitest tests for the new exporters in addition to the review fixes from the prior comment. Test approach: "simulate real"
Coverage
Extra fixes uncovered while writing tests
Test plan items 1–2 (Notion / Bilibili paste) and item 4 (Hugo preview) still need manual verification with a real account, but the suite covers the underlying transformation logic that drives all three. Item 3 ( |
PerishCode
left a comment
There was a problem hiding this comment.
@wuwangzhang1216 nice clean set of new emitters — the test matrices (DOMParser round-trip + marked validation for the Markdown emitter) are exactly the right level of coverage and caught the Unicode/codepoint edge cases I would have asked about. A few non-blocking correctness notes inline on three edge cases that the current fixtures don't cover yet: a CDN allowlist regex that admits lookalike domains, and two Markdown round-trip paths (image/code inside <a>, and <br> inside table cells) that the emitter silently mangles. Happy to land as-is if you'd rather follow up separately.
🔁 Powered by Looper · runner=reviewer · agent=claude-code · An autonomous AI dev team for your GitHub repos.
|
Pushed d786299 — all three non-blocking findings from @PerishCode's review are addressed, with regression tests for each. 1.
|
|
Thanks for the detailed fix notes, @wuwangzhang1216 — that makes the follow-up easy to track. @PerishCode could you re-check the current head when you have a moment? The author says the three non-blocking findings from your review are addressed with regression coverage. @qiongyu1999, your product approval is now stale after the push as well, so a quick current-head re-approval would unblock the product gate once review/mergeability are otherwise ready. |
…trip targets
- notion.ts: juice-inline body + drop section wrappers and class/data-* noise;
keep `language-*` on <code> so Notion picks the right code mode.
- bilibili.ts: walk DOM through a 专栏 tag whitelist; replace non-bili-CDN
images with a "re-upload to bilibili" placeholder, stash the original src
on data-original-src so the user can recover it.
- mastodon.ts / bluesky.ts: build {blob, text, filename} share bundles —
screenshot via the existing iframeToBlob, caption trimmed by code-point
count to 500 / 300 chars (so emoji and surrogate pairs don't get sliced).
Image and text go on the clipboard via separate calls — browsers refuse
mixed-mime writes.
- markdown-roundtrip.ts: focused HTML → GFM walker covering the subset the
editor renders (headings, lists with nesting, fenced code with language,
tables, links, images, blockquotes). Skipped turndown to avoid pulling a
large dep for a narrow input.
Wire-up into the export menu is left for a follow-up so this PR stays
mechanical.
…comment Addresses review feedback on the new export targets: - markdown-roundtrip: use longest-backtick-run fences for inline + fenced code so embedded backticks don't break the block; URL-escape link/image hrefs with whitespace or parens via the angle-bracket form, and escape quotes/backslashes in titles; backslash-escape brackets inside link text and img alt; new escapeBlockStarts pass keeps a paragraph that starts with "# ", "> ", "- ", "1. ", etc. from being parsed as a heading or list; renderList now partitions <li> children into inline / nested- list / other-block buckets so paragraphs and code blocks inside list items render with a proper continuation indent. - bilibili: filter <code class> to language-* only so arbitrary highlight classes don't leak through; drop dead "caller decides" comment. - mastodon: clarify that truncateForPost counts code points, not graphemes — ZWJ emoji sequences are conservatively counted as multiple units.
…mitters Adds vitest + happy-dom and 50 tests covering the new export targets. Each test runs in a real DOM environment; the markdown emitter tests additionally pipe the output through `marked` (already a runtime dep) to verify the produced Markdown round-trips into the same structural shape — closest "simulate real" check we can do without spinning up Hugo / 11ty / Obsidian. - markdown-roundtrip.test.ts (25): headings, inline marks (with literal *, _, ~ kept literal), inline code with backticks (longest-fence rule), fenced code with embedded triple backticks, links/images (angle-bracket URL form for spaces+parens, title quote escape, bracket escape in link text), line-start block-marker escaping (paragraph starting with `1. `, `# `, `- `, `> ` stays a paragraph), nested lists, list item with paragraph + code block, blockquote, table with pipe escape, hr, full-document smoke. - bilibili.test.ts (9): whitelist enforcement, attribute filtering, language-* class retention on <code>, CDN vs. non-CDN image rewrite. - notion.test.ts (8): section/article/header/footer/main unwrapping, data-* + class stripping, style attribute preserved, code language-* class retention, language-plaintext fallback, <pre> without <code> gets wrapped. - mastodon.test.ts (8): whitespace collapse, surrogate-pair safety, ZWJ-emoji conservative behavior, boundary cases. While writing these: - Hoisted the first <p>'s inline content onto the list-marker line when an <li> has no inline children; otherwise marked saw "1. \n\n text" and started a new list at the next marker. - Tightened notion.ts <code class> filter to keep only language-* tokens (matches bilibili.ts; arbitrary highlighter classes no longer leak through). `pnpm test` (now wired in package.json) runs the full suite; `tsc --noEmit` clean.
Addresses the three non-blocking findings from PerishCode's review: - bilibili `isBilibiliCdn`: tighten the regex so `evilhdslb.com` / `notbilibili.com` no longer pass as valid CDNs. Require either an exact host match or a subdomain dot before the literal domain. Without this, the placeholder swap was skipped for lookalike URLs and the user would silently lose the image after bilibili's publish-time scrub. - markdown emitter, nested elements inside <a>: move bracket escape into `escapeMd` at the text-node level instead of wrapping the fully-composed `inner()` string. The old approach mangled <a><img src=y alt=a></a> to `[!\[a\](y)](href)` so the inner image no longer round-tripped; same for `<a><code>x</code></a>` where literal backslashes appeared inside the code span. - renderTable: GFM tables require one row per source line, so a literal \n inside a cell (from <br>, <p>, etc.) terminates the row early and collapses every downstream row. Collapse intra-cell \r?\n+ to literal `<br>` (inline HTML is permitted inside GFM cells) before pipe-escaping. Tests added: bilibili lookalike + subdomain matrix; markdown emitter `<a><img>` (badge link), `<a><code>` (no stray backslashes), and `<td>line1<br>line2</td>` (table stays intact). 55/55 green.
d786299 to
3a0f402
Compare
|
Thanks for the PR and the follow-up fixes. I rebased this branch onto the latest What changed:
Validation:
Risk: Low-to-medium. The rebase was limited to directory/package-shape alignment, and the exporter test suite stayed green. Product wiring for these targets remains out of scope for this PR. |
|
Thanks @PerishCode for taking the time to rebase this onto the new
I've also gone through and resolved the three outdated review threads from the original review pass. Each of the findings landed in 3a0f402 with regression coverage:
With those threads closed, the PR is now |
Summary
Adds five new export targets under src/lib/export/, filling out the matrix beyond WeChat / Zhihu / image / clipboard / download / deck:
<section>/<article>wrappers (Notion unwraps anyway) and stripsclass/data-*noise, but preserveslanguage-*on<code>so Notion picks the right code-block mode.p,h1-6, lists,pre,code,a,img,figure,span, etc.). Images not on*.hdslb.com/*.bilibili.comget swapped for a placeholder SVG withdata-original-srcstashed for recovery.Notes
tsc --noEmitclean.Test plan
toNotionHtmlinto a Notion doc — verify headings, code blocks (with language), and inline color/weight survivetoBilibiliHtmlinto a 专栏 draft — confirm unsupported tags are gone and external images render as the placeholderbuildMastodonShare/buildBlueskyShareagainst a sample preview iframe — verify caption truncation respects code points (try one with emoji past the limit)htmlToMarkdownon a deck-style document — verify nested lists, code fences with language hints, and tables render correctly in a Hugo preview