Pandoc relative paths with multiple files: tests
Bobby Huang ·
The section The two sample files prints both inputs and the folder they were run from. Plain runs of both readers are quoted in Plain readers keep paths as typed. Markdown-reader HTML carrying the extra switch is quoted under Turning on rebase_relative_paths. The same switch on the other reader fills Switching it on for commonmark. A URL and a slash-led path get their own look in Paths the extension left alone. One link that climbs a folder is traced in A parent-folder path kept as typed. A run made from inside one file's folder is quoted in One file run from its own folder. A numbered command list fills Steps for converting files from different folders.
Key takeaways
- Folder layout and file text for the inputs appear in The two sample files.
- Converted HTML for both files, switch on, is quoted in Turning on rebase_relative_paths.
- The section Paths the extension left alone looks at the URL and the slash-led path from the sample files.
- Read A parent-folder path kept as typed for the link out of the b folder.
- Commands to repeat this test come in order under Steps for converting files from different folders.
Say you pass pandoc two Markdown files that live in different folders, and each one points at images and pages relative to its own folder. Here you'll find the paths pandoc wrote in that case, with and without a pandoc extension called rebase_relative_paths.
Each command got one run on one macOS computer, and pandoc --version printed 3.8.3. The output below is copied from the logs of that test. It says what pandoc wrote and makes no claim about how any other program treats those paths afterward.
The two sample files
The test folder rb holds two subfolders, a and b, with one file in each. From inside rb, the first file printed as:
cat a/one.md
# One

