diff --git a/go.mod b/go.mod index 5192329..5bf48dd 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,11 @@ require ( ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/stretchr/testify v1.11.1 // indirect golang.org/x/sys v0.25.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d6a1d7b..9de3ae1 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A= @@ -11,6 +13,10 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0 github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -18,3 +24,6 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 5d993f9..9752846 100644 --- a/main.go +++ b/main.go @@ -89,7 +89,7 @@ func main() { os.Exit(1) } case "run": - if err := server.Start(dir); err != nil { + if _, err := server.Start(dir); err != nil { if errors.Is(err, os.ErrNotExist) { fmt.Fprintf(os.Stderr, "%s does not exist\n", dir) fmt.Fprintf(os.Stderr, "Try: fes new %s\n", dir) diff --git a/modules/server/server.go b/modules/server/server.go index 10c329b..87ad8f7 100644 --- a/modules/server/server.go +++ b/modules/server/server.go @@ -1,11 +1,10 @@ package server import ( + "context" "fes/modules/config" "fes/modules/ui" "fmt" - "github.com/pelletier/go-toml/v2" - lua "github.com/yuin/gopher-lua" "html/template" "io/fs" "net/http" @@ -15,6 +14,9 @@ import ( "sort" "strings" "time" + + "github.com/pelletier/go-toml/v2" + lua "github.com/yuin/gopher-lua" ) /* this is the request data we pass over the bus to the application, via the fes.bus interface */ @@ -409,9 +411,13 @@ func readArchive(w http.ResponseWriter, route string) error { } /* start the Fes server */ -func Start(dir string) error { +func Start(dir string) (*http.Server, error) { + if _, err := os.Stat(dir); os.IsNotExist(err) { + return nil, fmt.Errorf("directory does not exist: %s", dir) + } + if err := os.Chdir(dir); err != nil { - return ui.Error(fmt.Sprintf("failed to change directory to %s", dir), err) + return nil, fmt.Errorf("failed to change directory to %s: %w", dir, err) } cfg := parseConfig() @@ -467,5 +473,22 @@ func Start(dir string) error { }) fmt.Printf("Server is running on http://localhost:%d\n", *config.Port) - return http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", *config.Port), nil) + + srv := &http.Server{ + Addr: fmt.Sprintf("0.0.0.0:%d", *config.Port), + } + + go func() { + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + ui.Error("Server failed: %v", err) + } + }() + + return srv, nil +} + +func Stop(srv *http.Server) error { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + return srv.Shutdown(ctx) } diff --git a/modules/server/server_test.go b/modules/server/server_test.go new file mode 100644 index 0000000..f614384 --- /dev/null +++ b/modules/server/server_test.go @@ -0,0 +1,1093 @@ +package server + +import ( + "io" + "net" + "net/http" + "os" + "path/filepath" + "strconv" + "strings" + "testing" + "time" + + "fes/modules/config" +) + +func resetServeMux() { + http.DefaultServeMux = http.NewServeMux() +} + +func chdir(t *testing.T, dir string) func() { + cwd, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + if err := os.Chdir(dir); err != nil { + t.Fatal(err) + } + return func() { + os.Chdir(cwd) + } +} + +func freePort(t *testing.T) int { + l, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatal(err) + } + defer l.Close() + return l.Addr().(*net.TCPAddr).Port +} + +func startServer(t *testing.T, dir string) (*http.Server, int) { + resetServeMux() + + restore := chdir(t, dir) + t.Cleanup(restore) + + port := freePort(t) + config.Port = &port + + srv, err := Start(".") + if err != nil { + t.Fatal(err) + } + + t.Cleanup(func() { + Stop(srv) + }) + + time.Sleep(100 * time.Millisecond) + + return srv, port +} + +func get(t *testing.T, port int, path string) (string, int) { + resp, err := http.Get("http://127.0.0.1:" + strconv.Itoa(port) + path) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + + b, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + + return string(b), resp.StatusCode +} + +type testCase struct { + name string + dir string + path string + expectBody string + expectEmpty bool + expectStatus int + expectHTMLHead bool +} + +func TestServer(t *testing.T) { + cases := []testCase{ + { + name: "lua basic", + dir: filepath.Join("..", "..", "test", "files", "lua-basic"), + path: "/", + expectBody: ` + + + + + +Document + + + +
+

Hello, World!

+
+ +`, + }, + { + name: "lua include", + dir: filepath.Join("..", "..", "test", "files", "lua-include"), + path: "/", + expectBody: ` + + + + + +Document + + + +
+

Hello, World!

+

Hello, World!

+
+ +`, + }, + { + name: "lua error", + dir: filepath.Join("..", "..", "test", "files", "lua-error"), + path: "/", + expectStatus: http.StatusInternalServerError, + }, + { + name: "markdown", + dir: filepath.Join("..", "..", "test", "files", "markdown"), + path: "/", + expectBody: strings.ReplaceAll(` +

Markdown: Syntax

+ + + +

Note: This document is itself written using Markdown; you +can see the source for it by adding ‘.text’ to the URL.

+ +
+ +

Overview

+ +

Philosophy

+ +

Markdown is intended to be as easy-to-read and easy-to-write as is feasible.

+ +

Readability, however, is emphasized above all else. A Markdown-formatted +document should be publishable as-is, as plain text, without looking +like it’s been marked up with tags or formatting instructions. While +Markdown’s syntax has been influenced by several existing text-to-HTML +filters – including Setext, atx, Textile, reStructuredText, +Grutatext, and EtText – the single biggest source of +inspiration for Markdown’s syntax is the format of plain text email.

+ +

Block Elements

+ +

Paragraphs and Line Breaks

+ +

A paragraph is simply one or more consecutive lines of text, separated +by one or more blank lines. (A blank line is any line that looks like a +blank line – a line containing nothing but spaces or tabs is considered +blank.) Normal paragraphs should not be indented with spaces or tabs.

+ +

The implication of the “one or more consecutive lines of text” rule is +that Markdown supports “hard-wrapped” text paragraphs. This differs +significantly from most other text-to-HTML formatters (including Movable +Type’s “Convert Line Breaks” option) which translate every line break +character in a paragraph into a <br /> tag.

+ +

When you do want to insert a <br /> break tag using Markdown, you +end a line with two or more spaces, then type return.

+ +

Headers

+ +

Markdown supports two styles of headers, [Setext] [1] and [atx] [2].

+ +

Optionally, you may “close” atx-style headers. This is purely +cosmetic – you can use this if you think it looks better. The +closing hashes don’t even need to match the number of hashes +used to open the header. (The number of opening hashes +determines the header level.)

+ +

Blockquotes

+ +

Markdown uses email-style > characters for blockquoting. If you’re +familiar with quoting passages of text in an email message, then you +know how to create a blockquote in Markdown. It looks best if you hard +wrap the text and put a > before every line:

+ +
+

This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

+ +

Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing.

+
+ +

Markdown allows you to be lazy and only put the > before the first +line of a hard-wrapped paragraph:

+ +
+

This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.

+ +

Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing.

+
+ +

Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by +adding additional levels of >:

+ +
+

This is the first level of quoting.

+ +
+

This is nested blockquote.

+
+ +

Back to the first level.

+
+ +

Blockquotes can contain other Markdown elements, including headers, lists, +and code blocks:

+ +
+

This is a header.

+ +
    +
  1. This is the first list item.
  2. +
  3. This is the second list item.
  4. +
+ +

Here’s some example code:

+ +
return shell_exec("echo $input | $markdown_script");
+
+
+ +

Any decent text editor should make email-style quoting easy. For +example, with BBEdit, you can make a selection and choose Increase +Quote Level from the Text menu.

+ +

Lists

+ +

Markdown supports ordered (numbered) and unordered (bulleted) lists.

+ +

Unordered lists use asterisks, pluses, and hyphens – interchangably +– as list markers:

+ + + +

is equivalent to:

+ + + +

and:

+ + + +

Ordered lists use numbers followed by periods:

+ +
    +
  1. Bird
  2. +
  3. McHale
  4. +
  5. Parish
  6. +
+ +

It’s important to note that the actual numbers you use to mark the +list have no effect on the HTML output Markdown produces. The HTML +Markdown produces from the above list is:

+ +

If you instead wrote the list in Markdown like this:

+ +
    +
  1. Bird
  2. +
  3. McHale
  4. +
  5. Parish
  6. +
+ +

or even:

+ +
    +
  1. Bird
  2. +
  3. McHale
  4. +
  5. Parish
  6. +
+ +

you’d get the exact same HTML output. The point is, if you want to, +you can use ordinal numbers in your ordered Markdown lists, so that +the numbers in your source match the numbers in your published HTML. +But if you want to be lazy, you don’t have to.

+ +

To make lists look nice, you can wrap items with hanging indents:

+ + + +

But if you want to be lazy, you don’t have to:

+ + + +

List items may consist of multiple paragraphs. Each subsequent +paragraph in a list item must be indented by either 4 spaces +or one tab:

+ +
    +
  1. This is a list item with two paragraphs. Lorem ipsum dolor +sit amet, consectetuer adipiscing elit. Aliquam hendrerit +mi posuere lectus.

    + +

    Vestibulum enim wisi, viverra nec, fringilla in, laoreet +vitae, risus. Donec sit amet nisl. Aliquam semper ipsum +sit amet velit.

  2. + +
  3. Suspendisse id sem consectetuer libero luctus adipiscing.

  4. +
+ +

It looks nice if you indent every line of the subsequent +paragraphs, but here again, Markdown will allow you to be +lazy:

+ + + +

To put a blockquote within a list item, the blockquote’s > +delimiters need to be indented:

+ + + +

To put a code block within a list item, the code block needs +to be indented twice – 8 spaces or two tabs:

+ + + +

Code Blocks

+ +

Pre-formatted code blocks are used for writing about programming or +markup source code. Rather than forming normal paragraphs, the lines +of a code block are interpreted literally. Markdown wraps a code block +in both <pre> and <code> tags.

+ +

To produce a code block in Markdown, simply indent every line of the +block by at least 4 spaces or 1 tab.

+ +

This is a normal paragraph:

+ +
This is a code block.
+
+ +

Here is an example of AppleScript:

+ +
tell application "Foo"
+    beep
+end tell
+
+ +

A code block continues until it reaches a line that is not indented +(or the end of the article).

+ +

Within a code block, ampersands (&) and angle brackets (< and >) +are automatically converted into HTML entities. This makes it very +easy to include example HTML source code using Markdown – just paste +it and indent it, and Markdown will handle the hassle of encoding the +ampersands and angle brackets. For example, this:

+ +
<div class="footer">
+    &copy; 2004 Foo Corporation
+</div>
+
+ +

Regular Markdown syntax is not processed within code blocks. E.g., +asterisks are just literal asterisks within a code block. This means +it’s also easy to use Markdown to write about Markdown’s own syntax.

+ +
tell application "Foo"
+    beep
+end tell
+
+ +

Span Elements

+ + + +

Markdown supports two style of links: inline and reference.

+ +

In both styles, the link text is delimited by [square brackets].

+ +

To create an inline link, use a set of regular parentheses immediately +after the link text’s closing square bracket. Inside the parentheses, +put the URL where you want the link to point, along with an optional +title for the link, surrounded in quotes. For example:

+ +

This is an example inline link.

+ +

This link has no title attribute.

+ +

Emphasis

+ +

Markdown treats asterisks (*) and underscores (_) as indicators of +emphasis. Text wrapped with one * or _ will be wrapped with an +HTML <em> tag; double *’s or _’s will be wrapped with an HTML +<strong> tag. E.g., this input:

+ +

single asterisks

+ +

single underscores

+ +

double asterisks

+ +

double underscores

+ +

Code

+ +

To indicate a span of code, wrap it with backtick quotes ($$). +Unlike a pre-formatted code block, a code span indicates code within a +normal paragraph. For example:

+ +

Use the printf() function.

`, "$$", "`"), + }, + { + name: "archive index", + dir: filepath.Join("..", "..", "test", "files", "archive"), + path: "/archive/", + expectHTMLHead: true, + }, + { + name: "not found", + dir: filepath.Join("..", "..", "test", "files", "minimal"), + path: "/does-not-exist", + expectStatus: http.StatusNotFound, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, port := startServer(t, tc.dir) + + body, status := get(t, port, tc.path) + + if tc.expectStatus != 0 { + if status != tc.expectStatus { + t.Fatalf("expected %d, got %d", tc.expectStatus, status) + } + return + } + + if tc.expectBody != "" { + if body != tc.expectBody { + t.Fatalf("unexpected body:\n--- got ---\n%s\n--- want ---\n%s", body, tc.expectBody) + } + return + } + + if body == "" && !tc.expectEmpty { + t.Fatal("empty response") + } + + if tc.expectHTMLHead && len(body) >= 6 && body[:6] != "" { + t.Fatal("archive index is not html") + } + }) + } +} diff --git a/test/files/archive/Fes.toml b/test/files/archive/Fes.toml new file mode 100644 index 0000000..5acc4c8 --- /dev/null +++ b/test/files/archive/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "archive" +version = "0.0.1" +authors = ["fSD"] diff --git a/test/files/archive/README.md b/test/files/archive/README.md new file mode 100644 index 0000000..07271ca --- /dev/null +++ b/test/files/archive/README.md @@ -0,0 +1,33 @@ +# archive + +``` +fes new archive +``` + +> **Know what you are doing?** Delete this file. Have fun! + +## Project Structure + +Inside your Fes project, you'll see the following directories and files: + +``` +. +├── Fes.toml +├── README.md +└── www + └── index.lua +``` + +Fes looks for `.lua` files in the `www/` directory. Each file is exposed as a route based on its file name. + +## Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `fes run .` | Runs the project at `.` | + +## What to learn more? + +Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html). \ No newline at end of file diff --git a/test/files/archive/archive/seal.png b/test/files/archive/archive/seal.png new file mode 100644 index 0000000..74634c2 Binary files /dev/null and b/test/files/archive/archive/seal.png differ diff --git a/test/files/archive/www/index.lua b/test/files/archive/www/index.lua new file mode 100644 index 0000000..d369c0e --- /dev/null +++ b/test/files/archive/www/index.lua @@ -0,0 +1,8 @@ +local fes = require("fes") +local site = fes.fes() + +-- site.copyright = fes.util.copyright("https://example.com", "vx-clutch") + +site:h1("Hello, World!") + +return site \ No newline at end of file diff --git a/test/files/lua-basic/Fes.toml b/test/files/lua-basic/Fes.toml new file mode 100644 index 0000000..a47b09f --- /dev/null +++ b/test/files/lua-basic/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "lua-basic" +version = "0.0.1" +authors = ["fSD"] diff --git a/test/files/lua-basic/README.md b/test/files/lua-basic/README.md new file mode 100644 index 0000000..300a5e8 --- /dev/null +++ b/test/files/lua-basic/README.md @@ -0,0 +1,33 @@ +# lua-basic + +``` +fes new lua-basic +``` + +> **Know what you are doing?** Delete this file. Have fun! + +## Project Structure + +Inside your Fes project, you'll see the following directories and files: + +``` +. +├── Fes.toml +├── README.md +└── www + └── index.lua +``` + +Fes looks for `.lua` files in the `www/` directory. Each file is exposed as a route based on its file name. + +## Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `fes run .` | Runs the project at `.` | + +## What to learn more? + +Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html). \ No newline at end of file diff --git a/test/files/lua-basic/www/index.lua b/test/files/lua-basic/www/index.lua new file mode 100644 index 0000000..8b68d9d --- /dev/null +++ b/test/files/lua-basic/www/index.lua @@ -0,0 +1,8 @@ +local fes = require("fes") +local site = fes.fes() + +-- site.copyright = fes.util.copyright("https://example.com", "vx-clutch") + +site:h1("Hello, World!") + +return site diff --git a/test/files/lua-error/Fes.toml b/test/files/lua-error/Fes.toml new file mode 100644 index 0000000..2cb1ebe --- /dev/null +++ b/test/files/lua-error/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "lua-error" +version = "0.0.1" +authors = ["fSD"] diff --git a/test/files/lua-error/README.md b/test/files/lua-error/README.md new file mode 100644 index 0000000..4bdcb6c --- /dev/null +++ b/test/files/lua-error/README.md @@ -0,0 +1,33 @@ +# lua-error + +``` +fes new lua-error +``` + +> **Know what you are doing?** Delete this file. Have fun! + +## Project Structure + +Inside your Fes project, you'll see the following directories and files: + +``` +. +├── Fes.toml +├── README.md +└── www + └── index.lua +``` + +Fes looks for `.lua` files in the `www/` directory. Each file is exposed as a route based on its file name. + +## Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `fes run .` | Runs the project at `.` | + +## What to learn more? + +Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html). \ No newline at end of file diff --git a/test/files/lua-error/www/index.lua b/test/files/lua-error/www/index.lua new file mode 100644 index 0000000..c5aeeb0 --- /dev/null +++ b/test/files/lua-error/www/index.lua @@ -0,0 +1,6 @@ +local fes = require("fes") +local site = fes.fes() + +Hello, World! + +return site diff --git a/test/files/lua-include/Fes.toml b/test/files/lua-include/Fes.toml new file mode 100644 index 0000000..805113e --- /dev/null +++ b/test/files/lua-include/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "lua-include" +version = "0.0.1" +authors = ["fSD"] diff --git a/test/files/lua-include/README.md b/test/files/lua-include/README.md new file mode 100644 index 0000000..d537fda --- /dev/null +++ b/test/files/lua-include/README.md @@ -0,0 +1,33 @@ +# lua-include + +``` +fes new lua-include +``` + +> **Know what you are doing?** Delete this file. Have fun! + +## Project Structure + +Inside your Fes project, you'll see the following directories and files: + +``` +. +├── Fes.toml +├── README.md +└── www + └── index.lua +``` + +Fes looks for `.lua` files in the `www/` directory. Each file is exposed as a route based on its file name. + +## Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `fes run .` | Runs the project at `.` | + +## What to learn more? + +Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html). \ No newline at end of file diff --git a/test/files/lua-include/include/test.lua b/test/files/lua-include/include/test.lua new file mode 100644 index 0000000..c15aba7 --- /dev/null +++ b/test/files/lua-include/include/test.lua @@ -0,0 +1,7 @@ +local test = {} + +test.render = function(std) + return std.h2("Hello, World!") +end + +return test diff --git a/test/files/lua-include/www/index.lua b/test/files/lua-include/www/index.lua new file mode 100644 index 0000000..537e22f --- /dev/null +++ b/test/files/lua-include/www/index.lua @@ -0,0 +1,10 @@ +local fes = require("fes") +local site = fes.fes() + +-- site.copyright = fes.util.copyright("https://example.com", "vx-clutch") + +site:h1("Hello, World!") + +site:note(fes.app.test.render(fes.std)) + +return site diff --git a/test/files/markdown/Fes.toml b/test/files/markdown/Fes.toml new file mode 100644 index 0000000..f800d4d --- /dev/null +++ b/test/files/markdown/Fes.toml @@ -0,0 +1,5 @@ +[app] + +name = "markdown" +version = "0.0.1" +authors = ["vx-clutch"] \ No newline at end of file diff --git a/test/files/markdown/README.md b/test/files/markdown/README.md new file mode 100644 index 0000000..3f62240 --- /dev/null +++ b/test/files/markdown/README.md @@ -0,0 +1,33 @@ +# markdown + +``` +fes new markdown +``` + +> **Know what you are doing?** Delete this file. Have fun! + +## Project Structure + +Inside your Fes project, you'll see the following directories and files: + +``` +. +├── Fes.toml +├── README.md +└── www + └── index.lua +``` + +Fes looks for `.lua` files in the `www/` directory. Each file is exposed as a route based on its file name. + +## Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `fes run .` | Runs the project at `.` | + +## What to learn more? + +Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html). \ No newline at end of file diff --git a/test/files/markdown/www/index.md b/test/files/markdown/www/index.md new file mode 100644 index 0000000..70dbadc --- /dev/null +++ b/test/files/markdown/www/index.md @@ -0,0 +1,310 @@ +# Markdown: Syntax + +* [Overview](#overview) + * [Philosophy](#philosophy) + * [Inline HTML](#html) + * [Automatic Escaping for Special Characters](#autoescape) +* [Block Elements](#block) + * [Paragraphs and Line Breaks](#p) + * [Headers](#header) + * [Blockquotes](#blockquote) + * [Lists](#list) + * [Code Blocks](#precode) + * [Horizontal Rules](#hr) +* [Span Elements](#span) + * [Links](#link) + * [Emphasis](#em) + * [Code](#code) + * [Images](#img) +* [Miscellaneous](#misc) + * [Backslash Escapes](#backslash) + * [Automatic Links](#autolink) + + +**Note:** This document is itself written using Markdown; you +can [see the source for it by adding '.text' to the URL](/projects/markdown/syntax.text). + +---- + +## Overview + +### Philosophy + +Markdown is intended to be as easy-to-read and easy-to-write as is feasible. + +Readability, however, is emphasized above all else. A Markdown-formatted +document should be publishable as-is, as plain text, without looking +like it's been marked up with tags or formatting instructions. While +Markdown's syntax has been influenced by several existing text-to-HTML +filters -- including [Setext](http://docutils.sourceforge.net/mirror/setext.html), [atx](http://www.aaronsw.com/2002/atx/), [Textile](http://textism.com/tools/textile/), [reStructuredText](http://docutils.sourceforge.net/rst.html), +[Grutatext](http://www.triptico.com/software/grutatxt.html), and [EtText](http://ettext.taint.org/doc/) -- the single biggest source of +inspiration for Markdown's syntax is the format of plain text email. + +## Block Elements + +### Paragraphs and Line Breaks + +A paragraph is simply one or more consecutive lines of text, separated +by one or more blank lines. (A blank line is any line that looks like a +blank line -- a line containing nothing but spaces or tabs is considered +blank.) Normal paragraphs should not be indented with spaces or tabs. + +The implication of the "one or more consecutive lines of text" rule is +that Markdown supports "hard-wrapped" text paragraphs. This differs +significantly from most other text-to-HTML formatters (including Movable +Type's "Convert Line Breaks" option) which translate every line break +character in a paragraph into a `
` tag. + +When you *do* want to insert a `
` break tag using Markdown, you +end a line with two or more spaces, then type return. + +### Headers + +Markdown supports two styles of headers, [Setext] [1] and [atx] [2]. + +Optionally, you may "close" atx-style headers. This is purely +cosmetic -- you can use this if you think it looks better. The +closing hashes don't even need to match the number of hashes +used to open the header. (The number of opening hashes +determines the header level.) + + +### Blockquotes + +Markdown uses email-style `>` characters for blockquoting. If you're +familiar with quoting passages of text in an email message, then you +know how to create a blockquote in Markdown. It looks best if you hard +wrap the text and put a `>` before every line: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. +> +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +> id sem consectetuer libero luctus adipiscing. + +Markdown allows you to be lazy and only put the `>` before the first +line of a hard-wrapped paragraph: + +> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet, +consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus. +Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus. + +> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse +id sem consectetuer libero luctus adipiscing. + +Blockquotes can be nested (i.e. a blockquote-in-a-blockquote) by +adding additional levels of `>`: + +> This is the first level of quoting. +> +> > This is nested blockquote. +> +> Back to the first level. + +Blockquotes can contain other Markdown elements, including headers, lists, +and code blocks: + +> ## This is a header. +> +> 1. This is the first list item. +> 2. This is the second list item. +> +> Here's some example code: +> +> return shell_exec("echo $input | $markdown_script"); + +Any decent text editor should make email-style quoting easy. For +example, with BBEdit, you can make a selection and choose Increase +Quote Level from the Text menu. + + +### Lists + +Markdown supports ordered (numbered) and unordered (bulleted) lists. + +Unordered lists use asterisks, pluses, and hyphens -- interchangably +-- as list markers: + +* Red +* Green +* Blue + +is equivalent to: + ++ Red ++ Green ++ Blue + +and: + +- Red +- Green +- Blue + +Ordered lists use numbers followed by periods: + +1. Bird +2. McHale +3. Parish + +It's important to note that the actual numbers you use to mark the +list have no effect on the HTML output Markdown produces. The HTML +Markdown produces from the above list is: + +If you instead wrote the list in Markdown like this: + +1. Bird +1. McHale +1. Parish + +or even: + +3. Bird +1. McHale +8. Parish + +you'd get the exact same HTML output. The point is, if you want to, +you can use ordinal numbers in your ordered Markdown lists, so that +the numbers in your source match the numbers in your published HTML. +But if you want to be lazy, you don't have to. + +To make lists look nice, you can wrap items with hanging indents: + +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. + Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, + viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. + Suspendisse id sem consectetuer libero luctus adipiscing. + +But if you want to be lazy, you don't have to: + +* Lorem ipsum dolor sit amet, consectetuer adipiscing elit. +Aliquam hendrerit mi posuere lectus. Vestibulum enim wisi, +viverra nec, fringilla in, laoreet vitae, risus. +* Donec sit amet nisl. Aliquam semper ipsum sit amet velit. +Suspendisse id sem consectetuer libero luctus adipiscing. + +List items may consist of multiple paragraphs. Each subsequent +paragraph in a list item must be indented by either 4 spaces +or one tab: + +1. This is a list item with two paragraphs. Lorem ipsum dolor + sit amet, consectetuer adipiscing elit. Aliquam hendrerit + mi posuere lectus. + + Vestibulum enim wisi, viverra nec, fringilla in, laoreet + vitae, risus. Donec sit amet nisl. Aliquam semper ipsum + sit amet velit. + +2. Suspendisse id sem consectetuer libero luctus adipiscing. + +It looks nice if you indent every line of the subsequent +paragraphs, but here again, Markdown will allow you to be +lazy: + +* This is a list item with two paragraphs. + + This is the second paragraph in the list item. You're +only required to indent the first line. Lorem ipsum dolor +sit amet, consectetuer adipiscing elit. + +* Another item in the same list. + +To put a blockquote within a list item, the blockquote's `>` +delimiters need to be indented: + +* A list item with a blockquote: + + > This is a blockquote + > inside a list item. + +To put a code block within a list item, the code block needs +to be indented *twice* -- 8 spaces or two tabs: + +* A list item with a code block: + + + +### Code Blocks + +Pre-formatted code blocks are used for writing about programming or +markup source code. Rather than forming normal paragraphs, the lines +of a code block are interpreted literally. Markdown wraps a code block +in both `
` and `` tags.
+
+To produce a code block in Markdown, simply indent every line of the
+block by at least 4 spaces or 1 tab.
+
+This is a normal paragraph:
+
+    This is a code block.
+
+Here is an example of AppleScript:
+
+    tell application "Foo"
+        beep
+    end tell
+
+A code block continues until it reaches a line that is not indented
+(or the end of the article).
+
+Within a code block, ampersands (`&`) and angle brackets (`<` and `>`)
+are automatically converted into HTML entities. This makes it very
+easy to include example HTML source code using Markdown -- just paste
+it and indent it, and Markdown will handle the hassle of encoding the
+ampersands and angle brackets. For example, this:
+
+    
+
+Regular Markdown syntax is not processed within code blocks. E.g.,
+asterisks are just literal asterisks within a code block. This means
+it's also easy to use Markdown to write about Markdown's own syntax.
+
+```
+tell application "Foo"
+    beep
+end tell
+```
+
+## Span Elements
+
+### Links
+
+Markdown supports two style of links: *inline* and *reference*.
+
+In both styles, the link text is delimited by [square brackets].
+
+To create an inline link, use a set of regular parentheses immediately
+after the link text's closing square bracket. Inside the parentheses,
+put the URL where you want the link to point, along with an *optional*
+title for the link, surrounded in quotes. For example:
+
+This is [an example](http://example.com/) inline link.
+
+[This link](http://example.net/) has no title attribute.
+
+### Emphasis
+
+Markdown treats asterisks (`*`) and underscores (`_`) as indicators of
+emphasis. Text wrapped with one `*` or `_` will be wrapped with an
+HTML `` tag; double `*`'s or `_`'s will be wrapped with an HTML
+`` tag. E.g., this input:
+
+*single asterisks*
+
+_single underscores_
+
+**double asterisks**
+
+__double underscores__
+
+### Code
+
+To indicate a span of code, wrap it with backtick quotes (`` ` ``).
+Unlike a pre-formatted code block, a code span indicates code within a
+normal paragraph. For example:
+
+Use the `printf()` function.
diff --git a/test/files/minimal/Fes.toml b/test/files/minimal/Fes.toml
new file mode 100644
index 0000000..6ccae2a
--- /dev/null
+++ b/test/files/minimal/Fes.toml
@@ -0,0 +1,5 @@
+[app]
+
+name = "minimal"
+version = "0.0.1"
+authors = ["fSD"]
diff --git a/test/files/minimal/README.md b/test/files/minimal/README.md
new file mode 100644
index 0000000..8043645
--- /dev/null
+++ b/test/files/minimal/README.md
@@ -0,0 +1,33 @@
+# minimal
+
+```
+fes new minimal
+```
+
+> **Know what you are doing?** Delete this file. Have fun!
+
+## Project Structure
+
+Inside your Fes project, you'll see the following directories and files:
+
+```
+.
+├── Fes.toml
+├── README.md
+└── www
+    └── index.lua
+```
+
+Fes looks for `.lua` files in the `www/` directory. Each file is exposed as a route based on its file name.
+
+## Commands
+
+All commands are run from the root of the project, from a terminal:
+
+| Command                   | Action                                           |
+| :------------------------ | :----------------------------------------------- |
+| `fes run .`               | Runs the project at `.`                          |
+
+## What to learn more?
+
+Check out [Fes's docs](https://docs.vxserver.dev/static/fes.html).
\ No newline at end of file
diff --git a/test/files/minimal/www/index.lua b/test/files/minimal/www/index.lua
new file mode 100644
index 0000000..5e0506d
--- /dev/null
+++ b/test/files/minimal/www/index.lua
@@ -0,0 +1 @@
+return "Hello, World!"