Esempio n. 1
0
func FindArchetype(kind string) (outpath string) {
	search := []string{helpers.AbsPathify(viper.GetString("archetypeDir"))}

	if viper.GetString("theme") != "" {
		themeDir := filepath.Join(helpers.AbsPathify("themes/"+viper.GetString("theme")), "/archetypes/")
		if _, err := os.Stat(themeDir); os.IsNotExist(err) {
			jww.ERROR.Println("Unable to find archetypes directory for theme :", viper.GetString("theme"), "in", themeDir)
		} else {
			search = append(search, themeDir)
		}
	}

	for _, x := range search {
		// If the new content isn't in a subdirectory, kind == "".
		// Therefore it should be excluded otherwise `is a directory`
		// error will occur. github.com/spf13/hugo/issues/411
		var pathsToCheck []string

		if kind == "" {
			pathsToCheck = []string{"default.md", "default"}
		} else {
			pathsToCheck = []string{kind + ".md", kind, "default.md", "default"}
		}
		for _, p := range pathsToCheck {
			curpath := filepath.Join(x, p)
			jww.DEBUG.Println("checking", curpath, "for archetypes")
			if exists, _ := helpers.Exists(curpath, hugofs.SourceFs); exists {
				jww.INFO.Println("curpath: " + curpath)
				return curpath
			}
		}
	}

	return ""
}
Esempio n. 2
0
func serve(port int) {
	jww.FEEDBACK.Println("Serving pages from " + helpers.AbsPathify(viper.GetString("PublishDir")))

	httpFs := &afero.HttpFs{SourceFs: hugofs.DestinationFS}
	fileserver := http.FileServer(httpFs.Dir(helpers.AbsPathify(viper.GetString("PublishDir"))))

	// We're only interested in the path
	u, err := url.Parse(viper.GetString("BaseURL"))
	if err != nil {
		jww.ERROR.Fatalf("Invalid BaseURL: %s", err)
	}
	if u.Path == "" || u.Path == "/" {
		http.Handle("/", fileserver)
	} else {
		http.Handle(u.Path, http.StripPrefix(u.Path, fileserver))
	}

	u.Host = net.JoinHostPort(serverInterface, strconv.Itoa(serverPort))
	u.Scheme = "http"
	jww.FEEDBACK.Printf("Web Server is available at %s\n", u.String())
	fmt.Println("Press Ctrl+C to stop")

	endpoint := net.JoinHostPort(serverInterface, strconv.Itoa(port))
	err = http.ListenAndServe(endpoint, nil)
	if err != nil {
		jww.ERROR.Printf("Error: %s\n", err.Error())
		os.Exit(1)
	}
}
Esempio n. 3
0
func copyStatic() error {
	staticDir := helpers.AbsPathify(viper.GetString("StaticDir")) + "/"
	if _, err := os.Stat(staticDir); os.IsNotExist(err) {
		jww.ERROR.Println("Unable to find Static Directory:", staticDir)
		return nil
	}

	publishDir := helpers.AbsPathify(viper.GetString("PublishDir")) + "/"

	syncer := fsync.NewSyncer()
	syncer.NoTimes = viper.GetBool("notimes")
	syncer.SrcFs = hugofs.SourceFs
	syncer.DestFs = hugofs.DestinationFS

	themeDir, err := helpers.GetThemeStaticDirPath()
	if err != nil {
		jww.ERROR.Println(err)
		return nil
	}

	if themeDir != "" {
		// Copy Static to Destination
		jww.INFO.Println("syncing from", themeDir, "to", publishDir)
		utils.CheckErr(syncer.Sync(publishDir, themeDir), fmt.Sprintf("Error copying static files of theme to %s", publishDir))
	}

	// Copy Static to Destination
	jww.INFO.Println("syncing from", staticDir, "to", publishDir)
	return syncer.Sync(publishDir, staticDir)
}
Esempio n. 4
0
func build(watches ...bool) {
	utils.CheckErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", helpers.AbsPathify(viper.GetString("PublishDir"))))
	watch := false
	if len(watches) > 0 && watches[0] {
		watch = true
	}
	utils.StopOnErr(buildSite(BuildWatch || watch))

	if BuildWatch {
		jww.FEEDBACK.Println("Watching for changes in", helpers.AbsPathify(viper.GetString("ContentDir")))
		jww.FEEDBACK.Println("Press Ctrl+C to stop")
		utils.CheckErr(NewWatcher(0))
	}
}
Esempio n. 5
0
func server(cmd *cobra.Command, args []string) {
	InitializeConfig()

	if cmd.Flags().Lookup("disableLiveReload").Changed {
		viper.Set("DisableLiveReload", disableLiveReload)
	}

	if serverWatch {
		viper.Set("Watch", true)
	}

	if viper.GetBool("watch") {
		serverWatch = true
	}

	l, err := net.Listen("tcp", net.JoinHostPort(serverInterface, strconv.Itoa(serverPort)))
	if err == nil {
		l.Close()
	} else {
		jww.ERROR.Println("port", serverPort, "already in use, attempting to use an available port")
		sp, err := helpers.FindAvailablePort()
		if err != nil {
			jww.ERROR.Println("Unable to find alternative port to use")
			jww.ERROR.Fatalln(err)
		}
		serverPort = sp.Port
	}

	viper.Set("port", serverPort)

	BaseURL, err := fixURL(BaseURL)
	if err != nil {
		jww.ERROR.Fatal(err)
	}
	viper.Set("BaseURL", BaseURL)

	if err := memStats(); err != nil {
		jww.ERROR.Println("memstats error:", err)
	}

	build(serverWatch)

	// Watch runs its own server as part of the routine
	if serverWatch {
		watched := getDirList()
		workingDir := helpers.AbsPathify(viper.GetString("WorkingDir"))
		for i, dir := range watched {
			watched[i], _ = helpers.GetRelativePath(dir, workingDir)
		}
		unique := strings.Join(helpers.RemoveSubpaths(watched), ",")

		jww.FEEDBACK.Printf("Watching for changes in %s/{%s}\n", workingDir, unique)
		err := NewWatcher(serverPort)
		if err != nil {
			fmt.Println(err)
		}
	}

	serve(serverPort)
}
Esempio n. 6
0
// NewTheme creates a new Hugo theme.
func NewTheme(cmd *cobra.Command, args []string) {
	InitializeConfig()

	if len(args) < 1 {
		cmd.Usage()
		jww.FATAL.Fatalln("theme name needs to be provided")
	}

	createpath := helpers.AbsPathify(filepath.Join("themes", args[0]))
	jww.INFO.Println("creating theme at", createpath)

	if x, _ := helpers.Exists(createpath, hugofs.SourceFs); x {
		jww.FATAL.Fatalln(createpath, "already exists")
	}

	mkdir(createpath, "layouts", "_default")
	mkdir(createpath, "layouts", "partials")

	touchFile(createpath, "layouts", "index.html")
	touchFile(createpath, "layouts", "_default", "list.html")
	touchFile(createpath, "layouts", "_default", "single.html")

	touchFile(createpath, "layouts", "partials", "header.html")
	touchFile(createpath, "layouts", "partials", "footer.html")

	mkdir(createpath, "archetypes")
	touchFile(createpath, "archetypes", "default.md")

	mkdir(createpath, "static", "js")
	mkdir(createpath, "static", "css")

	by := []byte(`The MIT License (MIT)

Copyright (c) ` + time.Now().Format("2006") + ` YOUR_NAME_HERE

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
`)

	err := helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), hugofs.SourceFs)
	if err != nil {
		jww.FATAL.Fatalln(err)
	}

	createThemeMD(createpath)
}
Esempio n. 7
0
// getDirList provides NewWatcher() with a list of directories to watch for changes.
func getDirList() []string {
	var a []string
	dataDir := helpers.AbsPathify(viper.GetString("DataDir"))
	layoutDir := helpers.AbsPathify(viper.GetString("LayoutDir"))
	walker := func(path string, fi os.FileInfo, err error) error {
		if err != nil {
			if path == dataDir && os.IsNotExist(err) {
				jww.WARN.Println("Skip DataDir:", err)
				return nil

			}
			if path == layoutDir && os.IsNotExist(err) {
				jww.WARN.Println("Skip LayoutDir:", err)
				return nil

			}
			jww.ERROR.Println("Walker: ", err)
			return nil
		}

		if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
			link, err := filepath.EvalSymlinks(path)
			if err != nil {
				jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
				return nil
			}
			linkfi, err := os.Stat(link)
			if err != nil {
				jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
				return nil
			}
			if !linkfi.Mode().IsRegular() {
				jww.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", path)
			}
			return nil
		}

		if fi.IsDir() {
			if fi.Name() == ".git" ||
				fi.Name() == "node_modules" || fi.Name() == "bower_components" {
				return filepath.SkipDir
			}
			a = append(a, path)
		}
		return nil
	}

	filepath.Walk(dataDir, walker)
	filepath.Walk(helpers.AbsPathify(viper.GetString("ContentDir")), walker)
	filepath.Walk(helpers.AbsPathify(viper.GetString("LayoutDir")), walker)
	filepath.Walk(helpers.AbsPathify(viper.GetString("StaticDir")), walker)
	if helpers.ThemeSet() {
		filepath.Walk(helpers.AbsPathify("themes/"+viper.GetString("theme")), walker)
	}

	return a
}
Esempio n. 8
0
func (s *Site) initialize() (err error) {
	if err = s.checkDirectories(); err != nil {
		return err
	}

	staticDir := helpers.AbsPathify(viper.GetString("StaticDir") + "/")

	s.Source = &source.Filesystem{
		AvoidPaths: []string{staticDir},
		Base:       s.absContentDir(),
	}

	s.Menus = Menus{}

	s.initializeSiteInfo()

	return
}
Esempio n. 9
0
func (s *Site) absPublishDir() string {
	return helpers.AbsPathify(viper.GetString("PublishDir"))
}
Esempio n. 10
0
func (s *Site) absContentDir() string {
	return helpers.AbsPathify(viper.GetString("ContentDir"))
}
Esempio n. 11
0
func (s *Site) absLayoutDir() string {
	return helpers.AbsPathify(viper.GetString("LayoutDir"))
}
Esempio n. 12
0
func (s *Site) absThemeDir() string {
	return helpers.AbsPathify("themes/" + viper.GetString("theme"))
}
Esempio n. 13
0
func (s *Site) absDataDir() string {
	return helpers.AbsPathify(viper.GetString("DataDir"))
}
Esempio n. 14
0
func convertContents(mark rune) (err error) {
	InitializeConfig()
	site := &hugolib.Site{}

	if err := site.Initialise(); err != nil {
		return err
	}

	if site.Source == nil {
		panic(fmt.Sprintf("site.Source not set"))
	}
	if len(site.Source.Files()) < 1 {
		return fmt.Errorf("No source files found")
	}

	jww.FEEDBACK.Println("processing", len(site.Source.Files()), "content files")
	for _, file := range site.Source.Files() {
		jww.INFO.Println("Attempting to convert", file.LogicalName())
		page, err := hugolib.NewPage(file.LogicalName())
		if err != nil {
			return err
		}

		psr, err := parser.ReadFrom(file.Contents)
		if err != nil {
			jww.ERROR.Println("Error processing file:", file.Path())
			return err
		}
		metadata, err := psr.Metadata()
		if err != nil {
			jww.ERROR.Println("Error processing file:", file.Path())
			return err
		}

		// better handling of dates in formats that don't have support for them
		if mark == parser.FormatToLeadRune("json") || mark == parser.FormatToLeadRune("yaml") || mark == parser.FormatToLeadRune("toml") {
			newmetadata := cast.ToStringMap(metadata)
			for k, v := range newmetadata {
				switch vv := v.(type) {
				case time.Time:
					newmetadata[k] = vv.Format(time.RFC3339)
				}
			}
			metadata = newmetadata
		}

		page.SetDir(filepath.Join(helpers.AbsPathify(viper.GetString("ContentDir")), file.Dir()))
		page.SetSourceContent(psr.Content())
		page.SetSourceMetaData(metadata, mark)

		if outputDir != "" {
			page.SaveSourceAs(filepath.Join(outputDir, page.FullFilePath()))
		} else {
			if unsafe {
				page.SaveSource()
			} else {
				jww.FEEDBACK.Println("Unsafe operation not allowed, use --unsafe or set a different output path")
			}
		}
	}
	return
}
Esempio n. 15
0
func NewContent(kind, name string) (err error) {
	jww.INFO.Println("attempting to create ", name, "of", kind)

	location := FindArchetype(kind)

	var by []byte

	if location != "" {
		by, err = ioutil.ReadFile(location)
		if err != nil {
			jww.ERROR.Println(err)
		}
	}
	if location == "" || err != nil {
		by = []byte("+++\n title = \"title\"\n draft = true \n+++\n")
	}

	psr, err := parser.ReadFrom(bytes.NewReader(by))
	if err != nil {
		return err
	}
	metadata, err := psr.Metadata()
	if err != nil {
		return err
	}
	newmetadata, err := cast.ToStringMapE(metadata)
	if err != nil {
		jww.ERROR.Println("Error processing archetype file:", location)
		return err
	}

	for k := range newmetadata {
		switch strings.ToLower(k) {
		case "date":
			newmetadata[k] = time.Now()
		case "title":
			newmetadata[k] = helpers.MakeTitle(helpers.Filename(name))
		}
	}

	caseimatch := func(m map[string]interface{}, key string) bool {
		for k := range m {
			if strings.ToLower(k) == strings.ToLower(key) {
				return true
			}
		}
		return false
	}

	if newmetadata == nil {
		newmetadata = make(map[string]interface{})
	}

	if !caseimatch(newmetadata, "date") {
		newmetadata["date"] = time.Now()
	}

	if !caseimatch(newmetadata, "title") {
		newmetadata["title"] = helpers.MakeTitle(helpers.Filename(name))
	}

	page, err := hugolib.NewPage(name)
	if err != nil {
		return err
	}

	if x := parser.FormatSanitize(viper.GetString("MetaDataFormat")); x == "json" || x == "yaml" || x == "toml" {
		newmetadata["date"] = time.Now().Format(time.RFC3339)
	}

	//page.Dir = viper.GetString("sourceDir")
	page.SetSourceMetaData(newmetadata, parser.FormatToLeadRune(viper.GetString("MetaDataFormat")))
	page.SetSourceContent(psr.Content())
	if err = page.SafeSaveSourceAs(filepath.Join(viper.GetString("contentDir"), name)); err != nil {
		return
	}
	jww.FEEDBACK.Println(helpers.AbsPathify(filepath.Join(viper.GetString("contentDir"), name)), "created")

	editor := viper.GetString("NewContentEditor")

	if editor != "" {
		jww.FEEDBACK.Printf("Editing %s in %s.\n", name, editor)

		cmd := exec.Command(editor, path.Join(viper.GetString("contentDir"), name))
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err = cmd.Run(); err != nil {
			return
		}
	}

	return nil
}
Esempio n. 16
0
// NewWatcher creates a new watcher to watch filesystem events.
func NewWatcher(port int) error {
	if runtime.GOOS == "darwin" {
		tweakLimit()
	}

	watcher, err := watcher.New(1 * time.Second)
	var wg sync.WaitGroup

	if err != nil {
		fmt.Println(err)
		return err
	}

	defer watcher.Close()

	wg.Add(1)

	for _, d := range getDirList() {
		if d != "" {
			_ = watcher.Add(d)
		}
	}

	go func() {
		for {
			select {
			case evs := <-watcher.Events:
				jww.INFO.Println("File System Event:", evs)

				staticChanged := false
				dynamicChanged := false
				staticFilesChanged := make(map[string]bool)

				for _, ev := range evs {
					ext := filepath.Ext(ev.Name)
					istemp := strings.HasSuffix(ext, "~") || (ext == ".swp") || (ext == ".swx") || (ext == ".tmp") || strings.HasPrefix(ext, ".goutputstream")
					if istemp {
						continue
					}
					// renames are always followed with Create/Modify
					if ev.Op&fsnotify.Rename == fsnotify.Rename {
						continue
					}

					isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(helpers.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, helpers.GetThemesDirPath()))
					staticChanged = staticChanged || isstatic
					dynamicChanged = dynamicChanged || !isstatic

					if isstatic {
						if staticPath, err := helpers.MakeStaticPathRelative(ev.Name); err == nil {
							staticFilesChanged[staticPath] = true
						}
					}

					// add new directory to watch list
					if s, err := os.Stat(ev.Name); err == nil && s.Mode().IsDir() {
						if ev.Op&fsnotify.Create == fsnotify.Create {
							watcher.Add(ev.Name)
						}
					}
				}

				if staticChanged {
					jww.FEEDBACK.Printf("Static file changed, syncing\n\n")
					utils.StopOnErr(copyStatic(), fmt.Sprintf("Error copying static files to %s", helpers.AbsPathify(viper.GetString("PublishDir"))))

					if !BuildWatch && !viper.GetBool("DisableLiveReload") {
						// Will block forever trying to write to a channel that nobody is reading if livereload isn't initalized

						// force refresh when more than one file
						if len(staticFilesChanged) == 1 {
							for path := range staticFilesChanged {
								livereload.RefreshPath(path)
							}

						} else {
							livereload.ForceRefresh()
						}
					}
				}

				if dynamicChanged {
					fmt.Print("\nChange detected, rebuilding site\n")
					const layout = "2006-01-02 15:04 -0700"
					fmt.Println(time.Now().Format(layout))
					utils.CheckErr(buildSite(true))

					if !BuildWatch && !viper.GetBool("DisableLiveReload") {
						// Will block forever trying to write to a channel that nobody is reading if livereload isn't initalized
						livereload.ForceRefresh()
					}
				}
			case err := <-watcher.Errors:
				if err != nil {
					fmt.Println("error:", err)
				}
			}
		}
	}()

	if port > 0 {
		if !viper.GetBool("DisableLiveReload") {
			livereload.Initialize()
			http.HandleFunc("/livereload.js", livereload.ServeJS)
			http.HandleFunc("/livereload", livereload.Handler)
		}

		go serve(port)
	}

	wg.Wait()
	return nil
}