Changeset 80 in code
- Timestamp:
- Dec 12, 2023, 2:09:17 PM (18 months ago)
- Location:
- trunk
- Files:
-
- 7 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Makefile
r75 r80 1 1 DESTDIR ?= 2 GOFLAGS ?= -v -buildvcs=false -mod=vendor -buildmode=exe 2 GOFLAGS ?= -v -buildvcs=false -mod=vendor -buildmode=exe -ldflags "-w -X `go list`.Date=${DATE} -X `go list`.Vendor=${GOOS} -X `go list`.Version=${VERSION}" 3 3 PREFIX ?= /usr/local 4 DATE ?= `date -u +%F` 5 GOOS ?= `go env GOOS` 4 6 VERSION ?= `git describe --tags` 5 7 -
trunk/README.md
r75 r80 18 18 Build it manually assuming you have Go (>=1.17) installed: 19 19 20 $ go install marisa.chaotic.ninja/aya/cmd/aya@latest 20 $ go install marisa.chaotic.ninja/aya/cmd/aya@latest (1) 21 21 --- or --- 22 22 $ git clone https://git.chaotic.ninja/yakumo.izuru/aya … … 24 24 $ make 25 25 # make install 26 26 27 (1) If you use this method, the `aya version` subcommand may print the wrong string, 28 but it should not be a problem unless you use it on a page. 29 27 30 ## Ideology 28 31 29 Keep your texts in markdown, or HTMLformat right in the main directory32 Keep your texts in markdown, [amber](https://github.com/eknkc/amber), or html format right in the main directory 30 33 of your blog/site. 31 34 … … 33 36 in the `.aya` subdirectory. 34 37 35 Define variables in the header of the content files using [YAML] :38 Define variables in the header of the content files using [YAML](https://www.yaml.io) : 36 39 37 title: My web site 38 keywords: best website, hello, world 39 --- 40 ```markdown 41 title: My web site 42 keywords: best website, hello, world 43 --- 40 44 41 Markdown text goes after a header *separator* 45 Markdown text goes after a header *separator* 46 ``` 42 47 43 48 Use placeholders for variables and plugins in your markdown or html … … 47 52 subdiretory. 48 53 49 Everything the extensions prints to stdoutbecomes the value of the54 Everything the extensions prints to [stdout](https://man.freebsd.org/cgi/man.cgi?fd) becomes the value of the 50 55 placeholder. 51 56 52 Every variable from the content header will be passed via environment variables like `title` becomes `$AYA_TITLE` and so on. There are some special variables: 57 Every variable from the content header will be passed via environment variables like `title` becomes `$AYA\_TITLE` and so on. 58 There are some special variables: 53 59 54 60 * `$AYA` - a path to the `aya` executable … … 94 100 rm -f $AYA_OUTDIR/styles.css 95 101 96 ## Extras 97 98 `aya` also supports generating `.html` and `.css` by means of using `.amber` 99 and `.gcss` files. See more at [eknkc/amber](https://github.com/eknkc/amber) [yosssi/gcss](https://github.com/yosssi/gcss) 102 Note, you can also place `.gcss` files for [gcss](https://github.com/yosssi/gcss) to process instead 100 103 101 104 ## Command line usage -
trunk/cmd/aya/main.go
r79 r80 1 // $TheSupernovaDuo: marisa.chaotic.ninja/aya/cmd/aya, v 0.7.0 2023-12-11 17:22:51+0000, yakumo_izuru Exp $1 // $TheSupernovaDuo: marisa.chaotic.ninja/aya/cmd/aya, v1.0.0 2023-12-12 13:44:23+0000, yakumo_izuru Exp $ 2 2 package main 3 3 4 4 import ( 5 "bytes"6 5 "fmt" 7 "io"8 6 "os" 9 "os/exec"10 "path/filepath"11 7 "strings" 12 "time"13 8 14 "gopkg.in/yaml.v3"15 9 "marisa.chaotic.ninja/aya" 16 10 ) … … 23 17 24 18 type Vars map[string]string 25 26 // renameExt renames extension (if any) from oldext to newext27 // If oldext is an empty string - extension is extracted automatically.28 // If path has no extension - new extension is appended29 func renameExt(path, oldext, newext string) string {30 if oldext == "" {31 oldext = filepath.Ext(path)32 }33 if oldext == "" || strings.HasSuffix(path, oldext) {34 return strings.TrimSuffix(path, oldext) + newext35 }36 return path37 }38 39 // globals returns list of global OS environment variables that start40 // with AYA_ prefix as Vars, so the values can be used inside templates41 func globals() Vars {42 vars := Vars{}43 for _, e := range os.Environ() {44 pair := strings.Split(e, "=")45 if strings.HasPrefix(pair[0], "AYA_") {46 vars[strings.ToLower(pair[0][3:])] = pair[1]47 }48 }49 return vars50 }51 52 // run executes a command or a script. Vars define the command environment,53 // each aya var is converted into OS environemnt variable with AYA_ prefix54 // prepended. Additional variable $AYA contains path to the aya binary. Command55 // stderr is printed to aya stderr, command output is returned as a string.56 func run(vars Vars, cmd string, args ...string) (string, error) {57 // First check if partial exists (.html)58 if b, err := os.ReadFile(filepath.Join(AYADIR, cmd+".html")); err == nil {59 return string(b), nil60 }61 62 var errbuf, outbuf bytes.Buffer63 c := exec.Command(cmd, args...)64 env := []string{"AYA=" + os.Args[0], "AYA_OUTDIR=" + PUBDIR}65 env = append(env, os.Environ()...)66 for k, v := range vars {67 env = append(env, "AYA_"+strings.ToUpper(k)+"="+v)68 }69 c.Env = env70 c.Stdout = &outbuf71 c.Stderr = &errbuf72 73 err := c.Run()74 75 if errbuf.Len() > 0 {76 fmt.Println("ERROR:", errbuf.String())77 }78 if err != nil {79 return "", err80 }81 return string(outbuf.Bytes()), nil82 }83 84 // getVars returns list of variables defined in a text file and actual file85 // content following the variables declaration. Header is separated from86 // content by an empty line. Header can be either YAML or JSON.87 // If no empty newline is found - file is treated as content-only.88 func getVars(path string, globals Vars) (Vars, string, error) {89 b, err := os.ReadFile(path)90 if err != nil {91 return nil, "", err92 }93 s := string(b)94 95 // Pick some default values for content-dependent variables96 v := Vars{}97 title := strings.Replace(strings.Replace(path, "_", " ", -1), "-", " ", -1)98 v["title"] = strings.ToTitle(title)99 v["description"] = ""100 v["file"] = path101 v["url"] = path[:len(path)-len(filepath.Ext(path))] + ".html"102 v["output"] = filepath.Join(PUBDIR, v["url"])103 104 // Override default values with globals105 for name, value := range globals {106 v[name] = value107 }108 109 // Add layout if none is specified110 if _, ok := v["layout"]; !ok {111 v["layout"] = "layout.html"112 }113 114 delim := "\n---\n"115 if sep := strings.Index(s, delim); sep == -1 {116 return v, s, nil117 } else {118 header := s[:sep]119 body := s[sep+len(delim):]120 121 vars := Vars{}122 if err := yaml.Unmarshal([]byte(header), &vars); err != nil {123 fmt.Println("ERROR: failed to parse header", err)124 return nil, "", err125 } else {126 // Override default values + globals with the ones defines in the file127 for key, value := range vars {128 v[key] = value129 }130 }131 if strings.HasPrefix(v["url"], "./") {132 v["url"] = v["url"][2:]133 }134 return v, body, nil135 }136 }137 138 // Render expanding aya plugins and variables139 func render(s string, vars Vars) (string, error) {140 delimOpen := "{{"141 delimClose := "}}"142 143 out := &bytes.Buffer{}144 for {145 if from := strings.Index(s, delimOpen); from == -1 {146 out.WriteString(s)147 return out.String(), nil148 } else {149 if to := strings.Index(s, delimClose); to == -1 {150 return "", fmt.Errorf("Closing delimiter not found")151 } else {152 out.WriteString(s[:from])153 cmd := s[from+len(delimOpen) : to]154 s = s[to+len(delimClose):]155 m := strings.Fields(cmd)156 if len(m) == 1 {157 if v, ok := vars[m[0]]; ok {158 out.WriteString(v)159 continue160 }161 }162 if res, err := run(vars, m[0], m[1:]...); err == nil {163 out.WriteString(res)164 } else {165 fmt.Println(err)166 }167 }168 }169 }170 171 }172 173 // This function passes the files to build to their corresponding functions174 // As far as I'm aware, Markdown has three possible filename extensions,175 // but .md is the most common one known.176 func build(path string, w io.Writer, vars Vars) error {177 ext := filepath.Ext(path)178 if ext == ".md" || ext == ".mkd" || ext == ".markdown" {179 return buildMarkdown(path, w, vars)180 } else if ext == ".htm" || ext == ".html" || ext == ".xht" || ext == ".xhtml" {181 return buildHTML(path, w, vars)182 } else if ext == ".amber" {183 return buildAmber(path, w, vars)184 } else if ext == ".gcss" {185 return buildGCSS(path, w)186 } else {187 return buildRaw(path, w)188 }189 }190 191 // Build everything and store it on PUBDIR192 // If boolean watch is true, it keeps on going193 // every time you modify something.194 func buildAll(watch bool) {195 lastModified := time.Unix(0, 0)196 modified := false197 198 vars := globals()199 for {200 os.Mkdir(PUBDIR, 0755)201 filepath.Walk(".", func(path string, info os.FileInfo, err error) error {202 // ignore hidden files and directories203 if filepath.Base(path)[0] == '.' || strings.HasPrefix(path, ".") {204 return nil205 }206 // inform user about fs walk errors, but continue iteration207 if err != nil {208 fmt.Println("error:", err)209 return nil210 }211 212 if info.IsDir() {213 os.Mkdir(filepath.Join(PUBDIR, path), 0755)214 return nil215 } else if info.ModTime().After(lastModified) {216 if !modified {217 // First file in this build cycle is about to be modified218 run(vars, "prehook")219 modified = true220 }221 fmt.Println("build:", path)222 return build(path, nil, vars)223 }224 return nil225 })226 if modified {227 // At least one file in this build cycle has been modified228 run(vars, "posthook")229 modified = false230 }231 if !watch {232 break233 }234 lastModified = time.Now()235 time.Sleep(1 * time.Second)236 }237 }238 19 239 20 // Initialize the environment … … 295 76 } 296 77 case "version": 297 fmt.Printf("%v\n", aya. FullVersion())78 fmt.Printf("%v\n", aya.PrintVersion()) 298 79 os.Exit(0) 299 80 case "watch": -
trunk/usage.go
r78 r80 7 7 // This function is called by the `aya help` subcommand 8 8 func PrintUsage() { 9 fmt.Printf("aya/%v\n", FullVersion())9 fmt.Printf("aya/%v\n", PrintFullVersion()) 10 10 fmt.Println("Homepage: https://aya.chaotic.ninja") 11 11 fmt.Println("Repository: https://git.chaotic.ninja/yakumo.izuru/aya") -
trunk/version.go
r79 r80 4 4 import ( 5 5 "fmt" 6 "time"7 6 ) 8 7 9 8 var ( 10 // Set to current tag 11 Version = "v0.7.0" 12 Time = time.Now() 9 // Variables set at build-time 10 Date string 11 Vendor string 12 Version string 13 13 ) 14 14 15 // FullVersion display the full version and build 16 func FullVersion() string { 17 d := Time.Day() 18 m := Time.Month() 19 y := Time.Year() 20 return fmt.Sprintf("%v || %d.%d.%d", Version, y, m, d) 15 // PrintVersion only displays the obvious 16 func PrintVersion() string { 17 return fmt.Sprintf("%s", Version) 21 18 } 19 20 // PrintFullVersion display the full version and build 21 func PrintFullVersion() string { 22 return fmt.Sprintf("%s, built at %s, on %s", Version, Date, Vendor) 23 }
Note:
See TracChangeset
for help on using the changeset viewer.