大規模なソフトウェアを使っていると、何かを実現するための調査に丸一日費やした挙げ句、実現できないことが分かる、なんてことがあります。
これはちょっと生産的でないので、少し調べて分からなかったらコードを直接書き換えてしまうのが好きです。
自分専用のちょー汚い修正 (dirty hack) をするのは簡単だし、運が良ければdirty hackをしているうちに自分が探していたコンフィグが見つかりhack不要で解決することもよくあります。
Hugoもそのような大規模ソフトウェアの1つで、なにか小さいことを実現するためにドキュメントやStackoverflowを読んで1時間くらい試行錯誤しなければなりません。試行錯誤は嫌なのでdirty hackすることにしました。
テキストプリプロセッサー
Hugoを使っていると、HugoにMarkdownを処理させる前に文字列置換を行いたいケースがたくさん出てきました。
1つだけ紹介します。
このブログでは、記事の一番下にRead Markdownというリンクをつけて原稿のMarkdownを公開しています。
私はMarkdownで公開記事を書くときは <!-- コメント -->
を下書きとして使います。
このコメントは基本的に公開して良いものなんですが、たまに恥ずかしくて公開したくないものがあります。
1
2
3
4
5
|
<!--
やべえこのコマンドなんかやばいっぽいぞ
TODO: ちゃんと調べて直す
-->
`sudo rm -rf /` を実行してみましょう。
|
これを “Read Markdown” から除外する方法を調べたところ、
shortcodeを使えば行けるっぽいですが、学習・試行錯誤したくないし、Hugo独自の記法は極力避けたい
(vscode等が解釈できないし、将来Hugo以外に移行するかもしれないので)です。
Hack
2020/08/22 (d39636a) 時点ではこれで行けました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
diff --git a/parser/pageparser/pageparser.go b/parser/pageparser/pageparser.go
index 3d17aa8e..09a6adc6 100644
--- a/parser/pageparser/pageparser.go
+++ b/parser/pageparser/pageparser.go
@@ -15,11 +15,17 @@ package pageparser
import (
"bytes"
+ "fmt"
"io"
"io/ioutil"
+ "os"
+ "os/exec"
+ "reflect"
+ "regexp"
"github.com/gohugoio/hugo/parser/metadecoders"
"github.com/pkg/errors"
+ "github.com/spf13/afero"
)
// Result holds the parse result.
@@ -100,8 +106,67 @@ func ParseMain(r io.Reader, cfg Config) (Result, error) {
return parseSection(r, cfg, lexMainSection)
}
+func preProcessContent(b []byte, fileName string) []byte {
+ re := regexp.MustCompile(`(?ms)^<!--\n.*?\n-->$`) // TODO: do in init()
+ tmp := re.Find(b)
+ if tmp == nil {
+ return b
+ }
+ fmt.Fprintf(os.Stderr, "%s: remove comment\n", fileName)
+ b2 := re.ReplaceAll(b, []byte(""))
+
+ // _ = os.MkdirAll(path.Dir("/tmp/hugo/" + fileName), 0700)
+ // fmt.Fprintf(os.Stderr, "%s: write to /tmp/hugo/%s.{1,2}\n", fileName, fileName)
+ // _ = ioutil.WriteFile(fmt.Sprintf("/tmp/hugo/%s.1", fileName), b, 0600)
+ // _ = ioutil.WriteFile(fmt.Sprintf("/tmp/hugo/%s.2", fileName), b2, 0600)
+
+ return b2
+}
+
+func preProcessContentExec(b []byte, fileName string) []byte {
+ cmd := exec.Command("sed", "s/foo/bar/g")
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ panic(err)
+ }
+ defer stdin.Close()
+ stdout, err := cmd.StdoutPipe()
+ if err != nil {
+ panic(err)
+ }
+ cmd.Stderr = os.Stderr
+ if err := cmd.Start(); err != nil {
+ panic(err)
+ }
+ defer stdout.Close()
+
+ if _, err := stdin.Write(b); err != nil {
+ panic(err)
+ }
+ stdin.Close()
+ var b2 []byte
+ if b2, err = ioutil.ReadAll(stdout); err != nil {
+ panic(err)
+ }
+
+ if err := cmd.Wait(); err != nil {
+ panic(err)
+ }
+
+ return b2
+}
+
func parseSection(r io.Reader, cfg Config, start stateFunc) (Result, error) {
b, err := ioutil.ReadAll(r)
+
+ fileName := func() string {
+ v := reflect.ValueOf(r).Elem()
+ file := v.FieldByName("File").Interface().(afero.File)
+ return file.Name()
+ }()
+ b = preProcessContent(b, fileName)
+ b = preProcessContentExec(b, fileName)
+
if err != nil {
return nil, errors.Wrap(err, "failed to read page content")
}
|
dirty hackだからお父さんreflectも使っちゃうぞー。
pageparser.parseSection()
でのバックトレースも貼っておきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
github.com/gohugoio/hugo/parser/pageparser.parseSection at pageparser.go:148
github.com/gohugoio/hugo/parser/pageparser.Parse at pageparser.go:45
github.com/gohugoio/hugo/hugolib.(*pageMap).newPageFromContentNode at content_map_page.go:146
github.com/gohugoio/hugo/hugolib.(*pageMap).assemblePages.func1 at content_map_page.go:364
github.com/armon/go-radix.recursiveWalk at radix.go:519
github.com/armon/go-radix.recursiveWalk at radix.go:525
github.com/armon/go-radix.recursiveWalk at radix.go:525
github.com/armon/go-radix.recursiveWalk at radix.go:525
github.com/armon/go-radix.(*Tree).Walk at radix.go:447
github.com/gohugoio/hugo/hugolib.(*pageMap).assemblePages at content_map_page.go:337
github.com/gohugoio/hugo/hugolib.(*pageMaps).AssemblePages.func1 at content_map_page.go:718
github.com/gohugoio/hugo/hugolib.(*pageMaps).withMaps.func1 at content_map_page.go:786
github.com/gohugoio/hugo/common/para.(*errGroupRunner).Run.func1 at para.go:52
golang.org/x/sync/errgroup.(*Group).Go.func1 at errgroup.go:57
runtime.goexit at asm_amd64.s:1373
|
go install --tags extended
でビルド・インストールしましょう。
こんな感じで任意のtext preprocessingをすることができました。わーい。
正攻法でもっと楽な方法があれば教えて下さい。