2017-07-01 05:10:58 +02:00
|
|
|
// Package plugins holds emulated Jekyll plugins.
|
|
|
|
//
|
|
|
|
// Unlike Jekyll, these are baked into the executable -- both because as of 2017.07 package "plugin' currently
|
|
|
|
// works only on Linux, but also because the gojekyll implementation is immature and any possible interfaces
|
|
|
|
// are far from baked.
|
2017-06-30 18:53:34 +02:00
|
|
|
package plugins
|
|
|
|
|
|
|
|
import (
|
2017-07-24 19:27:05 +02:00
|
|
|
"context"
|
2017-06-30 18:53:34 +02:00
|
|
|
"fmt"
|
2017-07-24 19:27:05 +02:00
|
|
|
"os/exec"
|
2017-07-09 06:21:02 +02:00
|
|
|
"regexp"
|
2017-07-24 19:27:05 +02:00
|
|
|
"strings"
|
2017-06-30 18:53:34 +02:00
|
|
|
|
2017-07-24 19:27:05 +02:00
|
|
|
"github.com/google/go-github/github"
|
2017-07-09 21:07:14 +02:00
|
|
|
"github.com/kyokomi/emoji"
|
2017-07-09 03:41:35 +02:00
|
|
|
"github.com/osteele/gojekyll/config"
|
|
|
|
"github.com/osteele/gojekyll/pages"
|
2017-07-09 22:17:20 +02:00
|
|
|
"github.com/osteele/gojekyll/utils"
|
2017-07-01 01:37:31 +02:00
|
|
|
"github.com/osteele/liquid"
|
2017-06-30 18:53:34 +02:00
|
|
|
)
|
|
|
|
|
2017-07-09 03:41:35 +02:00
|
|
|
// Site is the site interface that is available to a plugin.
|
|
|
|
type Site interface {
|
|
|
|
AddDocument(pages.Document, bool)
|
|
|
|
Config() *config.Config
|
2017-07-12 13:10:52 +02:00
|
|
|
TemplateEngine() *liquid.Engine
|
2017-07-09 03:41:35 +02:00
|
|
|
Pages() []pages.Page
|
|
|
|
}
|
|
|
|
|
|
|
|
// Plugin describes the hooks that a plugin can override.
|
|
|
|
type Plugin interface {
|
2017-07-24 19:27:05 +02:00
|
|
|
Initialize(Site) error
|
2017-07-10 15:17:31 +02:00
|
|
|
ConfigureTemplateEngine(*liquid.Engine) error
|
2017-07-24 19:27:05 +02:00
|
|
|
ModifySiteDrop(Site, map[string]interface{}) error
|
|
|
|
PostRead(Site) error
|
2017-07-09 06:21:02 +02:00
|
|
|
PostRender([]byte) []byte
|
2017-07-09 03:41:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
type plugin struct{}
|
|
|
|
|
2017-07-24 19:27:05 +02:00
|
|
|
func (p plugin) Initialize(Site) error { return nil }
|
|
|
|
func (p plugin) ConfigureTemplateEngine(*liquid.Engine) error { return nil }
|
|
|
|
func (p plugin) ModifySiteDrop(Site, map[string]interface{}) error { return nil }
|
|
|
|
func (p plugin) PostRead(Site) error { return nil }
|
|
|
|
func (p plugin) PostRender(b []byte) []byte { return b }
|
2017-07-09 03:41:35 +02:00
|
|
|
|
2017-07-09 04:47:50 +02:00
|
|
|
// Lookup returns a plugin if it has been registered.
|
|
|
|
func Lookup(name string) (Plugin, bool) {
|
|
|
|
p, found := directory[name]
|
|
|
|
return p, found
|
2017-07-09 03:41:35 +02:00
|
|
|
}
|
|
|
|
|
2017-07-01 05:10:58 +02:00
|
|
|
// Install installs a plugin from the plugin directory.
|
2017-07-09 04:47:50 +02:00
|
|
|
func Install(names []string, site Site) {
|
|
|
|
for _, name := range names {
|
|
|
|
p, found := directory[name]
|
|
|
|
if found {
|
|
|
|
if err := p.Initialize(site); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fmt.Printf("warning: gojekyll does not emulate the %s plugin.\n", name)
|
2017-06-30 18:53:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-09 04:47:50 +02:00
|
|
|
var directory = map[string]Plugin{}
|
2017-06-30 18:53:34 +02:00
|
|
|
|
2017-07-09 01:57:41 +02:00
|
|
|
// register installs a plugin in the plugin directory.
|
2017-07-09 04:47:50 +02:00
|
|
|
func register(name string, p Plugin) {
|
|
|
|
directory[name] = p
|
2017-06-30 18:53:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
2017-07-24 19:27:05 +02:00
|
|
|
register("jemoji", jemojiPlugin{})
|
|
|
|
register("jekyll-github-metadata", jekyllGithubMetadataPlugin{})
|
2017-07-09 06:21:02 +02:00
|
|
|
register("jekyll-mentions", jekyllMentionsPlugin{})
|
2017-07-24 18:18:24 +02:00
|
|
|
register("jekyll-optional-front-matter", jekyllOptionalFrontMatterPlugin{})
|
2017-07-05 22:54:48 +02:00
|
|
|
|
|
|
|
// the following plugins are always active
|
2017-07-02 05:06:47 +02:00
|
|
|
// no warning but effect; the server runs in this mode anyway
|
2017-07-09 04:47:50 +02:00
|
|
|
register("jekyll-live-reload", plugin{})
|
|
|
|
register("jekyll-sass-converter", plugin{})
|
2017-06-30 18:53:34 +02:00
|
|
|
}
|
2017-07-01 16:37:11 +02:00
|
|
|
|
2017-07-09 21:07:14 +02:00
|
|
|
// Some small plugins are below. More involved plugins are in separate files.
|
|
|
|
|
|
|
|
// jekyll-jemoji
|
|
|
|
|
2017-07-24 19:27:05 +02:00
|
|
|
type jemojiPlugin struct{ plugin }
|
2017-07-09 21:07:14 +02:00
|
|
|
|
2017-07-24 19:27:05 +02:00
|
|
|
func (p jemojiPlugin) PostRender(b []byte) []byte {
|
2017-07-09 22:17:20 +02:00
|
|
|
return utils.ApplyToHTMLText(b, func(s string) string {
|
2017-07-09 21:07:14 +02:00
|
|
|
s = emoji.Sprint(s)
|
|
|
|
return s
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-07-24 19:27:05 +02:00
|
|
|
// jekyll-github-metadata
|
|
|
|
|
|
|
|
type jekyllGithubMetadataPlugin struct{ plugin }
|
|
|
|
|
|
|
|
func (p jekyllGithubMetadataPlugin) ModifySiteDrop(s Site, d map[string]interface{}) error {
|
|
|
|
cmd := exec.Command("git", "remote", "-v") // nolint: gas
|
|
|
|
cmd.Dir = s.Config().SourceDir()
|
|
|
|
out, err := cmd.Output()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
m := regexp.MustCompile(`origin\s+https://github.com/(.+?)/(.+)\.git\b`).FindStringSubmatch(string(out))
|
|
|
|
owner, name := m[1], m[2]
|
|
|
|
nwo := fmt.Sprintf("%s/%s", owner, name)
|
|
|
|
client := github.NewClient(nil)
|
|
|
|
var ctx = context.Background()
|
|
|
|
repo, _, err := client.Repositories.Get(ctx, owner, name)
|
|
|
|
userPage := *repo.Name == fmt.Sprintf("%s.github.com", strings.ToLower(*repo.Owner.Login))
|
|
|
|
gh := map[string]interface{}{
|
|
|
|
"clone_url": repo.CloneURL,
|
|
|
|
"language": repo.Language,
|
|
|
|
"owner_gravatar_url": repo.Owner.GravatarID,
|
|
|
|
"owner_name": repo.Owner.Login,
|
|
|
|
"owner_url": repo.Owner.URL,
|
|
|
|
"project_tagline": repo.Description,
|
|
|
|
"project_title": repo.Name, // TODO is this right?
|
|
|
|
"repository_name": repo.Name,
|
|
|
|
"repository_nwo": nwo,
|
|
|
|
"repository_url": repo.URL,
|
|
|
|
"releases_url": repo.ReleasesURL,
|
|
|
|
"latest_release_url": repo.URL,
|
|
|
|
"issues_url": repo.IssuesURL,
|
|
|
|
"show_downloads?": repo.HasDownloads,
|
|
|
|
"repo_clone_url": repo.GitURL,
|
|
|
|
"is_project_page": !userPage,
|
|
|
|
"is_user_page": userPage,
|
|
|
|
// TODO
|
|
|
|
// contributors environment public_repositories releases releases_url
|
|
|
|
// show_downloads tar_url zip_url url versions wiki_url
|
|
|
|
}
|
|
|
|
d["github"] = gh
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-07-09 21:07:14 +02:00
|
|
|
// jekyll-mentions
|
|
|
|
|
2017-07-09 06:21:02 +02:00
|
|
|
type jekyllMentionsPlugin struct{ plugin }
|
|
|
|
|
|
|
|
var mentionPattern = regexp.MustCompile(`@(\w+)`)
|
|
|
|
|
|
|
|
func (p jekyllMentionsPlugin) PostRender(b []byte) []byte {
|
2017-07-09 22:17:20 +02:00
|
|
|
return utils.ApplyToHTMLText(b, func(s string) string {
|
2017-07-09 06:21:02 +02:00
|
|
|
return mentionPattern.ReplaceAllString(s, `<a href="https://github.com/$1" class="user-mention">@$1</a>`)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-07-24 18:18:24 +02:00
|
|
|
// jekyll-optional-front-matter
|
|
|
|
|
|
|
|
type jekyllOptionalFrontMatterPlugin struct{ plugin }
|
|
|
|
|
|
|
|
var requireFrontMatterExclude = []string{
|
|
|
|
"README",
|
|
|
|
"LICENSE",
|
|
|
|
"LICENCE",
|
|
|
|
"COPYING",
|
|
|
|
"CODE_OF_CONDUCT",
|
|
|
|
"CONTRIBUTING",
|
|
|
|
"ISSUE_TEMPLATE",
|
|
|
|
"PULL_REQUEST_TEMPLATE",
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p jekyllOptionalFrontMatterPlugin) Initialize(s Site) error {
|
|
|
|
m := map[string]bool{}
|
|
|
|
for _, k := range requireFrontMatterExclude {
|
|
|
|
m[k] = true
|
|
|
|
}
|
|
|
|
s.Config().RequireFrontMatter = false
|
|
|
|
s.Config().RequireFrontMatterExclude = m
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-07-09 21:07:14 +02:00
|
|
|
// helpers
|
|
|
|
|
2017-07-11 18:03:52 +02:00
|
|
|
// func (p plugin) stubbed(name string) {
|
|
|
|
// fmt.Printf("warning: gojekyll does not emulate the %s plugin. Some tags have been stubbed to prevent errors.\n", name)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// func (p plugin) makeUnimplementedTag(pluginName string) liquid.Renderer {
|
|
|
|
// warned := false
|
|
|
|
// return func(ctx render.Context) (string, error) {
|
|
|
|
// if !warned {
|
|
|
|
// fmt.Printf("The %q tag in the %q plugin has not been implemented.\n", ctx.TagName(), pluginName)
|
|
|
|
// warned = true
|
|
|
|
// }
|
|
|
|
// return fmt.Sprintf(`<!-- unimplemented tag: %q -->`, ctx.TagName()), nil
|
|
|
|
// }
|
|
|
|
// }
|