Honor --canonical-url on every network that has one - #29
Merged
Conversation
`--canonical-url` reached dev.to and nowhere else. Publishing the same article to `--to htmlblog,devto` therefore left two copies with no statement about which was the original, and a Hashnode or Ghost copy had no way to say at all. Now every long-form adapter that has a field for it sends one, and the field name differs every time, which is exactly why this failed quietly rather than loudly: dev.to `canonical_url`, Hashnode `originalArticleURL`, Ghost `canonical_url`, Tumblr `source_url` (the attribution link it has instead of a canonical), gitblog a `canonical:` frontmatter key, htmlblog a real `<link rel="canonical">`. WordPress and Micro.blog are deliberately not in that list. WordPress core has no canonical field, only an SEO plugin's post meta, and Micropub defines no canonical property. Faking either would post something that looks configured and does nothing. htmlblog points a page at itself from the siteUrl it was logged in with, since the original should confirm what the copies claim; an explicit --canonical-url overrides it for an article first published elsewhere. That URL has to byte match the address the post is actually served at, so postUrl() is now the one place that joins siteUrl to a file name, used by the canonical, the link myna reports and the timeline alike. It trims trailing slashes, which the old inline concatenation did not. When cli-tools' blog-post writes the page instead, it applies its own siteUrl and myna forwards the flag only when one was given. That flag needs cli-tools 0.28.0. 242 tests pass, 11 new. The API adapters are driven against a stubbed fetch and the request body read back, because the assertion worth making is the field name on the wire. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GNpSGiTnfodDyCQ3MU6Nsk
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
--canonical-urlreached dev.to and nowhere else. So--to htmlblog,devtopublished the same article twice with no statement about which was the
original, and a Hashnode or Ghost copy had no way to make one at all.
What each network actually calls it
This failed quietly rather than loudly because the field name is different
every time:
canonical_url(already worked)originalArticleURLcanonical_urlsource_url— the attribution link it has instead of a canonicalcanonical:frontmatter key, for the site template to render<link rel="canonical">in the headWordPress and Micro.blog are deliberately excluded. WordPress core has no
canonical field — it belongs to an SEO plugin's post meta — and Micropub
defines no canonical property. Wiring something plausible there would ship a
flag that looks configured and does nothing.
htmlblog points at itself
The original should confirm what the copies claim, so a page gets a
self-referential canonical from the
siteUrlthe account logged in with.--canonical-urloverrides it for an article first published elsewhere.A canonical that does not byte-match the real address points at a different
page, so
postUrl()is now the single place that joinssiteUrlto a filename, shared by the canonical, the URL myna reports and the timeline. It trims
trailing slashes, which the old inline concatenation did not — that was a
latent double-slash bug in the reported URL.
The blog-post seam
myna post --to htmlblogdoes not write the page itself whenprofullstack/cli-tools'
blog-postis on PATH, and on the dev box it is. So atag added only to myna's native writer would never have reached the real blog.
blog-post applies its own
siteUrl(profullstack/cli-tools#55) and mynaforwards
--canonicalonly when one was explicitly given. That flag needscli-tools 0.28.0.
Verified
back, since the assertion worth making is the field name on the wire. One
test loops every network claiming support and fails if the URL is missing
from the request, so adding an adapter and forgetting to wire it breaks.
frontmatter quoting, and trailing-slash joining
tsc --noEmitcleanVersion bumped to 0.12.0 across the packages that track core. The plugin
packages sit at 0.8.4 on main already; that drift is pre-existing and untouched.
🤖 Generated with Claude Code
https://claude.ai/code/session_01GNpSGiTnfodDyCQ3MU6Nsk