Compare commits
17 Commits
restructur
...
4eaead6abc
| Author | SHA1 | Date | |
|---|---|---|---|
| 4eaead6abc | |||
| 4abf2969ca | |||
| 1c229f1b3e | |||
| 5a733b8642 | |||
| 11ab1630be | |||
| 5fabd0233d | |||
| c43e905729 | |||
| 3430141184 | |||
| c5fe2eb7e7 | |||
| afd0d9eef4 | |||
| b593aa26f0 | |||
| fb8dc3cb90 | |||
| c681e342a0 | |||
| 99e437b42b | |||
| 56f22bb472 | |||
| e53cc17025 | |||
| 2798cd6553 |
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
FROM golang:1.25-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
|
RUN apk add --no-cache git build-base
|
||||||
|
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENV CGO_ENABLED=0
|
||||||
|
ENV GOOS=linux
|
||||||
|
ENV GOARCH=amd64
|
||||||
|
|
||||||
|
RUN go build -ldflags="-X fes/modules/version.gitCommit=$(git rev-parse --short HEAD) -s -w" -o fes
|
||||||
|
|
||||||
|
FROM scratch
|
||||||
|
|
||||||
|
COPY --from=builder /src/fes /fes
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
ENTRYPOINT ["/fes"]
|
||||||
|
CMD ["run", "/app"]
|
||||||
@@ -34,7 +34,7 @@ go install fes
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
Run `fes run doc` for the documentation website or goto [docs.vxserver.dev](https://docs.vxserver.dev)
|
Run `fes doc` for the documentation website or goto [docs.vxserver.dev](https://docs.vxserver.dev)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
local foo = {}
|
|
||||||
|
|
||||||
foo.render = function()
|
|
||||||
return "This was called from a foo function"
|
|
||||||
end
|
|
||||||
|
|
||||||
return foo
|
|
||||||
4
examples/archive/README.md
Normal file
4
examples/archive/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# archive
|
||||||
|
|
||||||
|
This example demonstrates the archive feature of Fes it is useful for file
|
||||||
|
sharing purposes.
|
||||||
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.8 MiB |
@@ -1,17 +1,10 @@
|
|||||||
local fes = require("fes")
|
local fes = require("fes")
|
||||||
local std = fes.std
|
|
||||||
|
|
||||||
local site = fes.fes()
|
local site = fes.fes()
|
||||||
|
|
||||||
site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "fSD")
|
site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "fSD")
|
||||||
|
|
||||||
site:h1("Hello, World!")
|
site:h1("Hello, World!")
|
||||||
|
|
||||||
site:note(fes.util.cc {
|
site:a("/archive", fes.std.h2("To the file room!"))
|
||||||
std.h2("Files"),
|
|
||||||
std.ul {
|
|
||||||
std.a("/archive", "to the file room!"),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return site
|
return site
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[app]
|
[app]
|
||||||
|
|
||||||
name = "advanced"
|
name = "best"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
authors = ["vx-clutch"]
|
authors = ["vx-clutch"]
|
||||||
23
examples/best/README.md
Normal file
23
examples/best/README.md
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# best
|
||||||
|
|
||||||
|
This is an example of best practices for the Fes framework.
|
||||||
|
|
||||||
|
## Parts
|
||||||
|
|
||||||
|
With best practice we can break our sites into a few parts.
|
||||||
|
|
||||||
|
## Index
|
||||||
|
|
||||||
|
The main page of the site loads in the header and the footer, as well as shows
|
||||||
|
some core information
|
||||||
|
|
||||||
|
## Include
|
||||||
|
|
||||||
|
Within include the header and footer are defined.
|
||||||
|
|
||||||
|
* **Header:** Site navigation and name display
|
||||||
|
* **Footer:** Extra and external information.
|
||||||
|
|
||||||
|
## Static
|
||||||
|
|
||||||
|
This is where we store our favicon.
|
||||||
13
examples/best/include/footer.lua
Normal file
13
examples/best/include/footer.lua
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
local footer = {}
|
||||||
|
|
||||||
|
footer.render = function(std)
|
||||||
|
return table.concat({
|
||||||
|
std.h2("Other resources"),
|
||||||
|
std.tl({
|
||||||
|
std.external("https://git.vxserver.dev/fSD/fes", "Fes source"),
|
||||||
|
std.external("https://docs.vxserver.dev/static/fes.html", "Documentation"),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
return footer
|
||||||
7
examples/best/include/header.lua
Normal file
7
examples/best/include/header.lua
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
local header = {}
|
||||||
|
|
||||||
|
header.render = function(std)
|
||||||
|
return std.center(std.ha("/", std.h1("Best Practices")))
|
||||||
|
end
|
||||||
|
|
||||||
|
return header
|
||||||
BIN
examples/best/static/favicon.ico
Normal file
BIN
examples/best/static/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
20
examples/best/www/index.lua
Normal file
20
examples/best/www/index.lua
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
local fes = require("fes")
|
||||||
|
local std = fes.std
|
||||||
|
local u = fes.util
|
||||||
|
|
||||||
|
local site = fes.fes()
|
||||||
|
|
||||||
|
site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "fSD")
|
||||||
|
site.title = "Best practices"
|
||||||
|
site.favicon = "/static/favicon.ico"
|
||||||
|
|
||||||
|
site:banner(fes.app.header.render(std))
|
||||||
|
|
||||||
|
site:note(u.cc {
|
||||||
|
std.h2("Hello, World!"),
|
||||||
|
std.p("This is an example of the best practices/canonical Fes site.")
|
||||||
|
})
|
||||||
|
|
||||||
|
site:note(fes.app.footer.render(std))
|
||||||
|
|
||||||
|
return site
|
||||||
5
examples/default/Fes.toml
Normal file
5
examples/default/Fes.toml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
[app]
|
||||||
|
|
||||||
|
name = "default"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["vx-clutch"]
|
||||||
33
examples/default/README.md
Normal file
33
examples/default/README.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# default
|
||||||
|
|
||||||
|
```
|
||||||
|
fes new default
|
||||||
|
```
|
||||||
|
|
||||||
|
> **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).
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
local fes = require("fes")
|
local fes = require("fes")
|
||||||
local std = fes.std
|
|
||||||
|
|
||||||
|
|
||||||
local site = fes.fes()
|
local site = fes.fes()
|
||||||
|
|
||||||
site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "fSD")
|
site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "fSD")
|
||||||
|
|
||||||
site:h1("Hello, World!")
|
site:h1("Hello, World!")
|
||||||
|
|
||||||
site:note(
|
|
||||||
fes.app.foo.render()
|
|
||||||
)
|
|
||||||
|
|
||||||
return site
|
return site
|
||||||
6
examples/error/README.md
Normal file
6
examples/error/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# error
|
||||||
|
|
||||||
|
This shows what a Lua error looks like to the user. Lua errors are the most
|
||||||
|
common and the most critical so that is why they are shown to the user. Other,
|
||||||
|
lesser errors, are only shown to the developer because of their different
|
||||||
|
nature.
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
local fes = require("fes")
|
local fes = require("fes")
|
||||||
local site = fes.fes()
|
local site = fes.fes()
|
||||||
|
|
||||||
|
site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "fSD")
|
||||||
|
|
||||||
This is what an error looks like
|
This is what an error looks like
|
||||||
|
|
||||||
|
site:h1("Hello, World!")
|
||||||
|
|
||||||
return site
|
return site
|
||||||
|
|||||||
4
examples/hello/README.md
Normal file
4
examples/hello/README.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# hello
|
||||||
|
|
||||||
|
This is a very simple hello world program, the only difference between this and
|
||||||
|
default is this README.
|
||||||
3
examples/markdown/README.md
Normal file
3
examples/markdown/README.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# markdown
|
||||||
|
|
||||||
|
This example demonstrate Fes's ability to handle markdown routes.
|
||||||
@@ -1 +1,3 @@
|
|||||||
# Hello, World!
|
# Markdown!
|
||||||
|
|
||||||
|
**Fes** also supports markdown routes!
|
||||||
|
|||||||
5
examples/simple/README.md
Normal file
5
examples/simple/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# simple
|
||||||
|
|
||||||
|
This simple example shows the extensibility of the Fes framework. It shows the
|
||||||
|
you do not necessarily need to use the site object (although it is recommended)
|
||||||
|
you can define your own site, similar to how Lisps do things.
|
||||||
14
go.mod
14
go.mod
@@ -3,13 +3,15 @@ module fes
|
|||||||
go 1.25.4
|
go 1.25.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/color v1.18.0 // indirect
|
github.com/fatih/color v1.18.0
|
||||||
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a // indirect
|
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a
|
||||||
github.com/gomarkdown/mdtohtml v0.0.0-20240124153210-d773061d1585 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
|
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c
|
||||||
|
github.com/yuin/gopher-lua v1.1.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
|
||||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
|
||||||
github.com/yuin/gopher-lua v1.1.1 // indirect
|
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sys v0.25.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,9 +1,7 @@
|
|||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
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/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
|
||||||
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=
|
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a h1:l7A0loSszR5zHd/qK53ZIHMO8b3bBSmENnQ6eKnUT0A=
|
||||||
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
github.com/gomarkdown/markdown v0.0.0-20250810172220-2e2c11897d1a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=
|
||||||
github.com/gomarkdown/mdtohtml v0.0.0-20240124153210-d773061d1585/go.mod h1:6grYm5/uY15CwgBBqwA3+o/cAzaxssckznJ0B35ouBY=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
|||||||
67
index.html
67
index.html
@@ -183,6 +183,7 @@ footer {
|
|||||||
<li><a href="#introduction">Introduction</a></li>
|
<li><a href="#introduction">Introduction</a></li>
|
||||||
<li><a href="#installation">Installation</a></li>
|
<li><a href="#installation">Installation</a></li>
|
||||||
<li><a href="#usage">Usage</a></li>
|
<li><a href="#usage">Usage</a></li>
|
||||||
|
<li><a href="#quick">Quick Start</a></li>
|
||||||
<li><a href="#cli-reference">Cli Reference</a></li>
|
<li><a href="#cli-reference">Cli Reference</a></li>
|
||||||
<li><a href="#reference">Reference</a></li>
|
<li><a href="#reference">Reference</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -210,6 +211,34 @@ footer {
|
|||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section id="quick">
|
||||||
|
<h2>Quick Start</h2>
|
||||||
|
<pre><code>fes new hello</code></pre>
|
||||||
|
<p>This creates a new project under the name <code>hello</code>.</p>
|
||||||
|
<pre><code>fes run hello</code></pre>
|
||||||
|
<p>This runs your project <code>hello</code>, by default at <a href="localhost:3000" target="_blank">localhost:3000</a>.</p>
|
||||||
|
<h3>Extensions</h3>
|
||||||
|
<p>Let's add a paragraph to this simple site. Right now you have the following page:</p>
|
||||||
|
<pre><code>local fes = require("fes")
|
||||||
|
local site = fes.fes()
|
||||||
|
|
||||||
|
-- site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "vx-clutch")
|
||||||
|
|
||||||
|
site:h1("Hello, World!")
|
||||||
|
|
||||||
|
return site</code></pre>
|
||||||
|
<p>To add a simple paragraph modify like so:</p>
|
||||||
|
<pre><code>local fes = require("fes")
|
||||||
|
local site = fes.fes()
|
||||||
|
|
||||||
|
-- site.copyright = fes.util.copyright("https://fsd.vxserver.dev", "vx-clutch")
|
||||||
|
|
||||||
|
site:h1("Hello, World!")
|
||||||
|
site:p("This is a paragraph")
|
||||||
|
|
||||||
|
return site</code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section id="cli-reference">
|
<section id="cli-reference">
|
||||||
<h2>Cli Reference</h2>
|
<h2>Cli Reference</h2>
|
||||||
<table> <thead>
|
<table> <thead>
|
||||||
@@ -219,18 +248,6 @@ footer {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
|
||||||
<td><code>--help</code></td>
|
|
||||||
<td>Display help information</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>--no-color</code></td>
|
|
||||||
<td>Disable color output</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><code>-p <port></code></td>
|
|
||||||
<td>Set the server port</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><code>new <project></code></td>
|
<td><code>new <project></code></td>
|
||||||
<td>Create a new projet called <project></td>
|
<td>Create a new projet called <project></td>
|
||||||
@@ -243,6 +260,30 @@ footer {
|
|||||||
<td><code>run <project></code></td>
|
<td><code>run <project></code></td>
|
||||||
<td>Run the projet called <project></td>
|
<td>Run the projet called <project></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>-help</code></td>
|
||||||
|
<td>Display help information.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>-V1</code></td>
|
||||||
|
<td>Print extended version information, this is very helpful when it comes to bug reporting.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>-no-color</code></td>
|
||||||
|
<td>Disable color output.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>-p <port></code></td>
|
||||||
|
<td>Set the server port.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>-static</code></td>
|
||||||
|
<td>Render and save all pages. (this feature is yet to be implemented)</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><code>-version</code></td>
|
||||||
|
<td>Print the version.</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
@@ -586,7 +627,7 @@ return hello</pre></code> This can be called from another with,
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>Last updated: 2025-12-16</p>
|
<p>Last updated: 2025-12-27</p>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
1258
lib/dkjson.lua
1258
lib/dkjson.lua
File diff suppressed because it is too large
Load Diff
79
lib/fes.lua
79
lib/fes.lua
@@ -4,25 +4,26 @@ local M = {}
|
|||||||
M.__index = M
|
M.__index = M
|
||||||
|
|
||||||
function M.fes(header, footer)
|
function M.fes(header, footer)
|
||||||
local config = {} local site_config = {}
|
local config = {}
|
||||||
local fes_mod = package.loaded.fes
|
local site_config = {}
|
||||||
if fes_mod and fes_mod.config then
|
local fes_mod = package.loaded.fes
|
||||||
config = fes_mod.config
|
if fes_mod and fes_mod.config then
|
||||||
if config.site then
|
config = fes_mod.config
|
||||||
site_config = config.site
|
if config.site then
|
||||||
end
|
site_config = config.site
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if site_config.favicon then
|
if site_config.favicon then
|
||||||
site_config.favicon = '<link rel="icon" type="image/x-icon" href="' .. site_config.favicon .. '">'
|
site_config.favicon = '<link rel="icon" type="image/x-icon" href="' .. site_config.favicon .. '">'
|
||||||
end
|
end
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
version = site_config.version,
|
version = site_config.version,
|
||||||
title = site_config.title,
|
title = site_config.title,
|
||||||
copyright = site_config.copyright,
|
copyright = site_config.copyright,
|
||||||
favicon = site_config.favicon,
|
favicon = site_config.favicon,
|
||||||
header = header or [[
|
header = header or [[
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -300,7 +301,7 @@ em, i { font-style: italic; }
|
|||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
]],
|
]],
|
||||||
footer = footer or [[
|
footer = footer or [[
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<a href="https://git.vxserver.dev/fSD/fes" target="_blank">Fes Powered</a>
|
<a href="https://git.vxserver.dev/fSD/fes" target="_blank">Fes Powered</a>
|
||||||
<a href="https://www.lua.org/" target="_blank">Lua Powered</a>
|
<a href="https://www.lua.org/" target="_blank">Lua Powered</a>
|
||||||
@@ -311,38 +312,42 @@ em, i { font-style: italic; }
|
|||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
]],
|
]],
|
||||||
parts = {}
|
parts = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
return setmetatable(self, M)
|
return setmetatable(self, M)
|
||||||
end
|
end
|
||||||
|
|
||||||
function M:custom(str)
|
function M:custom(str)
|
||||||
table.insert(self.parts, str)
|
table.insert(self.parts, str)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
for name, func in pairs(std) do
|
for name, func in pairs(std) do
|
||||||
if type(func) == "function" then
|
if type(func) == "function" then
|
||||||
M[name] = function(self, ...)
|
M[name] = function(self, ...)
|
||||||
local result = func(...)
|
local result = func(...)
|
||||||
table.insert(self.parts, result)
|
table.insert(self.parts, result)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function M:build()
|
function M:build()
|
||||||
local header = self.header
|
local header = self.header
|
||||||
header = header:gsub("{{TITLE}}", self.title or "Document")
|
header = header:gsub("{{TITLE}}", self.title or "Document")
|
||||||
local favicon_html = self.favicon and ('<link rel="icon" type="image/x-icon" href="' .. self.favicon .. '">')
|
local favicon_html = self.favicon and ('<link rel="icon" type="image/x-icon" href="' .. self.favicon .. '">')
|
||||||
header = header:gsub("{{FAVICON}}", favicon_html or [[<link rel="icon" href="data:image/svg+xml,<svg xmlns=%%22http://www.w3.org/2000/svg%%22 viewBox=%%220 0 100 100%%22><text y=%%22.9em%%22 font-size=%%2290%%22>🔥</text></svg>">]])
|
header = header:gsub(
|
||||||
local footer = self.footer:gsub("{{COPYRIGHT}}", self.copyright or "© The Copyright Holder")
|
"{{FAVICON}}",
|
||||||
return header .. table.concat(self.parts, "\n") .. footer
|
favicon_html
|
||||||
|
or [[<link rel="icon" href="data:image/svg+xml,<svg xmlns=%%22http://www.w3.org/2000/svg%%22 viewBox=%%220 0 100 100%%22><text y=%%22.9em%%22 font-size=%%2290%%22>🔥</text></svg>">]]
|
||||||
|
)
|
||||||
|
local footer = self.footer:gsub("{{COPYRIGHT}}", self.copyright or "© The Copyright Holder")
|
||||||
|
return header .. table.concat(self.parts, "\n") .. footer
|
||||||
end
|
end
|
||||||
|
|
||||||
M.__tostring = function(self)
|
M.__tostring = function(self)
|
||||||
return self:build()
|
return self:build()
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
15
lib/std.lua
15
lib/std.lua
@@ -19,29 +19,29 @@ end
|
|||||||
function M.a(link, str)
|
function M.a(link, str)
|
||||||
link = link or "https://example.com"
|
link = link or "https://example.com"
|
||||||
str = str or link
|
str = str or link
|
||||||
return "<a href=\"" .. link .. "\">" .. str .. "</a>"
|
return '<a href="' .. link .. '">' .. str .. "</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.ha(link, str)
|
function M.ha(link, str)
|
||||||
link = link or "https://example.com"
|
link = link or "https://example.com"
|
||||||
str = str or link
|
str = str or link
|
||||||
return "<a class=\"hidden\" href=\"" .. link .. "\">" .. str .. "</a>"
|
return '<a class="hidden" href="' .. link .. '">' .. str .. "</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.external(link, str)
|
function M.external(link, str)
|
||||||
return "<a target=\"_blank\" href=\"" .. link .. "\">" .. str .. "</a>"
|
return '<a target="_blank" href="' .. link .. '">' .. str .. "</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.note(str)
|
function M.note(str)
|
||||||
return '<div class="note">' .. str .. '</div>'
|
return '<div class="note">' .. str .. "</div>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.muted(str)
|
function M.muted(str)
|
||||||
return '<div class="muted">' .. str .. '</div>'
|
return '<div class="muted">' .. str .. "</div>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.callout(str)
|
function M.callout(str)
|
||||||
return '<div class="callout">' .. str .. '</div>'
|
return '<div class="callout">' .. str .. "</div>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.h1(str)
|
function M.h1(str)
|
||||||
@@ -55,7 +55,8 @@ end
|
|||||||
function M.h3(str)
|
function M.h3(str)
|
||||||
return "<h3>" .. (str or "") .. "</h3>"
|
return "<h3>" .. (str or "") .. "</h3>"
|
||||||
end
|
end
|
||||||
function M.h4(str) return "<h4>" .. (str or "") .. "</h4>"
|
function M.h4(str)
|
||||||
|
return "<h4>" .. (str or "") .. "</h4>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.h5(str)
|
function M.h5(str)
|
||||||
|
|||||||
15
main.go
15
main.go
@@ -25,7 +25,8 @@ var documentation string
|
|||||||
func init() {
|
func init() {
|
||||||
config.Port = flag.Int("p", 3000, "Set the server port")
|
config.Port = flag.Int("p", 3000, "Set the server port")
|
||||||
config.Color = flag.Bool("no-color", false, "Disable color output")
|
config.Color = flag.Bool("no-color", false, "Disable color output")
|
||||||
config.Static = flag.Bool("static", false, "Render and save all pages.")
|
config.Static = flag.Bool("static", false, "Render and save all pages")
|
||||||
|
config.Docker = flag.Bool("docker", false, "Create a docker project")
|
||||||
config.Lib = lib
|
config.Lib = lib
|
||||||
config.Doc = documentation
|
config.Doc = documentation
|
||||||
}
|
}
|
||||||
@@ -33,13 +34,13 @@ func init() {
|
|||||||
func main() {
|
func main() {
|
||||||
flag.Usage = func() {
|
flag.Usage = func() {
|
||||||
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] <command> <project_dir>\n", os.Args[0])
|
fmt.Fprintf(flag.CommandLine.Output(), "Usage: %s [options] <command> <project_dir>\n", os.Args[0])
|
||||||
fmt.Println("Commands:")
|
fmt.Fprintf(os.Stderr, "Commands:")
|
||||||
fmt.Println(" new <project_dir> Create a new project")
|
fmt.Fprintf(os.Stderr, " new <project_dir> Create a new project")
|
||||||
fmt.Println(" doc Open documentation")
|
fmt.Fprintf(os.Stderr, " doc Open documentation")
|
||||||
fmt.Println(" run <project_dir> Start the server")
|
fmt.Fprintf(os.Stderr, " run <project_dir> Start the server")
|
||||||
fmt.Println("Options:")
|
fmt.Fprintf(os.Stderr, "Options:")
|
||||||
flag.PrintDefaults()
|
flag.PrintDefaults()
|
||||||
fmt.Println("For bug reports, contact a developer and describe the issue. Provide the output of the `-V1` flag.")
|
fmt.Fprintf(os.Stderr, "For bug reports, contact a developer and describe the issue. Provide the output of the `-V1` flag.")
|
||||||
}
|
}
|
||||||
|
|
||||||
showVersion := flag.Bool("version", false, "Show version and exit")
|
showVersion := flag.Bool("version", false, "Show version and exit")
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ var Doc string
|
|||||||
var Port *int
|
var Port *int
|
||||||
var Color *bool
|
var Color *bool
|
||||||
var Static *bool
|
var Static *bool
|
||||||
|
var Docker *bool
|
||||||
|
|
||||||
type AppConfig struct {
|
type AppConfig struct {
|
||||||
App struct {
|
App struct {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/pkg/browser"
|
"github.com/pkg/browser"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/* open documentation in browser */
|
||||||
func Open() error {
|
func Open() error {
|
||||||
fmt.Println("Opening documentation in browser")
|
fmt.Println("Opening documentation in browser")
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package new
|
package new
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fes/modules/config"
|
||||||
|
"fes/modules/ui"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -26,7 +28,7 @@ func getName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* helper function for writing files */
|
/* helper function for writing files */
|
||||||
func write(path string, format string, args ...interface{}) error {
|
func write(path string, format string, args ...any) error {
|
||||||
dir := filepath.Dir(path)
|
dir := filepath.Dir(path)
|
||||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -49,6 +51,22 @@ func Project(dir string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if *config.Docker {
|
||||||
|
write("docker-compose.yml", `services:
|
||||||
|
%s:
|
||||||
|
image: git.vxserver.dev/fsd/fes:latest
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- ./app:/app`, dir)
|
||||||
|
if err := os.Mkdir("app", 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.Chdir("app"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
name := getName()
|
name := getName()
|
||||||
|
|
||||||
write("www/index.lua", `local fes = require("fes")
|
write("www/index.lua", `local fes = require("fes")
|
||||||
@@ -64,5 +82,48 @@ return site`, name)
|
|||||||
name = "%s"
|
name = "%s"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
authors = ["%s"]`, dir, name)
|
authors = ["%s"]`, dir, name)
|
||||||
|
write("README.md", strings.ReplaceAll(`# %s
|
||||||
|
|
||||||
|
$$$$$$
|
||||||
|
fes new %s
|
||||||
|
$$$$$$
|
||||||
|
|
||||||
|
> **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).`, "$$", "`"), dir, dir)
|
||||||
|
|
||||||
|
ui.Hint("you can run this with `fes run %s`", dir)
|
||||||
|
|
||||||
|
fmt.Println("Created new Fes project at", func() string {
|
||||||
|
if res, err := filepath.Abs(dir); err == nil {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
return dir
|
||||||
|
}())
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,21 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/* this is the request data we pass over the bus to the application, via the fes.bus interface */
|
||||||
type reqData struct {
|
type reqData struct {
|
||||||
path string
|
path string
|
||||||
params map[string]string
|
params map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* performs relavent handling based on the directory passaed
|
||||||
|
*
|
||||||
|
* Special directories
|
||||||
|
* - www/ <= contains lua routes.
|
||||||
|
* - static/ <= static content accessable at /static/path or /static/dir/path.
|
||||||
|
* - include/ <= globally accessable lua functions, cannot directly access "fes" right now.
|
||||||
|
* - archive/ <= contains user facing files such as archives or dists.
|
||||||
|
*
|
||||||
|
*/
|
||||||
func handleDir(entries []os.DirEntry, dir string, routes map[string]string, base string, isStatic bool) error {
|
func handleDir(entries []os.DirEntry, dir string, routes map[string]string, base string, isStatic bool) error {
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
path := filepath.Join(dir, entry.Name())
|
path := filepath.Join(dir, entry.Name())
|
||||||
@@ -59,6 +69,7 @@ func handleDir(entries []os.DirEntry, dir string, routes map[string]string, base
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(vx-clutch): this should not be a function
|
||||||
func loadIncludeModules(L *lua.LState, includeDir string) *lua.LTable {
|
func loadIncludeModules(L *lua.LState, includeDir string) *lua.LTable {
|
||||||
app := L.NewTable()
|
app := L.NewTable()
|
||||||
ents, err := os.ReadDir(includeDir)
|
ents, err := os.ReadDir(includeDir)
|
||||||
@@ -94,7 +105,8 @@ func loadIncludeModules(L *lua.LState, includeDir string) *lua.LTable {
|
|||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadLua(entry string, cfg *config.AppConfig, requestData reqData) ([]byte, error) {
|
/* renders the given lua route */
|
||||||
|
func renderRoute(entry string, cfg *config.AppConfig, requestData reqData) ([]byte, error) {
|
||||||
L := lua.NewState()
|
L := lua.NewState()
|
||||||
defer L.Close()
|
defer L.Close()
|
||||||
|
|
||||||
@@ -219,6 +231,7 @@ func loadLua(entry string, cfg *config.AppConfig, requestData reqData) ([]byte,
|
|||||||
return []byte(""), nil
|
return []byte(""), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* this indexes and generate the page for viewing the archive directory */
|
||||||
func generateArchiveIndex(fsPath string, urlPath string) (string, error) {
|
func generateArchiveIndex(fsPath string, urlPath string) (string, error) {
|
||||||
info, err := os.Stat(fsPath)
|
info, err := os.Stat(fsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -309,6 +322,9 @@ func generateArchiveIndex(fsPath string, urlPath string) (string, error) {
|
|||||||
return b.String(), nil
|
return b.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* generates the data for the not found page. Checks for user-defined source in this order
|
||||||
|
* 404.lua => 404.md => 404.html => default.
|
||||||
|
*/
|
||||||
func generateNotFoundData(cfg *config.AppConfig) []byte {
|
func generateNotFoundData(cfg *config.AppConfig) []byte {
|
||||||
notFoundData := []byte(`
|
notFoundData := []byte(`
|
||||||
<html>
|
<html>
|
||||||
@@ -320,9 +336,13 @@ func generateNotFoundData(cfg *config.AppConfig) []byte {
|
|||||||
</html>
|
</html>
|
||||||
`)
|
`)
|
||||||
if _, err := os.Stat(filepath.Join("www", "404.lua")); err == nil {
|
if _, err := os.Stat(filepath.Join("www", "404.lua")); err == nil {
|
||||||
if nf, err := loadLua("www/404.lua", cfg, reqData{}); err == nil {
|
if nf, err := renderRoute("www/404.lua", cfg, reqData{}); err == nil {
|
||||||
notFoundData = nf
|
notFoundData = nf
|
||||||
}
|
}
|
||||||
|
} else if _, err := os.Stat("www/404.md"); err == nil {
|
||||||
|
if buf, err := os.ReadFile("www/404.html"); err == nil {
|
||||||
|
notFoundData = []byte(markdownToHTML(string(buf)))
|
||||||
|
}
|
||||||
} else if _, err := os.Stat("www/404.html"); err == nil {
|
} else if _, err := os.Stat("www/404.html"); err == nil {
|
||||||
if buf, err := os.ReadFile("www/404.html"); err == nil {
|
if buf, err := os.ReadFile("www/404.html"); err == nil {
|
||||||
notFoundData = buf
|
notFoundData = buf
|
||||||
@@ -331,6 +351,7 @@ func generateNotFoundData(cfg *config.AppConfig) []byte {
|
|||||||
return notFoundData
|
return notFoundData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* helper to load all special directories */
|
||||||
func loadDirs() map[string]string {
|
func loadDirs() map[string]string {
|
||||||
routes := make(map[string]string)
|
routes := make(map[string]string)
|
||||||
|
|
||||||
@@ -355,6 +376,7 @@ func loadDirs() map[string]string {
|
|||||||
return routes
|
return routes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* helper to parse the Fes.toml and generate config */
|
||||||
func parseConfig() config.AppConfig {
|
func parseConfig() config.AppConfig {
|
||||||
tomlDocument, err := os.ReadFile("Fes.toml")
|
tomlDocument, err := os.ReadFile("Fes.toml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -372,15 +394,21 @@ func parseConfig() config.AppConfig {
|
|||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func readArchive(w http.ResponseWriter, route string) {
|
/* helper to read the archive files */
|
||||||
|
func readArchive(w http.ResponseWriter, route string) error {
|
||||||
fsPath := "." + route
|
fsPath := "." + route
|
||||||
if info, err := os.Stat(fsPath); err == nil && info.IsDir() {
|
if info, err := os.Stat(fsPath); err == nil && info.IsDir() {
|
||||||
if page, err := generateArchiveIndex(fsPath, route); err == nil {
|
if page, err := generateArchiveIndex(fsPath, route); err == nil {
|
||||||
w.Write([]byte(page))
|
w.Write([]byte(page))
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* start the Fes server */
|
||||||
func Start(dir string) error {
|
func Start(dir string) error {
|
||||||
if err := os.Chdir(dir); err != nil {
|
if err := os.Chdir(dir); err != nil {
|
||||||
return ui.Error(fmt.Sprintf("failed to change directory to %s", dir), err)
|
return ui.Error(fmt.Sprintf("failed to change directory to %s", dir), err)
|
||||||
@@ -405,7 +433,7 @@ func Start(dir string) error {
|
|||||||
route = r.URL.Path
|
route = r.URL.Path
|
||||||
|
|
||||||
if strings.HasPrefix(route, "/archive") {
|
if strings.HasPrefix(route, "/archive") {
|
||||||
readArchive(w, route)
|
err = readArchive(w, route)
|
||||||
} else {
|
} else {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
w.Write([]byte(notFoundData))
|
w.Write([]byte(notFoundData))
|
||||||
@@ -422,10 +450,11 @@ func Start(dir string) error {
|
|||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
if strings.HasSuffix(route, ".lua") {
|
if strings.HasSuffix(route, ".lua") {
|
||||||
data, err = loadLua(route, &cfg, reqData{path: r.URL.Path, params: params})
|
data, err = renderRoute(route, &cfg, reqData{path: r.URL.Path, params: params})
|
||||||
} else if strings.HasSuffix(route, ".md") {
|
} else if strings.HasSuffix(route, ".md") {
|
||||||
data, err = os.ReadFile(route)
|
data, err = os.ReadFile(route)
|
||||||
data = []byte(markdownToHTML(string(data)))
|
data = []byte(markdownToHTML(string(data)))
|
||||||
|
data = []byte("<style>body {max-width: 80ch;}</style>\n" + string(data))
|
||||||
} else {
|
} else {
|
||||||
data, err = os.ReadFile(route)
|
data, err = os.ReadFile(route)
|
||||||
}
|
}
|
||||||
@@ -438,5 +467,5 @@ func Start(dir string) error {
|
|||||||
})
|
})
|
||||||
|
|
||||||
fmt.Printf("Server is running on http://localhost:%d\n", *config.Port)
|
fmt.Printf("Server is running on http://localhost:%d\n", *config.Port)
|
||||||
return http.ListenAndServe(fmt.Sprintf(":%d", *config.Port), nil)
|
return http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", *config.Port), nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ import (
|
|||||||
"github.com/fatih/color"
|
"github.com/fatih/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
hint_color = 0xbda02a
|
||||||
|
)
|
||||||
|
|
||||||
|
/* print out the current path (route) and relevant error */
|
||||||
func Path(path string, err error) {
|
func Path(path string, err error) {
|
||||||
path = strings.TrimPrefix(path, "/")
|
path = strings.TrimPrefix(path, "/")
|
||||||
|
|
||||||
@@ -29,29 +34,45 @@ func Path(path string, err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print general system warning */
|
||||||
func Warning(msg string, err error) error {
|
func Warning(msg string, err error) error {
|
||||||
fmt.Printf("%s: %s: %v\n", version.PROGRAM_NAME, color.MagentaString("warning"), err)
|
fmt.Printf("%s: %s: %v\n", version.PROGRAM_NAME, color.MagentaString("warning"), err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print general system error */
|
||||||
func Error(msg string, err error) error {
|
func Error(msg string, err error) error {
|
||||||
fmt.Printf("%s: %s: %v\n", version.PROGRAM_NAME, color.RedString("error"), err)
|
fmt.Printf("%s: %s: %v\n", version.PROGRAM_NAME, color.RedString("error"), err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print fatality and panic */
|
||||||
func Fatal(msg string, err error) error {
|
func Fatal(msg string, err error) error {
|
||||||
fmt.Printf("%s: %s: %v\n", version.PROGRAM_NAME, color.RedString("fatal"), err)
|
fmt.Printf("%s: %s: %v\n", version.PROGRAM_NAME, color.RedString("fatal"), err)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print a useful hint to the user */
|
||||||
|
func Hint(format string, args ...any) {
|
||||||
|
color.RGB(func(hex int) (r, g, b int) {
|
||||||
|
r = (hex >> 16) & 0xFF
|
||||||
|
g = (hex >> 8) & 0xFF
|
||||||
|
b = hex & 0xFF
|
||||||
|
return
|
||||||
|
}(hint_color)).Printf("hint: %s\n", fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print message using the ok status color */
|
||||||
func OK(msg string) {
|
func OK(msg string) {
|
||||||
color.Green(msg)
|
color.Green(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print message using the warning status color */
|
||||||
func WARN(msg string) {
|
func WARN(msg string) {
|
||||||
color.Magenta(msg)
|
color.Magenta(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* print message using the error status color */
|
||||||
func ERROR(msg string) {
|
func ERROR(msg string) {
|
||||||
color.Red(msg)
|
color.Red(msg)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user