diff --git a/README.md b/README.md
index f4b0bf5..2656c42 100644
--- a/README.md
+++ b/README.md
@@ -1,147 +1,219 @@
-# Pagine
-Template-driven generator for building websites of any scale.
+# Pagine v2
-Latest version: v1.0.0
+Pagine is an high-performance website constructor that makes full use of multicore hardware.
-- Template.
-- Separation of **content**, **template** and **page form**.
+Build jobs can be completed very fast.
-### Planned features
+## Features
-- Job pipeline for reducing redundant content generation.
-- Directories as collections of web pages.
+- Parallel hierarchy processing and unit execution. Everything is executed in parallel from beginning to end.
+- Hierarchical metadata propagation which makes metadata management easy.
+- Manage templates and assets via Git. Every template can be distributed and used without modification.
+- In-template builtin functions
+- Interact with Pagine in templates.
+- Update on file change while running as HTTP server.
+
+Supported rich text formats:
+
+- Markdown with MathJax/LaTeX support
+- Asciidoc
## Install
+### Binaries
+
+Find the executable that matches your OS and architecture in [releases](https://github.com/webpagine/pagine/releases).
+
+### Build from source
+
```shell
$ go install github.com/webpagine/pagine/cmd/pagine
-$ pagine --gen
```
-Serve as HTTP server and automatically generate when files change:
+## Usage
+
+Usage of pagine:
+- `-public` string
+ - Location of public directory. (default `/tmp/$(basename $PWD).public`)
+- `-root` string
+ - Site root. (default `$PWD`)
+- `-serve` string
+ - Specify the port to listen and serve as HTTP.
+
+
+### Generate
+
+```shell
+$ cd ~/web/my_site
+$ pagine
+Generation complete.
+```
+
+### Run as HTTP server
```shell
-$ pagine --serve --listen :8080 --public /tmp/public
+$ cd ~/web/my_site
+$ pagine --serve :12450
```
> [!NOTE]
> Incremental generation is not implemented yet.
-> Set the `--public` under `/tmp/` is recommended to reduce hard disk writes.
+> Set the `--public` under `/tmp` is recommended to reduce hard disk writes.
-## Get Started
+## Structure
-Example structure:
-```
-.
-├── pagine.toml
-├── contents/
-│ └── my_first_post.md
-├── data/
-│ ├── header_all.toml
-│ └── header_specific.toml
-├── posts/
-│ └── my_first_post.html.pagine
-└── templates/
- ├── header.html
- ├── footer.html
- └── post.html
-```
-
-### Site
+### Template
-- Top level directory contents, such as website metadata.
-- Global elements, such as page frame, navigation bar.
+Template is a set of page frames (Go template file) and assets (e.g. SVGs, stylesheets and scripts).
-For example: `/pagine.toml`
+Manifest of one template looks like:
```toml
-ignore = [ "/\\.*", "/*toml", "/contents/*", "/templates/*" ]
-```
+[manifest]
+canonical = "com.symboltics.pagine.genesis" # Canonical name
+patterns = [ "/*html" ] # Matched files will be added as template file.
-### Template
+[[templates]]
+name = "page" # Export as name `page`
+export = "page.html" # Export `page.html`
-Current implementation of template depends on Go `text/template` library.
+[[templates]]
+name = "post" # Export as name `post`
+export = "post.html" # Export `post.html`
+```
-For Go templates, refer to the [tutorial](https://gohugo.io/templates/introduction/) by Hugo team.
+To the Go templates files syntax, see [text/template](https://pkg.go.dev/text/template).
-For example: `/templates/post.html`
+Example: `page.html`
```html
-
+
- {{ .data.title }}
+ {{ .title }}
+
-
{{ .contents.my_first_post }}
-
{{ .data.time }}
+{{ template "header.html" .header }}
+{{ render .content }}
+{{ template "footer.html" .footer }}
```
-### Page
+### Env
+
+"Environment" is the configuration of the details of the entire process.
+
+```toml
+ignore = [ "/.git*" ] # Pattern matching. Matched files will not be **copied** to the public/destination.
+
+[use]
+genesis = "/templates/genesis"
+another = "/templates/something_else" # Load and set alias for the template.
+```
+
+Installing templates via Git submodule is recommended. Such as:
+
+```shell
+$ git submodule add https://github.com/webpagine/genesis templates/genesis
+```
+
+### Level
-Page is a set of attributions of single page.
+Each "level" contains its metadata. And a set of units to be executed.
-- Templates to be used.
-- Data definitions to be used in template.
-- Different contents to be used in template.
+For directories, metadata sets are stored in `metadata.toml` in the form of map, and units are stored in `unit.toml`
-For example: `/posts/my_first_post.html.pagine`
+Each template has its alias that defined in `env` as the namespace.
+
+Levels can override fields propagated from parents.
+
+Example: `/metadata.toml`
```toml
-# Templates to be used in this page.
-[templates]
-header = "/templates/header.html"
-footer = "/templates/footer.html"
-
-# Main template (top-level) is required.
-main = "/templates/post.html"
-
-# Include data definitions from extern TOMLs.
-[include]
-header = [
- "/data/header_all.toml",
- "/data/header_specific.toml",
-]
-
-# Contents to be parsed and generated to HTML.
-[contents]
-my_first_post = "/contents/my_first_post.md"
-
-# Define data for template "main".
-[define.main]
-lang = "en"
-title = "My First Post"
-time = 2024-05-01
-
-# Define data for template "header".
-[define.header]
-logo = "/assets/img/logo.svg"
+[genesis]
+title = "Pagine"
+
+[genesis.head]
+icon = "/favicon.ico"
+
+[[genesis.header.nav.items]]
+name = "Documentation"
+link = "/docs/"
```
+### Unit
+
+Example: `/unit.toml`
+```toml
+[[unit]]
+template = "genesis:page" # Which template to use.
+output = "/index.html" # Where to save the result.
+define = { title = "Pagine" } # Unit-specified metadata.
+
+[[unit]]
+template = "genesis:page"
+output = "/404.html"
+define = { title = "Page not found" }
+```
+
+## Builtin functions
+
+### Arithmetic
+
+| Func | Args | Result |
+|-------|-----------|--------|
+| `add` | a, b: Int | Int |
+| `sub` | a, b: Int | Int |
+| `mul` | a, b: Int | Int |
+| `div` | a, b: Int | Int |
+| `mod` | a,b : Int | Int |
+
+### Engine API
+
+| Func | Args | Description |
+|-----------|-------------|--------------------------------------------------------------------------------------------------|
+| `getAttr` | key: String | Get meta information in the form of map about units, hierarchy and templates provided by engine. |
+
+| Attribution | Description |
+|----------------|-------------------------------------------------|
+| `unitBase` | Unit's level's base dir path. |
+| `templateBase` | It tells the template where it has been stored. |
+
+### Data processing
+
+| Func | Args | Result | Description |
+|------------------|-------------------------------------------------|--------------------------------------------------|-----------------------------------------------------------------------------------|
+| `divideSliceByN` | slice: []Any, n: Int | [][]Any | Divide a slice into *len(slice) / N* slices |
+| `mapAsSlice` | map: map[String]Any, **key**, **value**: String | []map[String]{ **key**: String, **value**: Any } | Convert map to a slice of map that contains two keys named **key** and **value**. |
+
### Content
-For each supported rich text format, there is a parser and an HTML generator. Pagine detects format by file name suffix `.md`.
+Path starts from where the unit is.
-The latest implementation accepts:
-- Markdown
+| Func | Args | Description |
+|------------------|--------------|-----------------------------------------|
+| `embed` | path: String | Embed file raw content. |
+| `render` | path: String | Invoke renderer by file extension name. |
+| `renderAsciidoc` | path: String | Render and embed Asciidoc content. |
+| `renderMarkdown` | path: String | Render and embed Markdown content. |
-For example: `/contents/my_first_post.md`
-```markdown
-# My First Post
+| Format | File Extension Name |
+|----------|---------------------|
+| Markdown | `md` |
+| Asciidoc | `adoc` |
-It is a post in Markdown.
-```
+## Deploy
-## Deploy manually
+### Manually
```shell
-$ pagine --gen --public ../public
+$ pagine --public ../public
```
-## Deploy via CI/CD
+Upload `public` to your server.
-### GitHub Actions
+### Deploy to GitHub Pages via GitHub Actions (recommended)
GitHub Actions workflow configuration can be found in [Get Started](https://github.com/webpagine/get-started) repository.
diff --git a/structure/builtin.go b/structure/builtin.go
index 468cd21..3db33c6 100644
--- a/structure/builtin.go
+++ b/structure/builtin.go
@@ -1,6 +1,10 @@
package structure
func add(aInt, bInt any) int { return aInt.(int) + bInt.(int) }
+func sub(aInt, bInt any) int { return aInt.(int) - bInt.(int) }
+func mul(aInt, bInt any) int { return aInt.(int) * bInt.(int) }
+func div(aInt, bInt any) int { return aInt.(int) / bInt.(int) }
+func mod(aInt, bInt any) int { return aInt.(int) % bInt.(int) }
func divideSliceByN(s []any, nInt any) [][]any {
n := nInt.(int)
diff --git a/structure/hierarchy.go b/structure/hierarchy.go
index d9e73f9..e99d3a5 100644
--- a/structure/hierarchy.go
+++ b/structure/hierarchy.go
@@ -89,7 +89,7 @@ func ExecuteLevels(env *Env, root, dest vfs.DirFS, inherit MetadataSet) (Level,
case err == nil:
// No error will cause interrupt below.
- for _, unitItem := range unitManifest.Unit {
+ for _, unitItem := range unitManifest.Units {
wg.Add(1)
go func() {
defer wg.Done()
diff --git a/structure/template.go b/structure/template.go
index 18ca595..f9b695a 100644
--- a/structure/template.go
+++ b/structure/template.go
@@ -71,11 +71,13 @@ func LoadTemplate(root vfs.DirFS) (*Template, error) {
}
var emptyFuncMap = map[string]any{
- "attr": _empty,
+ "getAttr": empty,
"embed": _empty,
"render": _empty,
"renderMarkdown": _empty,
"renderAsciidoc": _empty,
}
+func empty() any { return nil }
+
func _empty(_ any) any { return "" }
diff --git a/structure/unit.go b/structure/unit.go
index dcf7b1f..bce252f 100644
--- a/structure/unit.go
+++ b/structure/unit.go
@@ -16,7 +16,7 @@ type UnitReport struct {
}
type UnitManifest struct {
- Unit []struct {
+ Units []struct {
Template string `toml:"template"`
Output string `toml:"output"`
Define map[string]any `toml:"define"`
@@ -44,7 +44,7 @@ func (u *Unit) Generate(env *Env, root, dest vfs.DirFS, data MetadataSet, define
templateBase, _ = strings.CutPrefix(t.Root.Path, env.Root.Path)
attr = map[string]any{
- "base": base,
+ "unitBase": base,
"templateBase": templateBase,
}
)
@@ -59,13 +59,17 @@ func (u *Unit) Generate(env *Env, root, dest vfs.DirFS, data MetadataSet, define
}
funcMap := map[string]any{
- "add": add,
+ "add": add,
+ "sub": sub,
+ "mul": mul,
+ "div": div,
+ "mod": mod,
+
"divideSliceByN": divideSliceByN,
"mapAsSlice": mapAsSlice,
- "attr": func(keyStr any) any {
- return attr[keyStr.(string)]
- },
+ "getAttr": func() any { return attr },
+
"embed": func(pathStr any) any {
b, err := root.ReadFile(pathStr.(string))
if err != nil {