Skip to content

Commit eac2a41

Browse files
authored
[Filter] add rewritelinks filter: transform image src and link targets in markdown files (recursively) (#116)
1 parent 96094c8 commit eac2a41

File tree

6 files changed

+734
-2
lines changed

6 files changed

+734
-2
lines changed

filters/hugo_rewritelinks.lua

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
--[[
2+
All 'readme.md' will be transformed into '_index.md', any other Markdown file
3+
'file.md' will be transformed into 'file/_index.md' (see 'hugo_makedeps.lua').
4+
5+
This filter will adapt the image sources and the link targets in a Markdown
6+
file so that Hugo can build a web page from it.
7+
]]--
8+
9+
10+
-- local vars
11+
local INDEX_MD = "readme" -- name of readme.md (will be set from metadata)
12+
13+
14+
15+
-- helper
16+
local function _is_relative (target)
17+
return pandoc.path.is_relative(target)
18+
end
19+
20+
local function _is_markdown (target)
21+
return target:match('.*%.md')
22+
end
23+
24+
local function _is_url (target)
25+
return target:match('https?://.*')
26+
end
27+
28+
29+
30+
-- replace `![](img/b.png)` with `![](b.png)`
31+
function Image (image)
32+
if _is_relative(image.src) and not _is_url(image.src) then
33+
image.src = pandoc.path.filename(image.src)
34+
return image
35+
end
36+
end
37+
38+
39+
40+
--[[
41+
Hugo's ref shortcode tries to resolve paths without a leading '/' first relative to the current page,
42+
then to the rest of the site. As long as all pages have a _unique name_, we can also simply use the
43+
name of the page. Index pages ('_index.md') must be referenced via their toplevel folder.
44+
45+
All files will be transformed during processing (see 'hugo_makedeps.lua'):
46+
- 'subdir/file-a.md' will become 'subdir/file-a/_index.md'
47+
- 'subdir/readme.md' will become 'subdir/_index.md'
48+
49+
Since "subdir" might contain something like "../", we would have to resolve this first. However, as
50+
Hugo's ref shortcode can also resolve just the filename without a path, we can skip this effort and
51+
just replace the path with the filename. For index files ('readme.md'), we have to extract the last
52+
folder.
53+
54+
This filter need to perform for any link in any Markdown file:
55+
- replace `[Y](subdir/file-a.md)` with `[Y]({{< ref "file-a" >}})`
56+
- replace `[Y](subdir/readme.md)` with `[Y]({{< ref "subdir" >}})`
57+
- replace `[Y](readme.md)` with `[Y]({{< ref "/" >}})` (might work with Hugo)
58+
59+
Caveats:
60+
(a) All referenced Markdown files must have UNIQUE NAMES.
61+
(b) References to the top index page (landing page) are (presumably) not working.
62+
]]--
63+
function Link (link)
64+
local target = link.target
65+
local content = pandoc.utils.stringify(link.content)
66+
67+
if _is_markdown(target) and _is_relative(target) and not _is_url(target) then
68+
local dir = pandoc.path.split(pandoc.path.directory(target)) -- 'foo.md' => {'.'}; 'sub/foo.md' => {'sub'}; './sub/foo.md' => {'.', 'sub'};
69+
local name, _ = pandoc.path.split_extension(pandoc.path.filename(target))
70+
71+
if name == INDEX_MD then
72+
target = (#dir >= 1 and dir[#dir] ~= ".") and dir[#dir] or "/" -- readme.md: use parent folder or '/'
73+
else
74+
target = name -- ordinary file: use it's name w/o extension
75+
end
76+
77+
return pandoc.RawInline('markdown', '[' .. content .. ']({{< ref "' .. target .. '" >}})')
78+
end
79+
end
80+
81+
82+
83+
function Meta (m)
84+
INDEX_MD = m.indexMD or "readme"
85+
end
86+
87+
88+
89+
return {
90+
{ Meta = Meta },
91+
{ Image = Image },
92+
{ Link = Link }
93+
}

filters/test filter/Makefile

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
11
DIFF ?= diff --strip-trailing-cr -u
22
PANDOC ?= pandoc
33

4+
FILES_TRANSFORM = readme.md
45
FILES_MAKEDEPS = readme.md
56

7+
LUA_REWRITELINKS = ../hugo_rewritelinks.lua
68
LUA_MAKEDEPS = ../hugo_makedeps.lua
79

810

9-
test: test_makedeps
11+
test: test_rewritelinks test_makedeps
12+
13+
test_rewritelinks: $(FILES_TRANSFORM)
14+
@$(PANDOC) -L $(LUA_REWRITELINKS) -t native $^ \
15+
| $(DIFF) expected_rewritelinks1.native -
16+
@$(PANDOC) -L $(LUA_REWRITELINKS) -M indexMD="foo" -t native $^ \
17+
| $(DIFF) expected_rewritelinks2.native -
18+
@$(PANDOC) -L $(LUA_REWRITELINKS) -M indexMD="readme" -t native $^ \
19+
| $(DIFF) expected_rewritelinks3.native -
20+
@$(PANDOC) -L $(LUA_REWRITELINKS) -M weight=42 -M indexMD="readme" -t native $^ \
21+
| $(DIFF) expected_rewritelinks4.native -
1022

1123
test_makedeps: $(FILES_MAKEDEPS)
1224
@$(PANDOC) -L $(LUA_MAKEDEPS) -t native $^ \
@@ -19,6 +31,16 @@ test_makedeps: $(FILES_MAKEDEPS)
1931
| $(DIFF) expected_makedeps4.native -
2032

2133

34+
expected: expected_rewritelinks1.native expected_rewritelinks2.native expected_rewritelinks3.native expected_rewritelinks4.native
35+
expected_rewritelinks1.native: $(FILES_TRANSFORM)
36+
$(PANDOC) -L $(LUA_REWRITELINKS) -t native -o $@ $^
37+
expected_rewritelinks2.native: $(FILES_TRANSFORM)
38+
$(PANDOC) -L $(LUA_REWRITELINKS) -M indexMD="foo" -t native -o $@ $^
39+
expected_rewritelinks3.native: $(FILES_TRANSFORM)
40+
$(PANDOC) -L $(LUA_REWRITELINKS) -M indexMD="readme" -t native -o $@ $^
41+
expected_rewritelinks4.native: $(FILES_TRANSFORM)
42+
$(PANDOC) -L $(LUA_REWRITELINKS) -M weight=42 -M indexMD="readme" -t native -o $@ $^
43+
2244
expected: expected_makedeps1.native expected_makedeps2.native expected_makedeps3.native expected_makedeps4.native
2345
expected_makedeps1.native: $(FILES_MAKEDEPS)
2446
$(PANDOC) -L $(LUA_MAKEDEPS) -t native -o $@ $^
@@ -31,6 +53,7 @@ expected_makedeps4.native: $(FILES_MAKEDEPS)
3153

3254

3355
clean:
56+
rm -rf expected_rewritelinks1.native expected_rewritelinks2.native expected_rewritelinks3.native expected_rewritelinks4.native
3457
rm -rf expected_makedeps1.native expected_makedeps2.native expected_makedeps3.native expected_makedeps4.native
3558

36-
.PHONY: test test_makedeps expected clean
59+
.PHONY: test test_rewritelinks test_makedeps expected clean
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
[ Header 1 ( "summary.md" , [] , [] ) [ Str "Summary.md" ]
2+
, Header
3+
2
4+
( "this-should-work" , [] , [] )
5+
[ Str "This" , Space , Str "should" , Space , Str "work" ]
6+
, Para [ Str "Thanks" , Space , Str "everyone!" ]
7+
, Para
8+
[ RawInline
9+
(Format "markdown") "[File A]({{< ref \"file-a\" >}})"
10+
]
11+
, Para
12+
[ RawInline
13+
(Format "markdown") "[File A]({{< ref \"file-a\" >}})"
14+
]
15+
, Figure
16+
( "" , [] , [] )
17+
(Caption
18+
Nothing
19+
[ Plain
20+
[ Str "Image"
21+
, Space
22+
, Str "A"
23+
, Space
24+
, Str "(from"
25+
, Space
26+
, Str "Readme.md)"
27+
]
28+
])
29+
[ Plain
30+
[ Image
31+
( "" , [] , [] )
32+
[ Str "Image"
33+
, Space
34+
, Str "A"
35+
, Space
36+
, Str "(from"
37+
, Space
38+
, Str "Readme.md)"
39+
]
40+
( "a.png" , "" )
41+
]
42+
]
43+
, Para [ Image ( "" , [] , [] ) [] ( "a.png" , "" ) ]
44+
, Header
45+
2
46+
( "different-wrong-format" , [] , [] )
47+
[ Str "Different"
48+
, Space
49+
, Str "(wrong)"
50+
, Space
51+
, Str "format"
52+
]
53+
, BulletList
54+
[ [ Plain
55+
[ Link
56+
( "" , [] , [] )
57+
[ Str "wrong" , Space , Str "extension" ]
58+
( "file-c.png" , "" )
59+
]
60+
]
61+
, [ Plain
62+
[ Link
63+
( "" , [] , [] )
64+
[ Str "not" , Space , Str "local" ]
65+
( "https://pandoc.org/lua-filters.html" , "" )
66+
]
67+
]
68+
, [ Plain
69+
[ Link
70+
( "" , [] , [] )
71+
[ Str "still" , Space , Str "not" , Space , Str "local" ]
72+
( "https://pandoc.org/lua-filters.md" , "" )
73+
]
74+
]
75+
, [ Plain
76+
[ Link
77+
( "" , [] , [] )
78+
[ Str "also" , Space , Str "not" , Space , Str "local" ]
79+
( "http://pandoc.org/lua-filters.md" , "" )
80+
]
81+
]
82+
, [ Plain
83+
[ RawInline
84+
(Format "markdown")
85+
"[not there and not here]({{< ref \"wuppie\" >}})"
86+
]
87+
]
88+
]
89+
, Header
90+
2
91+
( "recursive-inclusion" , [] , [] )
92+
[ Str "Recursive" , Space , Str "inclusion" ]
93+
, Para
94+
[ RawInline
95+
(Format "markdown") "[File B]({{< ref \"file-b\" >}})"
96+
]
97+
, Header
98+
2 ( "subdirectories" , [] , [] ) [ Str "Subdirectories" ]
99+
, Para
100+
[ RawInline
101+
(Format "markdown")
102+
"[Subdir: File D]({{< ref \"file-d\" >}})"
103+
]
104+
, Para
105+
[ RawInline
106+
(Format "markdown")
107+
"[Subdir: File D]({{< ref \"file-d\" >}})"
108+
]
109+
, Header
110+
2
111+
( "subdirectories-recursive" , [] , [] )
112+
[ Str "Subdirectories," , Space , Str "recursive" ]
113+
, Para
114+
[ RawInline
115+
(Format "markdown")
116+
"[Subdir: File E]({{< ref \"file-e\" >}})"
117+
]
118+
, Header
119+
2
120+
( "subdirectories-direct-plus-recursive" , [] , [] )
121+
[ Str "Subdirectories,"
122+
, Space
123+
, Str "direct"
124+
, Space
125+
, Str "plus"
126+
, Space
127+
, Str "recursive"
128+
]
129+
, Para
130+
[ RawInline
131+
(Format "markdown")
132+
"[Subdir/Leaf: Foo]({{< ref \"foo\" >}})"
133+
]
134+
, Header
135+
2
136+
( "links-to-landing-pages" , [] , [] )
137+
[ Str "Links"
138+
, Space
139+
, Str "to"
140+
, Space
141+
, Str "Landing"
142+
, Space
143+
, Str "Pages"
144+
]
145+
, Para
146+
[ RawInline
147+
(Format "markdown")
148+
"[subdir/readme]({{< ref \"subdir\" >}})"
149+
]
150+
, Para
151+
[ RawInline
152+
(Format "markdown") "[readme]({{< ref \"/\" >}})"
153+
]
154+
]

0 commit comments

Comments
 (0)