Skip to content

Commit

Permalink
Fixed links.
Browse files Browse the repository at this point in the history
Signed-off-by: Bartlomiej Plotka <[email protected]>
  • Loading branch information
bwplotka committed May 18, 2021
1 parent 600e9bf commit f6f1466
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 54 deletions.
80 changes: 51 additions & 29 deletions pkg/mdformatter/mdformatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ package mdformatter
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"sort"
"strconv"

"github.com/Kunde21/markdownfmt/v2/markdown"
"github.com/bwplotka/mdox/pkg/gitdiff"
Expand All @@ -23,6 +21,7 @@ import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"gopkg.in/yaml.v3"
)

type SourceContext struct {
Expand Down Expand Up @@ -100,44 +99,67 @@ func (RemoveFrontMatter) Close() error { return nil }
type FormatFrontMatterTransformer struct{}

func (FormatFrontMatterTransformer) TransformFrontMatter(_ SourceContext, frontMatter map[string]interface{}) ([]byte, error) {
return FormatFrontMatter(frontMatter), nil
return FormatFrontMatter(frontMatter)
}

func FormatFrontMatter(frontMatter map[string]interface{}) []byte {
if len(frontMatter) == 0 {
return nil
func FormatFrontMatter(m map[string]interface{}) ([]byte, error) {
if len(m) == 0 {
return nil, nil
}

keys := make([]string, 0, len(frontMatter))
for k := range frontMatter {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Sort(sort.Reverse(sort.StringSlice(keys)))

b := bytes.NewBuffer([]byte("---"))
for _, k := range keys {
// Check if frontMatter[k] is a map.
frontMatterMap, isMap := frontMatter[k].(map[string]interface{})
if isMap {
// Loop through all nested keys.
_, _ = fmt.Fprintf(b, "\n%v:", k)
for key, val := range frontMatterMap {
if v, ok := val.(string); ok {
_, _ = fmt.Fprintf(b, "\n %v: %v", key, strconv.Quote(v))
continue
}
_, _ = fmt.Fprintf(b, "\n %v: %v", key, val)
}
continue
f := sortedFrontMatter{
m: m,
keys: keys,
}

b := bytes.NewBuffer([]byte("---\n"))
o, err := yaml.Marshal(f)
if err != nil {
return nil, errors.Wrap(err, "marshall front matter")
}
_, _ = b.Write(o)
_, _ = b.Write([]byte("---\n\n"))
return b.Bytes(), nil
}

var _ yaml.Marshaler = sortedFrontMatter{}

type sortedFrontMatter struct {
m map[string]interface{}
keys []string
}

func (f sortedFrontMatter) MarshalYAML() (interface{}, error) {
n := &yaml.Node{
Kind: yaml.MappingNode,
}

for _, k := range f.keys {
n.Content = append(n.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: k})

b, err := yaml.Marshal(f.m[k])
if err != nil {
return nil, errors.Wrap(err, "map marshal")
}
if f, ok := frontMatter[k].(string); ok {
_, _ = fmt.Fprintf(b, "\n%v: %v", k, strconv.Quote(f))
continue
v := &yaml.Node{}
if err := yaml.Unmarshal(b, v); err != nil {
return nil, err
}

// We expect a node of type document with single content containing other nodes.
if len(v.Content) != 1 {
return nil, errors.Errorf("unexpected node after unmarshalling interface: %#v", v)
}
_, _ = fmt.Fprintf(b, "\n%v: %v", k, frontMatter[k])
// TODO(bwplotka): This creates weird indentation, fix it.
n.Content = append(n.Content, v.Content[0])
}
_, _ = b.Write([]byte("\n---\n\n"))
return b.Bytes()
return n, nil
}

func (FormatFrontMatterTransformer) Close(SourceContext) error { return nil }
Expand Down
4 changes: 2 additions & 2 deletions pkg/transform/testdata/testproj/Proposals/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Proposals

[RelLink](../../../tmp/2/test1/_index.md)
[RelLink](../README.md)

[RelLink](../Team/doc.md)
[RelLink](../Team/doc.md)
2 changes: 1 addition & 1 deletion pkg/transform/testdata/testproj/Team/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

[RelLink](../README.md#group-handbook)

[RelLink](../Proposals/README.md)
[RelLink](../Proposals/README.md)
96 changes: 75 additions & 21 deletions pkg/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func Dir(ctx context.Context, logger log.Logger, configFile string) error {
}

var (
linkTransformer = &relLinkTransformer{outputDir: c.OutputDir, absRelNewPathByFile: map[string]string{}}
linkTransformer = &relLinkTransformer{outputDir: c.OutputDir, newAbsRelPathByOldAbsRelPath: map[string]string{}}
files []string
)

Expand All @@ -58,13 +58,16 @@ func Dir(ctx context.Context, logger log.Logger, configFile string) error {
if info.IsDir() || path == c.InputDir {
return nil
}
files = append(files, path)

// Copy while preserving structure and tolerating custom mapping.
// absRelPath is an absolute path, but relatively to input dir (has `/` upfront).
absRelPath := strings.TrimPrefix(path, c.InputDir)

target := filepath.Join(c.OutputDir, absRelPath)
defer func() {
files = append(files, target)
}()

t, ok := firstMatch(absRelPath, c.Transformations)
if !ok {
level.Debug(logger).Log("msg", "copying without transformation", "in", path, "absRelPath", absRelPath, "target", target)
Expand All @@ -74,7 +77,7 @@ func Dir(ctx context.Context, logger log.Logger, configFile string) error {
var opts []mdformatter.Option
newAbsRelPath := newTargetAbsRelPath(absRelPath, t)
if newAbsRelPath != absRelPath {
linkTransformer.absRelNewPathByFile[path] = newAbsRelPath
linkTransformer.newAbsRelPathByOldAbsRelPath[absRelPath] = newAbsRelPath
}

target = filepath.Join(c.OutputDir, newAbsRelPath)
Expand All @@ -84,10 +87,14 @@ func Dir(ctx context.Context, logger log.Logger, configFile string) error {
}

if t.FrontMatter != nil {
firstHeader, err := readFirstHeader(path)
firstHeader, rest, err := popFirstHeader(path)
if err != nil {
return errors.Wrap(err, "read first header")
}

if err := ioutil.WriteFile(target, rest, os.ModePerm); err != nil {
return err
}
opts = append(opts, mdformatter.WithFrontMatterTransformer(&frontMatterTransformer{c: t.FrontMatter, firstHeader: firstHeader}))
}
return mdformatter.Format(ctx, logger, []string{target}, opts...)
Expand All @@ -100,25 +107,62 @@ func Dir(ctx context.Context, logger log.Logger, configFile string) error {
}

type relLinkTransformer struct {
outputDir string
absRelNewPathByFile map[string]string
outputDir string
newAbsRelPathByOldAbsRelPath map[string]string
}

func (r *relLinkTransformer) TransformDestination(ctx mdformatter.SourceContext, destination []byte) ([]byte, error) {
d := string(destination)
if strings.Contains(d, "://") || filepath.IsAbs(d) {
split := strings.Split(string(destination), "#")
dest := split[0]
if strings.Contains(dest, "://") || filepath.IsAbs(dest) || strings.HasPrefix(string(destination), "#") {
return destination, nil
}
// TODO(bwplotka): Check if links are outside?
currentAbsRelPath := strings.TrimPrefix(ctx.Filepath, r.outputDir)
if filepath.Join(currentAbsRelPath, dest) == ctx.Filepath {
// Pointing to self.
_, file := filepath.Split(ctx.Filepath)
if len(split) > 1 {
return []byte(file + "#" + split[1]), nil
}
return []byte(file), nil
}

currentAbsRelDir := filepath.Dir(currentAbsRelPath)

// Do we changed?
change := ""
for n, old := range r.newAbsRelPathByOldAbsRelPath {
if old != currentAbsRelPath {
continue
}
c, err := filepath.Rel(filepath.Dir(old), filepath.Dir(n))
if err != nil {
return nil, err
}
change = c
break
}

// absTargetRelPath is an absolute path, but relatively to input dir (has `/` upfront).
absTargetRelPath := strings.TrimPrefix(ctx.Filepath, r.outputDir)
adjustedAbsRelDir := filepath.Join(currentAbsRelDir, change)
adjustedAbsRelDest := filepath.Join(adjustedAbsRelDir, dest)

if absNewRelPath, ok := r.absRelNewPathByFile[filepath.Join(absTargetRelPath, d)]; ok {
str, err := filepath.Rel(ctx.Filepath, filepath.Join(r.outputDir, absNewRelPath))
return []byte(str), err
// Does the link points to something that changed?
if absNewRelPath, ok := r.newAbsRelPathByOldAbsRelPath[adjustedAbsRelDest]; ok {
adjustedAbsRelDest = absNewRelPath
}
return destination, nil

newDest, err := filepath.Rel(currentAbsRelDir, adjustedAbsRelDest)
if err != nil {
return nil, err
}
if newDest == "." {
newDest = ""
}
if len(split) > 1 {
return []byte(strings.TrimPrefix(newDest, "/") + "#" + split[1]), nil
}
return []byte(strings.TrimPrefix(newDest, "/")), nil
}

func (r *relLinkTransformer) Close(mdformatter.SourceContext) error { return nil }
Expand All @@ -141,11 +185,12 @@ func (f *frontMatterTransformer) TransformFrontMatter(ctx mdformatter.SourceCont
}); err != nil {
return nil, err
}

m := map[string]interface{}{}
if err := yaml.Unmarshal(b.Bytes(), m); err != nil {
return nil, errors.Wrapf(err, "generated template for %v is not a valid yaml", ctx.Filepath)
}
return mdformatter.FormatFrontMatter(m), nil
return mdformatter.FormatFrontMatter(m)
}

func (f *frontMatterTransformer) Close(mdformatter.SourceContext) error { return nil }
Expand Down Expand Up @@ -201,20 +246,29 @@ func copyFiles(src, dst string) (err error) {
return err
}

// TODO(bwplotka): Use formatter.
func readFirstHeader(path string) (string, error) {
// TODO(bwplotka): Use formatter, remove the title etc.
// Super hacky for now.
func popFirstHeader(path string) (_ string, rest []byte, err error) {
file, err := os.Open(path)
if err != nil {
return "", nil
return "", nil, err
}
defer func() { _ = file.Close() }()
defer errcapture.Close(&err, file, "close file")

scanner := bufio.NewScanner(file)
for scanner.Scan() {
text := scanner.Text()
if strings.HasPrefix(text, "#") {
return text, scanner.Err()
if _, err := file.Seek(int64(len(text)), 0); err != nil {
return "", nil, errors.Wrap(err, "seek")
}
rest, err := ioutil.ReadAll(file)
if err != nil {
return "", nil, errors.Wrap(err, "read")
}

return strings.TrimPrefix(text, "# "), rest, scanner.Err()
}
}
return "<no header found>", nil
return "", nil, errors.New("No header found")
}
2 changes: 1 addition & 1 deletion pkg/transform/transform_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func TestTransform(t *testing.T) {
tmpDir = "./tmp"
testData = "testdata"
)
//defer func() { _ = os.RemoveAll(tmpDir) }()
defer func() { _ = os.RemoveAll(tmpDir) }()

logger := log.NewLogfmtLogger(os.Stdout)

Expand Down

0 comments on commit f6f1466

Please sign in to comment.