See [the notes](/blog/notes), the [site](https://example.com/page) and [the root file](/top.md).
And the second:
cat b/two.md
# Two

Back to [one](../a/one.md).
one.md carries four targets. The image path is img/chart.png, the first link goes to notes.md, the second is the full URL https://example.com/page, and the third is /top.md, which starts with a slash. two.md has an image called logo.png and one link, ../a/one.md, written to reach one.md from the b folder.
For how image and link paths are written inside a single file, read images in Markdown and links to local files. Here the interest is what changes once two files from two folders go through one pandoc command.
Each multi-file run below was started from rb, so the arguments read a/one.md b/two.md.
Plain readers keep paths as typed
First, the markdown reader with nothing added:
pandoc -f markdown -t html a/one.md b/two.md
<h1 id="one">One</h1>
<figure>
<img src="img/chart.png" alt="Chart" />
<figcaption aria-hidden="true">Chart</figcaption>
</figure>
<p>See <a href="notes.md">the notes</a>, the <a
href="https://example.com/page">site</a> and <a href="/top.md">the root
file</a>.</p>
<h1 id="two">Two</h1>
<figure>
<img src="logo.png" alt="Logo" />
<figcaption aria-hidden="true">Logo</figcaption>
</figure>
<p>Back to <a href="../a/one.md">one</a>.</p>
Pandoc joined the two inputs into a single HTML stream, one.md first. Each src and href matches its source byte for byte: img/chart.png, notes.md, logo.png and ../a/one.md came out with no folder in front. This reader also put each image inside a figure element with a caption.
The commonmark reader, again with nothing added:
pandoc -f commonmark -t html a/one.md b/two.md
<h1>One</h1>
<p><img src="img/chart.png" alt="Chart" /></p>
<p>See <a href="notes.md">the notes</a>, the <a
href="https://example.com/page">site</a> and <a href="/top.md">the root
file</a>.</p>
<h1>Two</h1>
<p><img src="logo.png" alt="Logo" /></p>
<p>Back to <a href="../a/one.md">one</a>.</p>
The markup differs: no heading ids, and each image sits in a plain p element. The paths, though, are identical to those in the markdown run, unchanged from the source. Whatever wrapper each reader chose, the src and href values came through as written.
Why nothing moved: line 55 of the extension listing pandoc printed for markdown reads -rebase_relative_paths. That listing came from pandoc --list-extensions. For commonmark, line 19 of its listing holds that same text. The minus sign in front marks it as off unless you ask for it.
Turning on rebase_relative_paths
Next, +rebase_relative_paths goes on the end of the reader name:
pandoc -f markdown+rebase_relative_paths -t html a/one.md b/two.md
<h1 id="one">One</h1>
<figure>
<img src="a/img/chart.png" alt="Chart" />
<figcaption aria-hidden="true">Chart</figcaption>
</figure>
<p>See <a href="a/notes.md">the notes</a>, the <a
href="https://example.com/page">site</a> and <a href="/top.md">the root
file</a>.</p>
<h1 id="two">Two</h1>
<figure>
<img src="b/logo.png" alt="Logo" />
<figcaption aria-hidden="true">Logo</figcaption>
</figure>
<p>Back to <a href="b/../a/one.md">one</a>.</p>
Four paths got longer. From one.md, the image became a/img/chart.png and the notes link became a/notes.md. From two.md, the image became b/logo.png and the link back became b/../a/one.md.
Look at the prefix in each case. Targets from a/one.md gained a/, and targets from b/two.md gained b/. Here, the text pandoc added equals the folder part of each input path as it was typed on the command line.
Pandoc wrapped the paragraph lines at the same spots as in the plain run; this copy keeps its wraps.
Switching it on for commonmark
The same plus sign works on commonmark:
pandoc -f commonmark+rebase_relative_paths -t html a/one.md b/two.md
<h1>One</h1>
<p><img src="a/img/chart.png" alt="Chart" /></p>
<p>See <a href="a/notes.md">the notes</a>, the <a
href="https://example.com/page">site</a> and <a href="/top.md">the root
file</a>.</p>
<h1>Two</h1>
<p><img src="b/logo.png" alt="Logo" /></p>
<p>Back to <a href="b/../a/one.md">one</a>.</p>
Here the four rewritten paths match the markdown run string for string. What still separates the two outputs is the markup the plain runs already showed: heading ids and figure wrappers on one side, bare headings and p around images on the other.
The name rebase_relative_paths belongs to pandoc. It shows up in both extension listings cited above, and both readers took it: each run logged rc=0.
Paths the extension left alone
Two of the targets in one.md came through both rebase runs untouched. The link to https://example.com/page kept its full URL, and the link to /top.md kept its leading slash with no a/ in front.
Counts from the extra test back this up. href="https://example.com/page" matched once in the markdown output and once in the commonmark output. href="/top.md" matched once in each as well.
Put together, pandoc rewrote the four targets in this test that began with a plain name or with .., and skipped the one with a scheme and the one starting at a slash.
A parent-folder path kept as typed
The link in two.md was ../a/one.md. After the switch, pandoc wrote b/../a/one.md in its place.
Pandoc put b/ in front and stopped there. The b/.. pair stayed in the string; it was not shortened to a/one.md. Counting in the markdown rebase output found href="b/../a/one.md" on one line and href="a/one.md" on none. The commonmark rebase output gave the same count for the longer form.
If your later steps compare paths as plain strings, b/../a/one.md and a/one.md are different strings, and this run produced the first one.
One file run from its own folder
The last run moved into rb/a and passed a bare file name, with the extension still on:
pandoc -f markdown+rebase_relative_paths -t html one.md
<h1 id="one">One</h1>
<figure>
<img src="img/chart.png" alt="Chart" />
<figcaption aria-hidden="true">Chart</figcaption>
</figure>
<p>See <a href="notes.md">the notes</a>, the <a
href="https://example.com/page">site</a> and <a href="/top.md">the root
file</a>.</p>
No path changed. The image stayed img/chart.png and the notes link stayed notes.md, the same lines the plain markdown run printed for this file. A search for a/img in this output matched zero lines.
The argument one.md has no folder part, and pandoc added no prefix. That fits the two-file runs, where each prefix matched the folder written in front of the file name.
Steps for converting files from different folders
These steps repeat this test's own commands with the same pandoc build. Step 4 is the run quoted under Turning on rebase_relative_paths, so your output can be held up against the logged bytes line by line.
- Pick one folder to run from. Here it was
rb, the parent ofaandb. - Name each input with its folder, as in
a/one.md b/two.md. - Add
+rebase_relative_pathsto the reader name, eithermarkdownorcommonmark. - Convert with
pandoc -f markdown+rebase_relative_paths -t html a/one.md b/two.md. - Search the output for a folder followed by
.., such asb/../a/one.md. Pandoc left that pair in place during this run. - Check the full URL and the slash-led path. In both rebase runs here, they kept their original text.
A sibling post in this batch, on LaTeX macros in pandoc Markdown, puts another pandoc extension through the same kind of test.
Where Sashmark fits
Sashmark's first version, as designed, would let you fix a sentence directly on the formatted page. If that clean Round-Trip holds, every line you skip should keep its bytes, with nothing lost. Changes View, planned for that version too, would point out what moved between one reading and the next, whether or not git is around.
It is in development and has not been released. The app itself has no AI features. See the planned features, the help pages and the roadmap for where each part stands.
More at sashmark.com.
Questions
- Does pandoc fix relative paths when I convert files from different folders?
- Compare the runs in Plain readers keep paths as typed against those in Turning on rebase_relative_paths.
- Why did a path come out with two dots still in it?
- The section A parent-folder path kept as typed walks through that link and the line counts taken on it.
- Does a lone file get a folder prefix?
- See One file run from its own folder for the command and the HTML it gave.
- Can commonmark take the same switch?
- That reader's HTML with the plus sign added sits in Switching it on for commonmark.