-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathconfig.go
183 lines (148 loc) · 5.61 KB
/
config.go
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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright (c) Bartłomiej Płotka @bwplotka
// Licensed under the Apache License 2.0.
package transform
import (
"bytes"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"text/template"
"github.com/gobwas/glob"
"gopkg.in/yaml.v3"
)
type LocalLinksStyle struct {
// Hugo make sure mdox converts the links to work on Hugo-like website so:
// * Adds `slug: {{ FileName }}` to make sure filename extension is part of path, if slug is not added.
// * Local links are lower cased (hugo does that by default).
// * All links are expected to be paths e.g ../ is added if they target local, non directory links.
Hugo *HugoLocalLinksStyle
}
type HugoLocalLinksStyle struct {
// e.g for google/docsy it is "_index.md"
IndexFileName string `yaml:"indexFileName"`
}
type Config struct {
Version int
// InputDir is a relative (to PWD) path that assumes input directory for markdown files and assets.
InputDir string `yaml:"inputDir"`
// OutputDir is a relative (to PWD) output directory that we expect all files to land in. Typically that can be `content` dir
// which hugo uses as an input.
OutputDir string `yaml:"outputDir"`
// ExtraInputGlobs allows to bring files from outside of input dir.
ExtraInputGlobs []string `yaml:"extraInputGlobs"`
// LinkPrefixForNonMarkdownResources specifies link to be glued onto relative links which don't point to markdown or image files.
LinkPrefixForNonMarkdownResources string `yaml:"linkPrefixForNonMarkdownResources"`
// Transformations to apply for any file.
Transformations []*TransformationConfig
// GitIgnored specifies if output dir should be git ignored or not.
GitIgnored bool `yaml:"gitIgnored"`
// LocalLinksStyle sets linking style to be applied. If empty, we assume default style.
LocalLinksStyle LocalLinksStyle `yaml:"localLinksStyle"`
}
type TransformationConfig struct {
_glob glob.Glob
// Glob matches files using https://github.com/gobwas/glob.
// Glob is matched against the relative path of the file in the input directory in
// relation to the input directory. For example:
// InputDir: dir1, File found in dir1/a/b/c/file.md, the given glob will be matched
// against a/b/c/file.md.
// After first match, file is no longer matching other elements.
Glob string
// Path is an optional different path for the file to be moved.
// If not specified, file will be moved to the exact same position as in input directory.
// Use absolute path to point the absolute structure where `/` means output directory.
// If relative path is used, it will start in the directory the file is in the input directory.
// NOTE: All relative links will be moved accordingly inside such file.
// TODO(bwplotka): Explain ** and * suffixes and ability to specify "invalid" paths like "/../".
Path string
PopHeader *bool `yaml:"popHeader"`
// FrontMatter holds front matter transformations.
FrontMatter *MatterConfig `yaml:"frontMatter"`
// BackMatter holds back matter transformations.
BackMatter *MatterConfig `yaml:"backMatter"`
}
func (tr TransformationConfig) targetRelPath(relPath string) (_ string, err error) {
if tr.Path == "" {
return relPath, nil
}
if dir, file := filepath.Split(strings.TrimSuffix(tr.Glob, filepath.Ext(tr.Glob))); file == "**" {
relPath, err = filepath.Rel(dir, relPath)
if err != nil {
return "", err
}
}
currDir, currFile := filepath.Split(relPath)
targetDir, targetSuffix := filepath.Split(strings.TrimPrefix(tr.Path, "/"))
if strings.HasSuffix(tr.Path, "/*") {
targetSuffix = currFile
} else if strings.HasSuffix(tr.Path, "/**") {
if !filepath.IsAbs(tr.Path) {
return "", fmt.Errorf("path has to be absolute if suffix /** is used, got %v", tr.Path)
}
targetSuffix = relPath
targetDir = filepath.Dir(strings.TrimPrefix(tr.Path, "/"))
}
if !filepath.IsAbs(tr.Path) {
targetDir = filepath.Join(currDir, targetDir)
}
return filepath.Join(targetDir, targetSuffix), nil
}
type MatterConfig struct {
_template *template.Template
// Template represents Go template that will be rendered as matter.
// This will override any existing matter.
// TODO(bwplotka): Add add only option?
Template string
}
func ParseConfig(c []byte) (Config, error) {
cfg := Config{}
dec := yaml.NewDecoder(bytes.NewReader(c))
dec.KnownFields(true)
if err := dec.Decode(&cfg); err != nil {
return Config{}, fmt.Errorf("parsing template content %q: %w", string(c), err)
}
if cfg.InputDir == "" {
return Config{}, errors.New("inputDir field is required")
}
d, err := os.Stat(cfg.InputDir)
if err != nil {
return Config{}, err
}
if !d.IsDir() {
return Config{}, errors.New("inputDir field is not pointing to a directory")
}
cfg.InputDir, err = filepath.Abs(cfg.InputDir)
if err != nil {
return Config{}, err
}
cfg.InputDir = strings.TrimSuffix(cfg.InputDir, "/")
if cfg.OutputDir == "" {
return Config{}, errors.New("outputDir field is required")
}
cfg.OutputDir, err = filepath.Abs(cfg.OutputDir)
if err != nil {
return Config{}, err
}
cfg.OutputDir = strings.TrimSuffix(cfg.OutputDir, "/")
for _, f := range cfg.Transformations {
f._glob, err = glob.Compile(f.Glob, '/')
if err != nil {
return Config{}, fmt.Errorf("compiling glob %v: %w", f.Glob, err)
}
if f.FrontMatter != nil {
f.FrontMatter._template, err = template.New("").Parse(f.FrontMatter.Template)
if err != nil {
return Config{}, fmt.Errorf("compiling frontMatter template %v: %w", f.FrontMatter.Template, err)
}
}
if f.BackMatter != nil {
f.BackMatter._template, err = template.New("").Parse(f.BackMatter.Template)
if err != nil {
return Config{}, fmt.Errorf("compiling backMatter template %v: %w", f.BackMatter.Template, err)
}
}
}
return cfg, nil
}