ref: 75c61236f159d9315849c88c73d21a4678994ee9
parent: 666ddd237791b56fd048992dca9a27d1af50a10e
	author: Anthony Fok <foka@debian.org>
	date: Tue Dec  1 22:36:18 EST 2015
	
Move `import jekyll` functions to import_jekyll.go Also rename import_test.go to import_jekyll_test.go
--- a/commands/import.go
+++ b/commands/import.go
@@ -14,31 +14,9 @@
package commands
import (
- "bytes"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "strings"
- "time"
-
- "github.com/spf13/cast"
"github.com/spf13/cobra"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
- "github.com/spf13/hugo/hugolib"
- "github.com/spf13/hugo/parser"
- jww "github.com/spf13/jwalterweatherman"
)
-func init() {- importCmd.AddCommand(importJekyllCmd)
-}
-
 var importCmd = &cobra.Command{Use: "import",
Short: "Import your site from others.",
@@ -48,436 +26,6 @@
Run: nil,
}
-var importJekyllCmd = &cobra.Command{- Use: "jekyll",
- Short: "hugo import from Jekyll",
- Long: `hugo import from Jekyll.
-
-Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
- Run: importFromJekyll,
-}
-
-func importFromJekyll(cmd *cobra.Command, args []string) {- jww.SetLogThreshold(jww.LevelTrace)
- jww.SetStdoutThreshold(jww.LevelWarn)
-
-	if len(args) < 2 {- jww.ERROR.Println(`Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.")
- return
- }
-
- jekyllRoot, err := filepath.Abs(filepath.Clean(args[0]))
-	if err != nil {-		jww.ERROR.Println("Path error:", args[0])- return
- }
-
- targetDir, err := filepath.Abs(filepath.Clean(args[1]))
-	if err != nil {-		jww.ERROR.Println("Path error:", args[1])- return
- }
-
- createSiteFromJekyll(jekyllRoot, targetDir)
-
-	jww.INFO.Println("Import Jekyll from:", jekyllRoot, "to:", targetDir)-	fmt.Println("Importing...")-
- fileCount := 0
-	callback := func(path string, fi os.FileInfo, err error) error {-		if err != nil {- return err
- }
-
-		if fi.IsDir() {- return nil
- }
-
- relPath, err := filepath.Rel(jekyllRoot, path)
-		if err != nil {-			jww.ERROR.Println("Get rel path error:", path)- return err
- }
-
- relPath = filepath.ToSlash(relPath)
- var draft bool = false
-
-		switch {- case strings.HasPrefix(relPath, "_posts/"):
-			relPath = "content/post" + relPath[len("_posts"):]- case strings.HasPrefix(relPath, "_drafts/"):
-			relPath = "content/draft" + relPath[len("_drafts"):]- draft = true
- default:
- return nil
- }
-
- fileCount++
- return convertJekyllPost(path, relPath, targetDir, draft)
- }
-
- err = filepath.Walk(jekyllRoot, callback)
-
-	if err != nil {- fmt.Println(err)
-	} else {-		fmt.Println("Congratulations!", fileCount, "posts imported!")-		fmt.Println("Now, start Hugo by yourself: \n" +- "$ git clone https://github.com/spf13/herring-cove.git " + args[1] + "/themes/herring-cove")
-		fmt.Println("$ cd " + args[1] + "\n$ hugo server -w --theme=herring-cove")- }
-}
-
-func createSiteFromJekyll(jekyllRoot, targetDir string) {- mkdir(targetDir, "layouts")
- mkdir(targetDir, "content")
- mkdir(targetDir, "archetypes")
- mkdir(targetDir, "static")
- mkdir(targetDir, "data")
- mkdir(targetDir, "themes")
-
- jekyllConfig := loadJekyllConfig(jekyllRoot)
- createConfigFromJekyll(targetDir, "yaml", jekyllConfig)
-
- copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"))
-}
-
-func loadJekyllConfig(jekyllRoot string) map[string]interface{} {- fs := hugofs.SourceFs
- path := filepath.Join(jekyllRoot, "_config.yml")
-
- exists, err := helpers.Exists(path, fs)
-
-	if err != nil || !exists {- return nil
- }
-
- f, err := fs.Open(path)
-	if err != nil {- return nil
- }
-
- defer f.Close()
-
- b, err := ioutil.ReadAll(f)
-
-	if err != nil {- return nil
- }
-
- c, err := parser.HandleYAMLMetaData(b)
-
-	if err != nil {- return nil
- }
-
-	return c.(map[string]interface{})-}
-
-func createConfigFromJekyll(inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {- title := "My New Hugo Site"
- baseurl := "http://replace-this-with-your-hugo-site.com/"
-
-	for key, value := range jekyllConfig {- lowerKey := strings.ToLower(key)
-
-		switch lowerKey {- case "title":
-			if str, ok := value.(string); ok {- title = str
- }
-
- case "url":
-			if str, ok := value.(string); ok {- baseurl = str
- }
- }
- }
-
-	in := map[string]interface{}{- "baseurl": baseurl,
- "title": title,
- "languageCode": "en-us",
- "disablePathToLower": true,
- }
- kind = parser.FormatSanitize(kind)
-
- by, err := parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind))
-	if err != nil {- return err
- }
-
- err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.SourceFs)
-	if err != nil {- return
- }
-
- return nil
-}
-
-func copyFile(source string, dest string) (err error) {- sf, err := os.Open(source)
-	if err != nil {- return err
- }
- defer sf.Close()
- df, err := os.Create(dest)
-	if err != nil {- return err
- }
- defer df.Close()
- _, err = io.Copy(df, sf)
-	if err == nil {- si, err := os.Stat(source)
-		if err != nil {- err = os.Chmod(dest, si.Mode())
- }
-
- }
- return
-}
-
-func copyDir(source string, dest string) (err error) {- fi, err := os.Stat(source)
-	if err != nil {- return err
- }
-	if !fi.IsDir() {- return errors.New(source + " is not a directory")
- }
- err = os.MkdirAll(dest, fi.Mode())
-	if err != nil {- return err
- }
- entries, err := ioutil.ReadDir(source)
-	for _, entry := range entries {- sfp := filepath.Join(source, entry.Name())
- dfp := filepath.Join(dest, entry.Name())
-		if entry.IsDir() {- err = copyDir(sfp, dfp)
-			if err != nil {- jww.ERROR.Println(err)
- }
-		} else {- err = copyFile(sfp, dfp)
-			if err != nil {- jww.ERROR.Println(err)
- }
- }
-
- }
- return nil
-}
-
-func copyJekyllFilesAndFolders(jekyllRoot string, dest string) (err error) {- fi, err := os.Stat(jekyllRoot)
-	if err != nil {- return err
- }
-	if !fi.IsDir() {- return errors.New(jekyllRoot + " is not a directory")
- }
- err = os.MkdirAll(dest, fi.Mode())
-	if err != nil {- return err
- }
- entries, err := ioutil.ReadDir(jekyllRoot)
-	for _, entry := range entries {- sfp := filepath.Join(jekyllRoot, entry.Name())
- dfp := filepath.Join(dest, entry.Name())
-		if entry.IsDir() {-			if entry.Name()[0] != '_' && entry.Name()[0] != '.' {- err = copyDir(sfp, dfp)
-				if err != nil {- jww.ERROR.Println(err)
- }
- }
-		} else {- lowerEntryName := strings.ToLower(entry.Name())
-			exceptSuffix := []string{".md", ".markdown", ".html", ".htm",- ".xml", ".textile", "rakefile", "gemfile", ".lock"}
- isExcept := false
-			for _, suffix := range exceptSuffix {-				if strings.HasSuffix(lowerEntryName, suffix) {- isExcept = true
- break
- }
- }
-
-			if !isExcept && entry.Name()[0] != '.' && entry.Name()[0] != '_' {- err = copyFile(sfp, dfp)
-				if err != nil {- jww.ERROR.Println(err)
- }
- }
- }
-
- }
- return nil
-}
-
-func parseJekyllFilename(filename string) (time.Time, string, error) {- re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`)
- r := re.FindAllStringSubmatch(filename, -1)
-	if len(r) == 0 {-		return time.Now(), "", errors.New("filename not match")- }
-
-	postDate, err := time.Parse("2006-01-02", r[0][1])-	if err != nil {- return time.Now(), "", err
- }
-
- postName := r[0][2]
-
- return postDate, postName, nil
-}
-
-func convertJekyllPost(path, relPath, targetDir string, draft bool) error {-	jww.TRACE.Println("Converting", path)-
- filename := filepath.Base(path)
- postDate, postName, err := parseJekyllFilename(filename)
-	if err != nil {-		jww.ERROR.Println("Parse filename error:", filename)- return err
- }
-
- jww.TRACE.Println(filename, postDate, postName)
-
- targetFile := filepath.Join(targetDir, relPath)
- targetParentDir := filepath.Dir(targetFile)
- os.MkdirAll(targetParentDir, 0777)
-
- contentBytes, err := ioutil.ReadFile(path)
-	if err != nil {-		jww.ERROR.Println("Read file error:", path)- return err
- }
-
- psr, err := parser.ReadFrom(bytes.NewReader(contentBytes))
-	if err != nil {-		jww.ERROR.Println("Parse file error:", path)- return err
- }
-
- metadata, err := psr.Metadata()
-	if err != nil {-		jww.ERROR.Println("Processing file error:", path)- return err
- }
-
- newmetadata, err := convertJekyllMetaData(metadata, postName, postDate, draft)
-	if err != nil {-		jww.ERROR.Println("Convert metadata error:", path)- return err
- }
-
- jww.TRACE.Println(newmetadata)
- content := convertJekyllContent(newmetadata, string(psr.Content()))
-
- page, err := hugolib.NewPage(filename)
-	if err != nil {-		jww.ERROR.Println("New page error", filename)- return err
- }
-
- page.SetDir(targetParentDir)
- page.SetSourceContent([]byte(content))
-	page.SetSourceMetaData(newmetadata, parser.FormatToLeadRune("yaml"))- page.SaveSourceAs(targetFile)
-
-	jww.TRACE.Println("Target file:", targetFile)-
- return nil
-}
-
-func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {-	url := postDate.Format("/2006/01/02/") + postName + "/"-
- metadata, err := cast.ToStringMapE(m)
-	if err != nil {- return nil, err
- }
-
-	if draft {- metadata["draft"] = true
- }
-
-	for key, value := range metadata {- lowerKey := strings.ToLower(key)
-
-		switch lowerKey {- case "layout":
- delete(metadata, key)
- case "permalink":
-			if str, ok := value.(string); ok {- url = str
- }
- delete(metadata, key)
- case "category":
-			if str, ok := value.(string); ok {-				metadata["categories"] = []string{str}- }
- delete(metadata, key)
- case "excerpt_separator":
-			if key != lowerKey {- delete(metadata, key)
- metadata[lowerKey] = value
- }
- case "date":
-			if str, ok := value.(string); ok {- re := regexp.MustCompile(`(\d+):(\d+):(\d+)`)
- r := re.FindAllStringSubmatch(str, -1)
-				if len(r) > 0 {- hour, _ := strconv.Atoi(r[0][1])
- minute, _ := strconv.Atoi(r[0][2])
- second, _ := strconv.Atoi(r[0][3])
- postDate = time.Date(postDate.Year(), postDate.Month(), postDate.Day(), hour, minute, second, 0, time.UTC)
- }
- }
- delete(metadata, key)
- }
-
- }
-
- metadata["url"] = url
- metadata["date"] = postDate.Format(time.RFC3339)
-
- return metadata, nil
-}
-
-func convertJekyllContent(m interface{}, content string) string {- metadata, _ := cast.ToStringMapE(m)
-
- lines := strings.Split(content, "\n")
- var resultLines []string
-	for _, line := range lines {- resultLines = append(resultLines, strings.Trim(line, "\r\n"))
- }
-
- content = strings.Join(resultLines, "\n")
-
- excerptSep := "<!--more-->"
-	if value, ok := metadata["excerpt_separator"]; ok {-		if str, strOk := value.(string); strOk {- content = strings.Replace(content, strings.TrimSpace(str), excerptSep, -1)
- }
- }
-
-	replaceList := []struct {- re *regexp.Regexp
- replace string
-	}{-		{regexp.MustCompile("<!-- more -->"), "<!--more-->"},-		{regexp.MustCompile(`\{%\s*raw\s*%\}\s*(.*?)\s*\{%\s*endraw\s*%\}`), "$1"},-		{regexp.MustCompile(`{%\s*highlight\s*(.*?)\s*%}`), "{{< highlight $1 >}}"},-		{regexp.MustCompile(`{%\s*endhighlight\s*%}`), "{{< / highlight >}}"},- }
-
-	for _, replace := range replaceList {- content = replace.re.ReplaceAllString(content, replace.replace)
- }
-
- return content
+func init() {+ importCmd.AddCommand(importJekyllCmd)
}
--- /dev/null
+++ b/commands/import_jekyll.go
@@ -1,0 +1,470 @@
+// Copyright © 2015 Steve Francia <spf@spf13.com>.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package commands
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "strings"
+ "time"
+
+ "github.com/spf13/cast"
+ "github.com/spf13/cobra"
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/hugolib"
+ "github.com/spf13/hugo/parser"
+ jww "github.com/spf13/jwalterweatherman"
+)
+
+var importJekyllCmd = &cobra.Command{+ Use: "jekyll",
+ Short: "hugo import from Jekyll",
+ Long: `hugo import from Jekyll.
+
+Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.",
+ Run: importFromJekyll,
+}
+
+func importFromJekyll(cmd *cobra.Command, args []string) {+ jww.SetLogThreshold(jww.LevelTrace)
+ jww.SetStdoutThreshold(jww.LevelWarn)
+
+	if len(args) < 2 {+ jww.ERROR.Println(`Import from Jekyll requires two paths, e.g. ` + "`hugo import jekyll jekyll_root_path target_path`.")
+ return
+ }
+
+ jekyllRoot, err := filepath.Abs(filepath.Clean(args[0]))
+	if err != nil {+		jww.ERROR.Println("Path error:", args[0])+ return
+ }
+
+ targetDir, err := filepath.Abs(filepath.Clean(args[1]))
+	if err != nil {+		jww.ERROR.Println("Path error:", args[1])+ return
+ }
+
+ createSiteFromJekyll(jekyllRoot, targetDir)
+
+	jww.INFO.Println("Import Jekyll from:", jekyllRoot, "to:", targetDir)+	fmt.Println("Importing...")+
+ fileCount := 0
+	callback := func(path string, fi os.FileInfo, err error) error {+		if err != nil {+ return err
+ }
+
+		if fi.IsDir() {+ return nil
+ }
+
+ relPath, err := filepath.Rel(jekyllRoot, path)
+		if err != nil {+			jww.ERROR.Println("Get rel path error:", path)+ return err
+ }
+
+ relPath = filepath.ToSlash(relPath)
+ var draft bool = false
+
+		switch {+ case strings.HasPrefix(relPath, "_posts/"):
+			relPath = "content/post" + relPath[len("_posts"):]+ case strings.HasPrefix(relPath, "_drafts/"):
+			relPath = "content/draft" + relPath[len("_drafts"):]+ draft = true
+ default:
+ return nil
+ }
+
+ fileCount++
+ return convertJekyllPost(path, relPath, targetDir, draft)
+ }
+
+ err = filepath.Walk(jekyllRoot, callback)
+
+	if err != nil {+ fmt.Println(err)
+	} else {+		fmt.Println("Congratulations!", fileCount, "posts imported!")+		fmt.Println("Now, start Hugo by yourself: \n" ++ "$ git clone https://github.com/spf13/herring-cove.git " + args[1] + "/themes/herring-cove")
+		fmt.Println("$ cd " + args[1] + "\n$ hugo server -w --theme=herring-cove")+ }
+}
+
+func createSiteFromJekyll(jekyllRoot, targetDir string) {+ mkdir(targetDir, "layouts")
+ mkdir(targetDir, "content")
+ mkdir(targetDir, "archetypes")
+ mkdir(targetDir, "static")
+ mkdir(targetDir, "data")
+ mkdir(targetDir, "themes")
+
+ jekyllConfig := loadJekyllConfig(jekyllRoot)
+ createConfigFromJekyll(targetDir, "yaml", jekyllConfig)
+
+ copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"))
+}
+
+func loadJekyllConfig(jekyllRoot string) map[string]interface{} {+ fs := hugofs.SourceFs
+ path := filepath.Join(jekyllRoot, "_config.yml")
+
+ exists, err := helpers.Exists(path, fs)
+
+	if err != nil || !exists {+ return nil
+ }
+
+ f, err := fs.Open(path)
+	if err != nil {+ return nil
+ }
+
+ defer f.Close()
+
+ b, err := ioutil.ReadAll(f)
+
+	if err != nil {+ return nil
+ }
+
+ c, err := parser.HandleYAMLMetaData(b)
+
+	if err != nil {+ return nil
+ }
+
+	return c.(map[string]interface{})+}
+
+func createConfigFromJekyll(inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {+ title := "My New Hugo Site"
+ baseurl := "http://replace-this-with-your-hugo-site.com/"
+
+	for key, value := range jekyllConfig {+ lowerKey := strings.ToLower(key)
+
+		switch lowerKey {+ case "title":
+			if str, ok := value.(string); ok {+ title = str
+ }
+
+ case "url":
+			if str, ok := value.(string); ok {+ baseurl = str
+ }
+ }
+ }
+
+	in := map[string]interface{}{+ "baseurl": baseurl,
+ "title": title,
+ "languageCode": "en-us",
+ "disablePathToLower": true,
+ }
+ kind = parser.FormatSanitize(kind)
+
+ by, err := parser.InterfaceToConfig(in, parser.FormatToLeadRune(kind))
+	if err != nil {+ return err
+ }
+
+ err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.SourceFs)
+	if err != nil {+ return
+ }
+
+ return nil
+}
+
+func copyFile(source string, dest string) (err error) {+ sf, err := os.Open(source)
+	if err != nil {+ return err
+ }
+ defer sf.Close()
+ df, err := os.Create(dest)
+	if err != nil {+ return err
+ }
+ defer df.Close()
+ _, err = io.Copy(df, sf)
+	if err == nil {+ si, err := os.Stat(source)
+		if err != nil {+ err = os.Chmod(dest, si.Mode())
+ }
+
+ }
+ return
+}
+
+func copyDir(source string, dest string) (err error) {+ fi, err := os.Stat(source)
+	if err != nil {+ return err
+ }
+	if !fi.IsDir() {+ return errors.New(source + " is not a directory")
+ }
+ err = os.MkdirAll(dest, fi.Mode())
+	if err != nil {+ return err
+ }
+ entries, err := ioutil.ReadDir(source)
+	for _, entry := range entries {+ sfp := filepath.Join(source, entry.Name())
+ dfp := filepath.Join(dest, entry.Name())
+		if entry.IsDir() {+ err = copyDir(sfp, dfp)
+			if err != nil {+ jww.ERROR.Println(err)
+ }
+		} else {+ err = copyFile(sfp, dfp)
+			if err != nil {+ jww.ERROR.Println(err)
+ }
+ }
+
+ }
+ return nil
+}
+
+func copyJekyllFilesAndFolders(jekyllRoot string, dest string) (err error) {+ fi, err := os.Stat(jekyllRoot)
+	if err != nil {+ return err
+ }
+	if !fi.IsDir() {+ return errors.New(jekyllRoot + " is not a directory")
+ }
+ err = os.MkdirAll(dest, fi.Mode())
+	if err != nil {+ return err
+ }
+ entries, err := ioutil.ReadDir(jekyllRoot)
+	for _, entry := range entries {+ sfp := filepath.Join(jekyllRoot, entry.Name())
+ dfp := filepath.Join(dest, entry.Name())
+		if entry.IsDir() {+			if entry.Name()[0] != '_' && entry.Name()[0] != '.' {+ err = copyDir(sfp, dfp)
+				if err != nil {+ jww.ERROR.Println(err)
+ }
+ }
+		} else {+ lowerEntryName := strings.ToLower(entry.Name())
+			exceptSuffix := []string{".md", ".markdown", ".html", ".htm",+ ".xml", ".textile", "rakefile", "gemfile", ".lock"}
+ isExcept := false
+			for _, suffix := range exceptSuffix {+				if strings.HasSuffix(lowerEntryName, suffix) {+ isExcept = true
+ break
+ }
+ }
+
+			if !isExcept && entry.Name()[0] != '.' && entry.Name()[0] != '_' {+ err = copyFile(sfp, dfp)
+				if err != nil {+ jww.ERROR.Println(err)
+ }
+ }
+ }
+
+ }
+ return nil
+}
+
+func parseJekyllFilename(filename string) (time.Time, string, error) {+ re := regexp.MustCompile(`(\d+-\d+-\d+)-(.+)\..*`)
+ r := re.FindAllStringSubmatch(filename, -1)
+	if len(r) == 0 {+		return time.Now(), "", errors.New("filename not match")+ }
+
+	postDate, err := time.Parse("2006-01-02", r[0][1])+	if err != nil {+ return time.Now(), "", err
+ }
+
+ postName := r[0][2]
+
+ return postDate, postName, nil
+}
+
+func convertJekyllPost(path, relPath, targetDir string, draft bool) error {+	jww.TRACE.Println("Converting", path)+
+ filename := filepath.Base(path)
+ postDate, postName, err := parseJekyllFilename(filename)
+	if err != nil {+		jww.ERROR.Println("Parse filename error:", filename)+ return err
+ }
+
+ jww.TRACE.Println(filename, postDate, postName)
+
+ targetFile := filepath.Join(targetDir, relPath)
+ targetParentDir := filepath.Dir(targetFile)
+ os.MkdirAll(targetParentDir, 0777)
+
+ contentBytes, err := ioutil.ReadFile(path)
+	if err != nil {+		jww.ERROR.Println("Read file error:", path)+ return err
+ }
+
+ psr, err := parser.ReadFrom(bytes.NewReader(contentBytes))
+	if err != nil {+		jww.ERROR.Println("Parse file error:", path)+ return err
+ }
+
+ metadata, err := psr.Metadata()
+	if err != nil {+		jww.ERROR.Println("Processing file error:", path)+ return err
+ }
+
+ newmetadata, err := convertJekyllMetaData(metadata, postName, postDate, draft)
+	if err != nil {+		jww.ERROR.Println("Convert metadata error:", path)+ return err
+ }
+
+ jww.TRACE.Println(newmetadata)
+ content := convertJekyllContent(newmetadata, string(psr.Content()))
+
+ page, err := hugolib.NewPage(filename)
+	if err != nil {+		jww.ERROR.Println("New page error", filename)+ return err
+ }
+
+ page.SetDir(targetParentDir)
+ page.SetSourceContent([]byte(content))
+	page.SetSourceMetaData(newmetadata, parser.FormatToLeadRune("yaml"))+ page.SaveSourceAs(targetFile)
+
+	jww.TRACE.Println("Target file:", targetFile)+
+ return nil
+}
+
+func convertJekyllMetaData(m interface{}, postName string, postDate time.Time, draft bool) (interface{}, error) {+	url := postDate.Format("/2006/01/02/") + postName + "/"+
+ metadata, err := cast.ToStringMapE(m)
+	if err != nil {+ return nil, err
+ }
+
+	if draft {+ metadata["draft"] = true
+ }
+
+	for key, value := range metadata {+ lowerKey := strings.ToLower(key)
+
+		switch lowerKey {+ case "layout":
+ delete(metadata, key)
+ case "permalink":
+			if str, ok := value.(string); ok {+ url = str
+ }
+ delete(metadata, key)
+ case "category":
+			if str, ok := value.(string); ok {+				metadata["categories"] = []string{str}+ }
+ delete(metadata, key)
+ case "excerpt_separator":
+			if key != lowerKey {+ delete(metadata, key)
+ metadata[lowerKey] = value
+ }
+ case "date":
+			if str, ok := value.(string); ok {+ re := regexp.MustCompile(`(\d+):(\d+):(\d+)`)
+ r := re.FindAllStringSubmatch(str, -1)
+				if len(r) > 0 {+ hour, _ := strconv.Atoi(r[0][1])
+ minute, _ := strconv.Atoi(r[0][2])
+ second, _ := strconv.Atoi(r[0][3])
+ postDate = time.Date(postDate.Year(), postDate.Month(), postDate.Day(), hour, minute, second, 0, time.UTC)
+ }
+ }
+ delete(metadata, key)
+ }
+
+ }
+
+ metadata["url"] = url
+ metadata["date"] = postDate.Format(time.RFC3339)
+
+ return metadata, nil
+}
+
+func convertJekyllContent(m interface{}, content string) string {+ metadata, _ := cast.ToStringMapE(m)
+
+ lines := strings.Split(content, "\n")
+ var resultLines []string
+	for _, line := range lines {+ resultLines = append(resultLines, strings.Trim(line, "\r\n"))
+ }
+
+ content = strings.Join(resultLines, "\n")
+
+ excerptSep := "<!--more-->"
+	if value, ok := metadata["excerpt_separator"]; ok {+		if str, strOk := value.(string); strOk {+ content = strings.Replace(content, strings.TrimSpace(str), excerptSep, -1)
+ }
+ }
+
+	replaceList := []struct {+ re *regexp.Regexp
+ replace string
+	}{+		{regexp.MustCompile("<!-- more -->"), "<!--more-->"},+		{regexp.MustCompile(`\{%\s*raw\s*%\}\s*(.*?)\s*\{%\s*endraw\s*%\}`), "$1"},+		{regexp.MustCompile(`{%\s*highlight\s*(.*?)\s*%}`), "{{< highlight $1 >}}"},+		{regexp.MustCompile(`{%\s*endhighlight\s*%}`), "{{< / highlight >}}"},+ }
+
+	for _, replace := range replaceList {+ content = replace.re.ReplaceAllString(content, replace.replace)
+ }
+
+ return content
+}
--- /dev/null
+++ b/commands/import_jekyll_test.go
@@ -1,0 +1,104 @@
+// Copyright © 2015 Steve Francia <spf@spf13.com>.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package commands
+
+import (
+ "encoding/json"
+ "github.com/stretchr/testify/assert"
+ "testing"
+ "time"
+)
+
+func TestParseJekyllFilename(t *testing.T) {+	filenameArray := []string{+ "2015-01-02-test.md",
+ "2012-03-15-中文.markup",
+ }
+
+	expectResult := []struct {+ postDate time.Time
+ postName string
+	}{+		{time.Date(2015, time.January, 2, 0, 0, 0, 0, time.UTC), "test"},+		{time.Date(2012, time.March, 15, 0, 0, 0, 0, time.UTC), "中文"},+ }
+
+	for i, filename := range filenameArray {+ postDate, postName, err := parseJekyllFilename(filename)
+ assert.Equal(t, err, nil)
+		assert.Equal(t, expectResult[i].postDate.Format("2006-01-02"), postDate.Format("2006-01-02"))+ assert.Equal(t, expectResult[i].postName, postName)
+ }
+}
+
+func TestConvertJekyllMetadata(t *testing.T) {+	testDataList := []struct {+		metadata interface{}+ postName string
+ postDate time.Time
+ draft bool
+ expect string
+	}{+		{map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,+			`{"date":"2015-10-01T00:00:00Z","url":"/2015/10/01/testPost/"}`},+		{map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), true,+			`{"date":"2015-10-01T00:00:00Z","draft":true,"url":"/2015/10/01/testPost/"}`},+		{map[interface{}]interface{}{"Permalink": "/permalink.html", "layout": "post"},+ "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
+			`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},+		{map[interface{}]interface{}{"permalink": "/permalink.html"},+ "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
+			`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},+		{map[interface{}]interface{}{"category": nil, "permalink": 123},+ "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
+			`{"date":"2015-10-01T00:00:00Z","url":"/2015/10/01/testPost/"}`},+		{map[interface{}]interface{}{"Excerpt_Separator": "sep"},+ "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
+			`{"date":"2015-10-01T00:00:00Z","excerpt_separator":"sep","url":"/2015/10/01/testPost/"}`},+		{map[interface{}]interface{}{"category": "book", "layout": "post", "Others": "Goods", "Date": "2015-10-01 12:13:11"},+ "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
+			`{"Others":"Goods","categories":["book"],"date":"2015-10-01T12:13:11Z","url":"/2015/10/01/testPost/"}`},+ }
+
+	for _, data := range testDataList {+ result, err := convertJekyllMetaData(data.metadata, data.postName, data.postDate, data.draft)
+ assert.Equal(t, nil, err)
+ jsonResult, err := json.Marshal(result)
+ assert.Equal(t, nil, err)
+ assert.Equal(t, data.expect, string(jsonResult))
+ }
+}
+
+func TestConvertJekyllContent(t *testing.T) {+	testDataList := []struct {+		metadata interface{}+ content string
+ expect string
+	}{+		{map[interface{}]interface{}{},+ `Test content\n<!-- more -->\npart2 content`, `Test content\n<!--more-->\npart2 content`},
+		{map[interface{}]interface{}{"excerpt_separator": "<!--sep-->"},+ `Test content\n<!--sep-->\npart2 content`, `Test content\n<!--more-->\npart2 content`},
+		{map[interface{}]interface{}{}, "{% raw %}text{% endraw %}", "text"},+		{map[interface{}]interface{}{}, "{%raw%} text2 {%endraw %}", "text2"},+		{map[interface{}]interface{}{},+			"{% highlight go %}\nvar s int\n{% endhighlight %}",+			"{{< highlight go >}}\nvar s int\n{{< / highlight >}}"},+ }
+
+	for _, data := range testDataList {+ result := convertJekyllContent(data.metadata, data.content)
+ assert.Equal(t, data.expect, result)
+ }
+}
--- a/commands/import_test.go
+++ /dev/null
@@ -1,104 +1,0 @@
-// Copyright © 2015 Steve Francia <spf@spf13.com>.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package commands
-
-import (
- "encoding/json"
- "github.com/stretchr/testify/assert"
- "testing"
- "time"
-)
-
-func TestParseJekyllFilename(t *testing.T) {-	filenameArray := []string{- "2015-01-02-test.md",
- "2012-03-15-中文.markup",
- }
-
-	expectResult := []struct {- postDate time.Time
- postName string
-	}{-		{time.Date(2015, time.January, 2, 0, 0, 0, 0, time.UTC), "test"},-		{time.Date(2012, time.March, 15, 0, 0, 0, 0, time.UTC), "中文"},- }
-
-	for i, filename := range filenameArray {- postDate, postName, err := parseJekyllFilename(filename)
- assert.Equal(t, err, nil)
-		assert.Equal(t, expectResult[i].postDate.Format("2006-01-02"), postDate.Format("2006-01-02"))- assert.Equal(t, expectResult[i].postName, postName)
- }
-}
-
-func TestConvertJekyllMetadata(t *testing.T) {-	testDataList := []struct {-		metadata interface{}- postName string
- postDate time.Time
- draft bool
- expect string
-	}{-		{map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,-			`{"date":"2015-10-01T00:00:00Z","url":"/2015/10/01/testPost/"}`},-		{map[interface{}]interface{}{}, "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), true,-			`{"date":"2015-10-01T00:00:00Z","draft":true,"url":"/2015/10/01/testPost/"}`},-		{map[interface{}]interface{}{"Permalink": "/permalink.html", "layout": "post"},- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
-			`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},-		{map[interface{}]interface{}{"permalink": "/permalink.html"},- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
-			`{"date":"2015-10-01T00:00:00Z","url":"/permalink.html"}`},-		{map[interface{}]interface{}{"category": nil, "permalink": 123},- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
-			`{"date":"2015-10-01T00:00:00Z","url":"/2015/10/01/testPost/"}`},-		{map[interface{}]interface{}{"Excerpt_Separator": "sep"},- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
-			`{"date":"2015-10-01T00:00:00Z","excerpt_separator":"sep","url":"/2015/10/01/testPost/"}`},-		{map[interface{}]interface{}{"category": "book", "layout": "post", "Others": "Goods", "Date": "2015-10-01 12:13:11"},- "testPost", time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC), false,
-			`{"Others":"Goods","categories":["book"],"date":"2015-10-01T12:13:11Z","url":"/2015/10/01/testPost/"}`},- }
-
-	for _, data := range testDataList {- result, err := convertJekyllMetaData(data.metadata, data.postName, data.postDate, data.draft)
- assert.Equal(t, nil, err)
- jsonResult, err := json.Marshal(result)
- assert.Equal(t, nil, err)
- assert.Equal(t, data.expect, string(jsonResult))
- }
-}
-
-func TestConvertJekyllContent(t *testing.T) {-	testDataList := []struct {-		metadata interface{}- content string
- expect string
-	}{-		{map[interface{}]interface{}{},- `Test content\n<!-- more -->\npart2 content`, `Test content\n<!--more-->\npart2 content`},
-		{map[interface{}]interface{}{"excerpt_separator": "<!--sep-->"},- `Test content\n<!--sep-->\npart2 content`, `Test content\n<!--more-->\npart2 content`},
-		{map[interface{}]interface{}{}, "{% raw %}text{% endraw %}", "text"},-		{map[interface{}]interface{}{}, "{%raw%} text2 {%endraw %}", "text2"},-		{map[interface{}]interface{}{},-			"{% highlight go %}\nvar s int\n{% endhighlight %}",-			"{{< highlight go >}}\nvar s int\n{{< / highlight >}}"},- }
-
-	for _, data := range testDataList {- result := convertJekyllContent(data.metadata, data.content)
- assert.Equal(t, data.expect, result)
- }
-}
--
⑨