diff --git a/commands/commands.go b/commands/commands.go index 3fd40f3..717b328 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -7,12 +7,15 @@ import ( "io/fs" "os" "path/filepath" + "regexp" "strings" + "time" "embed" "github.com/facundoolano/jorge/config" "github.com/facundoolano/jorge/site" + "golang.org/x/text/unicode/norm" ) //go:embed all:initfiles @@ -34,9 +37,9 @@ func Init(projectDir string) error { return err } - siteName := prompt("site name") - siteUrl := prompt("site url") - siteAuthor := prompt("author") + siteName := Prompt("site name") + siteUrl := Prompt("site url") + siteAuthor := Prompt("author") // creating config and readme files manually, since I want to use the supplied config values in their // contents. (I don't want to render liquid templates in the WalkDir below since some of the initfiles @@ -80,6 +83,7 @@ func Init(projectDir string) error { if err != nil { return err } + fmt.Println("added", path) return targetFile.Sync() }) } @@ -108,7 +112,7 @@ func ensureEmptyProjectDir(projectDir string) error { } // Prompt the user for a string value -func prompt(label string) string { +func Prompt(label string) string { // https://dev.to/tidalcloud/interactive-cli-prompts-in-go-3bj9 var s string r := bufio.NewReader(os.Stdin) @@ -122,16 +126,68 @@ func prompt(label string) string { return strings.TrimSpace(s) } -func Post() error { - // prompt for title - // slugify - // fail if file already exist - // create a new .org file with the slug - // add front matter and org options - fmt.Println("not implemented yet") +func Post(root string, title string) error { + config, err := config.Load(root) + if err != nil { + return err + } + + now := time.Now() + slug := slugify(title) + filename := strings.ReplaceAll(config.PostFormat, ":title", slug) + filename = strings.ReplaceAll(config.PostFormat, ":year", string(now.Year())) + filename = strings.ReplaceAll(config.PostFormat, ":month", string(int(now.Month()))) + filename = strings.ReplaceAll(config.PostFormat, ":day", string(now.Day())) + path := filepath.Join(config.SrcDir, filename) + + // ensure the dir already exists + if err := os.MkdirAll(filepath.Dir(path), FILE_RW_MODE); err != nil { + return err + } + + // if file already exists, prompt user for a different one + if _, err := os.Stat(path); os.IsExist(err) { + fmt.Printf("%s already exists\n", path) + filename = Prompt("filename") + path = filepath.Join(config.SrcDir, filename) + } + + // initialize a template for the post + content := fmt.Sprintf(`--- +title: %s +date: %s +layout: post +lang: %s +tags: [] +---`, title, now.Format(time.RFC3339), config.Lang) + + // org files need some extra boilerplate + if filepath.Ext(path) == ".org" { + content += fmt.Sprintf(` +#+OPTIONS: toc:nil num:nil +#+LANGUAGE: %s`, config.Lang) + } + + if err := os.WriteFile(path, []byte(content), FILE_RW_MODE); err != nil { + return err + } + fmt.Println("added", path) return nil } +var nonWordRegex = regexp.MustCompile(`[^\w-]`) +var whitespaceRegex = regexp.MustCompile(`\s+`) + +func slugify(title string) string { + slug := strings.ToLower(title) + slug = strings.TrimSpace(title) + slug = norm.NFC.String(slug) + slug = nonWordRegex.ReplaceAllString(slug, "") + slug = whitespaceRegex.ReplaceAllString(slug, "-") + + return slug +} + // Read the files in src/ render them and copy the result to target/ func Build(root string) error { config, err := config.Load(root) diff --git a/config/config.go b/config/config.go index 192f6c4..69af09d 100644 --- a/config/config.go +++ b/config/config.go @@ -27,7 +27,8 @@ type Config struct { DataDir string SiteUrl string - SlugFormat string + PostFormat string + Lang string Minify bool LiveReload bool @@ -53,7 +54,8 @@ func Load(rootDir string) (*Config, error) { LayoutsDir: filepath.Join(rootDir, "layouts"), IncludesDir: filepath.Join(rootDir, "includes"), DataDir: filepath.Join(rootDir, "data"), - SlugFormat: ":title", + PostFormat: "blog/:title.org", + Lang: "en", Minify: true, LiveReload: false, LinkStatic: false, @@ -80,8 +82,11 @@ func Load(rootDir string) (*Config, error) { if url, found := config.overrides["url"]; found { config.SiteUrl = url.(string) } - if slug, found := config.overrides["url"]; found { - config.SlugFormat = slug.(string) + if format, found := config.overrides["post_format"]; found { + config.PostFormat = format.(string) + } + if format, found := config.overrides["lang"]; found { + config.Lang = format.(string) } return config, nil diff --git a/go.mod b/go.mod index 3e445a9..544faed 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/tdewolff/minify/v2 v2.20.16 github.com/yuin/goldmark v1.7.0 golang.org/x/net v0.0.0-20201224014010-6772e930b67b + golang.org/x/text v0.3.3 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b ) diff --git a/go.sum b/go.sum index 8aa6335..c85cb3c 100644 --- a/go.sum +++ b/go.sum @@ -29,6 +29,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/main.go b/main.go index 8d9c864..0ac514d 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,14 @@ func run(args []string) error { } return commands.Build(rootDir) case "post": - return commands.Post() + var title string + if len(os.Args) >= 3 { + title = os.Args[2] + } else { + title = commands.Prompt("title") + } + rootDir := "." + return commands.Post(rootDir, title) case "serve": rootDir := "." if len(os.Args) > 2 { diff --git a/site/site.go b/site/site.go index f119fdf..255d480 100644 --- a/site/site.go +++ b/site/site.go @@ -338,7 +338,7 @@ func writeToFile(targetPath string, source io.Reader) error { return err } - fmt.Println("added", targetPath) + fmt.Println("wrote", targetPath) return targetFile.Sync() }