From: early Date: Sat, 2 Nov 2024 18:27:04 +0000 (-0600) Subject: Move engine to web X-Git-Url: https://git.earlybird.gay/?a=commitdiff_plain;h=c58c3de9b999cdbc68446cd4109d1419299e92de;p=today Move engine to web --- diff --git a/engine/.gitignore b/engine/.gitignore deleted file mode 100644 index 3845427..0000000 --- a/engine/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Ignore anything ending in local -*local - -# Disregard ignore rules for example files -!*.example \ No newline at end of file diff --git a/engine/LICENSE.txt b/engine/LICENSE.txt deleted file mode 100644 index a8876ed..0000000 --- a/engine/LICENSE.txt +++ /dev/null @@ -1,18 +0,0 @@ -Copyright © 2024 Early - -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. \ No newline at end of file diff --git a/engine/README.md b/engine/README.md deleted file mode 100644 index df9e1df..0000000 --- a/engine/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Today Engine - -The Today engine builds on Go's templating with reusable parts in the style of -the Web Components standard. It aims to be usable for anything from static HTML -websites to complex applications with a mix of server and client-side behavior. - -Author: Early N. -License: MIT, BSD - -## Installation - -go get git.earlybird.gay/today-engine@latest - -## Why? - -In short, to help guide new developers in making a website (today). The -technologies that dominate the modern web are complex in unique ways that do -not promote foundational knowledge of the web platform. Today focuses on: - -- Building websites from a basis of standard HTML/CSS -- Promoting use of the Web Components standard of JS -- Expanding on Go's robust template system with reusable chunks of HTML - -## How? - -## License - -The portions of `today-engine` owned by Early N. are under the MIT license. Some -code sections have a different license: - -- `package html` is under a BSD-3-Clause style license from Google. diff --git a/engine/cmd/standard-test/main.go b/engine/cmd/standard-test/main.go deleted file mode 100644 index 88fad7c..0000000 --- a/engine/cmd/standard-test/main.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "context" - "net/http" - "syscall" - "text/template" - - tapp "git.earlybird.gay/today-app/app" - "git.earlybird.gay/today-engine/page" - "git.earlybird.gay/today-engine/part" - "git.earlybird.gay/today-engine/render" - stdpart "git.earlybird.gay/today-engine/standard/part" -) - -var Thing = part.New("test-thing", "test_thing.html", - part.OnLoad(func(ctx context.Context, data render.Data) error { - // fmt.Println(data.Get("value")) - return nil - }), -) - -var Page = page.New("index", "pages/index.html", - page.Includes( - stdpart.ContactForm([]string{"Feedback"}), Thing, - ), - page.Funcs(template.FuncMap{ - "SliceOfLen": func(i int) []int { - out := make([]int, i) - for i := range i { - out[i] = i - } - return out - }, - }), -) - -var Static = page.Static("pages/static.html") - -func main() { - app := tapp.New() - tapp.GetEnv().Apply(app) - app.ShutdownOnSignal(syscall.SIGINT, syscall.SIGTERM) - - app.Handle("GET /{$}", Page) - app.Handle("GET /static", Static) - app.Handle("GET /", http.FileServer(http.Dir("public"))) - app.Handle("GET /contact", stdpart.HandleContactForm(nil, stdpart.PrintContact)) - app.Handle("POST /contact", stdpart.HandleContactForm(nil, stdpart.PrintContact)) - - // fmt.Println(Page.Raw()) - - app.ListenAndServe() -} diff --git a/engine/cmd/standard-test/pages/index.html b/engine/cmd/standard-test/pages/index.html deleted file mode 100644 index b7a2f80..0000000 --- a/engine/cmd/standard-test/pages/index.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Today Engine Examples - - - - -
- - - - {{- range $i, $v := SliceOfLen 5 }} - - {{ end -}} - - Link to static page -
- - - \ No newline at end of file diff --git a/engine/cmd/standard-test/pages/static.html b/engine/cmd/standard-test/pages/static.html deleted file mode 100644 index e03b202..0000000 --- a/engine/cmd/standard-test/pages/static.html +++ /dev/null @@ -1,16 +0,0 @@ - - - - - Today Engine Examples - - - - -
-

This is a normal static page.

-

It doesn't do anything except exist. Neat.

-
- - - \ No newline at end of file diff --git a/engine/cmd/standard-test/public/style.css b/engine/cmd/standard-test/public/style.css deleted file mode 100644 index c0a3157..0000000 --- a/engine/cmd/standard-test/public/style.css +++ /dev/null @@ -1,51 +0,0 @@ -/* The Great Reset */ -* { - padding: 0; - margin: 0; -} - -/* Containers */ - -html { - font-family: sans-serif; -} - -main, section, nav { - display: flex; - flex-direction: column; - gap: 1rem; -} - -main { - margin: 2rem 25%; -} - -span { - display: inline; -} - -span + span { - margin-left: .5rem; -} - -/* Forms */ - -form { - display: grid; - grid-template-columns: 1fr 3fr; - gap: .5rem; -} - -form > input[type="submit"] { - grid-column: span 2; -} - -/* Typography */ - -:not(pre) > code { - font-size: 1rem; -} - -li { - margin-left: 20px; -} \ No newline at end of file diff --git a/engine/cmd/standard-test/test_thing.html b/engine/cmd/standard-test/test_thing.html deleted file mode 100644 index 5988d75..0000000 --- a/engine/cmd/standard-test/test_thing.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/engine/component/component.go b/engine/component/component.go deleted file mode 100644 index 22370e2..0000000 --- a/engine/component/component.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (C) 2024 early (LGPL) -package component - -import ( - "git.earlybird.gay/today-engine/include" - "git.earlybird.gay/today-engine/internal/compile" -) - -type Component struct { - name string - fileName string - source include.Opener - - includes []compile.Source -} - -type Config func(*Component) - -func Includes(includes ...compile.Source) Config { - return func(p *Component) { - p.includes = includes - } -} - -func New(name string, source string, optional ...func(*Component)) *Component { - p := new(Component) - // Assign basic parameters - p.name = name - p.fileName = source - p.source = include.File(source, "git.earlybird.gay/today-engine/component") - // Run optional arguments - for _, of := range optional { - of(p) - } - return p -} - -func (p *Component) Name() string { - return p.name -} - -func (p *Component) FileName() string { - return p.fileName -} - -func (p *Component) File() include.Opener { - return p.source -} - -func (p *Component) Includes() []compile.Source { - return p.includes -} diff --git a/engine/go.mod b/engine/go.mod deleted file mode 100644 index fb5c1b4..0000000 --- a/engine/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module git.earlybird.gay/today-engine - -go 1.22.4 - -require ( - git.earlybird.gay/today-app v0.0.0-20240813010007-675f08ae35b1 - golang.org/x/text v0.17.0 -) diff --git a/engine/go.sum b/engine/go.sum deleted file mode 100644 index ca1ee0b..0000000 --- a/engine/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -git.earlybird.gay/today-app v0.0.0-20240813010007-675f08ae35b1 h1:Fbz2uRIWK6CR+jcNN0KQrna4u/kYZccn2obcla7dPfQ= -git.earlybird.gay/today-app v0.0.0-20240813010007-675f08ae35b1/go.mod h1:AxsoC2ERffYriW60C1vdV34ew6JPKxllG9mHOTs128I= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= diff --git a/engine/htmltree/attrs.go b/engine/htmltree/attrs.go deleted file mode 100644 index 4d9ab2e..0000000 --- a/engine/htmltree/attrs.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (C) 2024 early (LGPL) -package htmltree - -import "git.earlybird.gay/today-engine/internal/html" - -func GetAttr(n *html.Node, name string) string { - for _, attr := range n.Attr { - if attr.Key == name { - return attr.Val - } - } - return "" -} - -func SetAttr(n *html.Node, name string, value string) { - for i, attr := range n.Attr { - if attr.Key == name { - n.Attr = append(n.Attr[:i], n.Attr[i+1:]...) - } - } - n.Attr = append(n.Attr, html.Attribute{Key: name, Val: value}) -} diff --git a/engine/htmltree/prettify.go b/engine/htmltree/prettify.go deleted file mode 100644 index 33fbdd0..0000000 --- a/engine/htmltree/prettify.go +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright (C) 2024 early (LGPL) -package htmltree - -import ( - "fmt" - "regexp" - "strings" - - "git.earlybird.gay/today-engine/internal/html" -) - -func Walk(root *html.Node, f func(*html.Node) (bool, error)) error { - var node, child *html.Node - var siblings []*html.Node - var stop bool - var err error - - node = root - - for node != nil && err == nil { - child = node.FirstChild - if node.NextSibling != nil { - siblings = append(siblings, node.NextSibling) - } - stop, err = f(node) - - if !stop && child != nil { - node = child - child = nil - } else if nextSibling := len(siblings) - 1; nextSibling != -1 { - node = siblings[nextSibling] - siblings = siblings[:nextSibling] - } else { - node = nil - } - } - return err -} - -func Print(root *html.Node) { - Walk(root, func(n *html.Node) (bool, error) { - fmt.Printf("%p: %+v\n", n, n) - return false, nil - }) -} - -func doctype(n *html.Node) bool { - return n.Type == html.DoctypeNode -} - -func comment(n *html.Node) bool { - return n.Type == html.CommentNode -} - -func template(n *html.Node) bool { - return n.Type == html.TextNode && strings.HasPrefix(n.Data, "{{") -} - -func elem(n *html.Node) bool { - return n.Type == html.ElementNode || comment(n) || template(n) -} - -var unindentRegexp = regexp.MustCompile(`^\n\s*`) -var despaceRegexp = regexp.MustCompile(`\n\s*`) - -func Minify() func(root *html.Node) { - return func(root *html.Node) { - Walk(root, func(n *html.Node) (bool, error) { - if n.Type == html.ElementNode && n.Data == "pre" { - return true, nil - } - if n.Type == html.ElementNode && n.Data == "script" { - return true, nil - } - if n.Type == html.TextNode { - n.Data = unindentRegexp.ReplaceAllString(n.Data, "") - n.Data = despaceRegexp.ReplaceAllString(n.Data, " ") - if n.PrevSibling == nil { - n.Data = strings.TrimLeft(n.Data, " \t") - } - if n.NextSibling == nil { - n.Data = strings.TrimRight(n.Data, " \t") - } - // Remove empty nodes - if n.Data == "" { - n.Parent.RemoveChild(n) - } - } - return false, nil - }) - } -} - -func spacer(indent string, depth int) *html.Node { - return &html.Node{ - Type: html.TextNode, - Data: "\n" + strings.Repeat(indent, depth), - } -} - -func pad(n *html.Node, indent string, depth int) { - if depth == 0 { - return - } - n.InsertBefore(spacer(indent, depth), n.FirstChild) - n.InsertBefore(spacer(indent, depth-1), nil) -} - -func newline(n *html.Node, indent string, depth int) { - if n.Parent == nil { - return - } - n.Parent.InsertBefore(spacer(indent, depth), n.NextSibling) -} - -func Prettify(indent string) func(root *html.Node) { - return func(root *html.Node) { - Minify()(root) - - var prettify func(n *html.Node, depth int) - - prettify = func(n *html.Node, depth int) { - if n == nil { - return - } - if elem(n) && n.Data == "pre" { - return - } - if n.NextSibling != nil && (elem(n) || doctype(n)) && elem(n.NextSibling) { - newline(n, indent, depth-1) - } - if n.FirstChild != nil && elem(n.FirstChild) { - pad(n, indent, depth) - } - - prettify(n.FirstChild, depth+1) - prettify(n.NextSibling, depth) - } - - prettify(root, 0) - } -} diff --git a/engine/internal/compile/compile.go b/engine/internal/compile/compile.go deleted file mode 100644 index 9b88807..0000000 --- a/engine/internal/compile/compile.go +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (C) 2024 early (LGPL) -package compile - -import ( - "errors" - "html/template" - "regexp" - "strings" - - "git.earlybird.gay/today-engine/htmltree" - "git.earlybird.gay/today-engine/include" - "git.earlybird.gay/today-engine/internal/html" - "git.earlybird.gay/today-engine/render" -) - -type Source interface { - Name() string - Source() include.Opener - Includes() []Source -} - -type TemplateSource interface { - Source - IncludeTagName() bool - TemplateFuncs() template.FuncMap - OnLoad() render.OnLoadFunc -} - -type Sources map[string]Source - -func mapSources(dst Sources, slice []Source) { - for _, source := range slice { - dst[source.Name()] = source - mapSources(dst, source.Includes()) - } -} - -type Result struct { - TemplateRaw string - TemplateFuncs template.FuncMap - TemplateDataLoader render.Loader -} - -func Compile(root TemplateSource, transform ...func(root *html.Node)) (Result, error) { - var result Result - reader, err := root.Source().Open() - if err != nil { - return result, err - } - document, err := html.Parse(reader) - if err != nil { - return result, err - } - - fullDependencies := Sources{root.Name(): root} - mapSources(fullDependencies, root.Includes()) - computeRoot := &computeNode{ - name: root.Name(), - compute: root.OnLoad(), - } - // Insert an assignment to $compute so we can always raise scope, even - // in pipelines - document.InsertBefore(&html.Node{ - Type: html.TextNode, - Data: "{{ $compute := .compute }}", - }, document.FirstChild) - - // Replace template functions in the root before we add any subsources - // fmt.Println(root.TemplateFuncs()) - htmltree.Walk(document, func(n *html.Node) (bool, error) { - replaceTemplateFuncs(root, n) - return false, nil - }) - - // Insert component sources into document - for name, subSource := range fullDependencies { - // Easiest way to tell right now is what isn't a template source, - // but this bears re-evaluating later - if _, ok := subSource.(TemplateSource); ok { - continue - } - err := insertComponentSource(subSource, document) - if err != nil { - return result, err - } - delete(fullDependencies, name) - } - - // gather global template funcs - // Start with the root's template, then add template funcs for all subsource - // with a namespace - templateFuncs := make(template.FuncMap) - for fname, f := range root.TemplateFuncs() { - templateFuncs[snakeCase(root.Name())+"_"+fname] = f - } - - var process func(n *html.Node, c *computeNode) error - process = func(n *html.Node, c *computeNode) error { - childComputeNode := c - siblingComputeNode := c - if n.Type == html.ElementNode { - if subSource, ok := fullDependencies[n.Data]; ok { - if templateSource, ok := subSource.(TemplateSource); ok { - // Template sources (parts) are inserted inline - // Add template funcs - for fname, f := range templateSource.TemplateFuncs() { - templateFuncs[snakeCase(n.Data)+"_"+fname] = f - } - // Parse HTML fragment and replace the content of the node with it - computeSubSource, err := insertTemplateSource(templateSource, n) - if err != nil { - return err - } - // Add compute node for subsource - c.children = append(c.children, computeSubSource) - childComputeNode = computeSubSource - } - } - } - var procErr error - if n.FirstChild != nil { - procErr = errors.Join(procErr, process(n.FirstChild, childComputeNode)) - } - if n.NextSibling != nil { - procErr = errors.Join(procErr, process(n.NextSibling, siblingComputeNode)) - } - return procErr - } - err = process(document, computeRoot) - if err != nil { - return result, err - } - - // POSTPROCESSING - - // Assign .SetDot to $setDot - document.InsertBefore(&html.Node{ - Type: html.TextNode, - Data: `{{ $setDot := .SetDot }}{{ with .Data }}`, - }, document.FirstChild) - // Add end to end - document.InsertBefore(&html.Node{ - Type: html.TextNode, - Data: "{{ end -}}", - }, nil) - - // Split up templates in text nodes - splitTemplateNodes(document) - - // Traverse the document and add $setDot on entry and exit from pipelines - addSetDots(document) - - for _, tf := range transform { - tf(document) - } - - raw, err := renderDocument(document) - if err != nil { - return result, err - } - result = Result{ - TemplateRaw: raw, - TemplateFuncs: templateFuncs, - TemplateDataLoader: computeRoot, - } - return result, err -} - -func renderDocument(document *html.Node) (string, error) { - // Basic render - buf := new(strings.Builder) - err := html.Render(buf, document) - if err != nil { - return "", err - } - raw := html.UnescapeString(buf.String()) - - // Clean boolean attributes - raw = removeEmptyAttrValues(raw) - - return raw, nil -} - -var emptyAttrRegex = regexp.MustCompile(`%%(?P[^<>]+?)%%=""`) - -func removeEmptyAttrValues(raw string) string { - return emptyAttrRegex.ReplaceAllString(raw, "$templateattr") -} diff --git a/engine/internal/compile/component.go b/engine/internal/compile/component.go deleted file mode 100644 index 7f77949..0000000 --- a/engine/internal/compile/component.go +++ /dev/null @@ -1,67 +0,0 @@ -package compile - -import ( - "errors" - - "git.earlybird.gay/today-engine/htmltree" - "git.earlybird.gay/today-engine/internal/html" - "git.earlybird.gay/today-engine/internal/html/atom" -) - -var ErrBadComponentFormat = errors.New("web components must either be a script or a template and script") - -// insertComponentSource inserts a web component subSource into a document. -// Unlike insertTemplateSource, this expects the root document and not the -// context where the web component is being included. -func insertComponentSource(subSource Source, document *html.Node) error { - // ===== SUBSOURCE PROCESSING ===== - // Parse the subSource in the context of a node named subSource.Name(). - // subSource should be: - //