Exporting (lib/exporter.rb)
The mirror of the importers, and a much smaller thing: one source (this archive), one destination format, no adapters. ./blog.sh export walks content.nosync and writes a Jekyll-shaped tree -- _posts/<date>-<slug>.md, _drafts/<slug>.md, pages at the root, media copied under assets/<year>/<slug>/. It only reads: nothing in the archive is touched, which is what lets it be the one command that still works on an installation somebody is leaving (hence its own entry point in scripts/export.rb, needing neither env.sh nor a config that parses).
Three details carry the design:
- Its own front matter writer, deliberately not the CLI's.
build_frontmatterinscripts/manage_post.rbis not YAML and does not try to be: its reader (MarkdownParser.parse_frontmatter) splits on the first colon and quotes nothing, which is right for a human editing one post. Every reader downstream of an export -- Jekyll, Hugo,Import::Jekyll-- uses a real YAML parser, where an unquoted title containing a colon is a syntax error that takes the whole post with it. The exporter therefore dumps through Psych withline_width: -1(a folded long title is valid YAML and unreadable). - Blocks markdown cannot write down become HTML, and get counted.
MarkdownWriterdrops what it has no syntax for -- a link card that is not the post's first block, an imported embed with no recognisable address -- which is correct foredit, where the CLI's loss guard stands behind it, and wrong for an export. So the exporter renders block by block (the writer keeps no state between blocks, so this is equivalent) and gives anything that came back empty the same HTML the build renders. Counted per type and said out loud, because the destination gets HTML where the rest of the post is markdown. Above each one goes<!-- blogsh:block {...} -->, the block's own JSON with its media paths rewritten to where they sit in the export: every other engine ignores an HTML comment, andImport::Jekyllturns it back into the block (own_blocks_to_sentinels→own_block), so the round-trip loses nothing.--inside the JSON is escaped as\u002d, or a caption containing-->would close the comment early and spill markup into the page. Video and audio skip the markdown path even where the writer has a form for them:!is this engine's own syntax, and CommonMark -- what every destination engine reads -- parses it as a literal!followed by an image. Exported as markdown, a YouTube video rendered on the destination as a broken image, and re-importing the tree downloaded YouTube's HTML page and filed it in the archive as02.jpgwith no error reported, because the fetch had technically succeeded. Found only by running the whole thing over a real 118-post archive; a fixture could not have shown it, because both ends of a fixture round-trip are ours. blogsh:is the round-trip. Everything no other engine has a word for goes under that one key --sourceabove all, which is half the re-import dedup key.Import::Jekyllreads it back (whitelisted, never merged), which turns export + import into a way to move an installation. A post carrying it also gets no platform tag: a tree that came from here and goes back must not collect a "jekyll" pill per round.
What is deliberately lost: small/mention/color spans keep their text and lose their styling, and a draft's preview URL is not exported (it is a private address, not a permalink).
