Skip to content

Add opt-in .md→.html link rewriting to the markdown bundle action #148

Description

@stevenchalem

Summary

Add an opt-in capability to the markdown bundle action that rewrites local .md links to .html in the rendered output, so that converting a Markdown doc to HTML also fixes its intra-doc links in a single action.

Motivation

Today the markdown action only renders Markdown to HTML — it does not touch links. To make links work in the rendered HTML, bundle authors must follow every markdown action with a separate move/copy action carrying a regex replace like:

replace:
  from: "((\./|(?:\.\./)+).+)\.md"
  to: "\g<1>.html"

In the gist repo's bundle.yaml this exact block is copy-pasted 6 times (docs, docs/models, migration v11–v14). That duplication is error-prone: a real bug shipped in the gist 14.1.0 web-download bundle because the replace block got attached to the wrong action (the *.md move instead of the *.html move), simultaneously skipping the conversion where it was needed and applying it where it was wrong. See gist issues semanticarts/gist#1443 (redesign request) and semanticarts/gist#1467 (the narrow bug fix).

Pushing the .md.html relink into the markdown action itself eliminates all six duplicated blocks downstream and removes the entire class of "wrong action" bugs, because there is no longer a separate, mis-attachable step. This is "Option A" from semanticarts/gist#1443.

Proposed change

Add an opt-in boolean option to the markdown action (proposed name: relinkHtml, matching the existing camelCase options like versionedDefinedBy, retainDefinedBy). When true, after rendering Markdown to HTML, rewrite local relative links ending in .md to .html.

- action: markdown
  source: "{output}/docs/"
  target: "{output}/docs/html/"
  includes: ["*.md"]
  relinkHtml: true        # NEW: render AND fix .md -> .html links

Rewrite semantics (must get these right)

  • Only rewrite local relative link targets — skip absolute URLs (http:, https:, mailto:, protocol-relative //, and root-absolute /...).
  • Operate on href/src attribute values of the rendered HTML, not arbitrary text — so .md mentioned in prose or code spans is not touched. (This is stricter/safer than the current regex, which matched anywhere in the file.)
  • Preserve any #fragment or ?query suffix: ./Foo.md#section./Foo.html#section.
  • More general than the current regex (which required an explicit ./ or ../ prefix): bare relative targets like Foo.md should also be relinked.

Implementation pointers (branch already created)

A branch feature/markdown-relink-html is already checked out off develop.

  • onto_tool/bundle.py:292__bundle_markdown__ (the markdown action). Currently renders via md2html and writes output; ignores replace. Read the option here (use the existing __boolean_option__ helper at onto_tool/bundle.py:849) and pass it through to the renderer.
  • onto_tool/mdutils.py:44md2html(md). Add an optional relink=False parameter; when set, post-process the rendered HTML to rewrite local .md links in href/src attributes to .html. A focused regex on attribute values (with the scheme/anchor handling above) is acceptable for the prototype; a real HTML parser is the robust version if needed.
  • onto_tool/bundle_schema.yaml — the markdown action validates against #/definitions/bulk_file_operation (def at line 32; markdown wired at lines 394–396). Add the new relinkHtml property so the option passes schema validation. (Confirm whether additionalProperties is restricted — this was the last unchecked item before the session paused.)

Tests

There is an existing markdown test harness to extend:

  • tests/bundle/test_markdown.py, tests/bundle/markdown.yaml, tests/bundle/bulk_md.yaml, tests/bundle/Table.md
  • Add a fixture Markdown file containing a mix of links: a local ./foo.md, a bare foo.md, a ../bar.md#frag, an external https://example.com/x.md, and a .md reference inside a code span. Assert that only the local relative ones are rewritten to .html, fragments preserved, external and code-span untouched.
  • Runner is pytest (see setup.py tests_require).

Acceptance criteria

  • markdown action accepts relinkHtml: true and rewrites local .md links to .html in rendered output.
  • External URLs, absolute paths, code-span text, and fragments are handled per the semantics above.
  • Schema validates the new option.
  • New test covers the link cases and passes; existing tests still pass.
  • (Follow-up, separate) Update gist bundle.yaml to use relinkHtml and delete the six duplicated move … replace blocks — tracked in gist, not here.

Out of scope

The relative-path depth fixes (e.g. ../docs/./, ../migration/../../migration/) are a separate concern ("Option B" in semanticarts/gist#1443) and are not part of this issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions