-
-
Save kmafeni04/9f18504821fe7743eebb1b274529c631 to your computer and use it in GitHub Desktop.
#!/bin/bash | |
# NOTE: rewritten in lua, https://gist.github.com/kmafeni04/7c5a40443a9f5fd9d31e73a4bc7c47b0 | |
echo "What would you like to name the project?:" | |
read -r project_name | |
mkdir "$project_name" | |
cd "$project_name" || return | |
luarocks install lapis --lua-version=5.1 | |
luarocks install etlua --lua-version=5.1 | |
lapis new --rockspec | |
mkdir -p static/css static/assets static/js | |
mkdir -p views/pages | |
gitignore='logs/ | |
nginx.conf.compiled | |
*.sqlite | |
*_temp/ | |
' | |
echo "$gitignore" > .gitignore | |
dockerfile='FROM openresty/openresty:jammy | |
WORKDIR /app | |
RUN apt update | |
RUN apt install -y sqlite3 | |
RUN apt install -y libssl-dev | |
RUN apt install -y libsqlite3-dev | |
RUN luarocks install luasec | |
RUN luarocks install lapis | |
RUN luarocks install etlua | |
RUN luarocks install lsqlite3 | |
RUN luarocks install tableshape | |
COPY . . | |
RUN lapis migrate production | |
RUN chmod a+rw /app /app/* | |
EXPOSE 8080 | |
CMD ["lapis", "server", "production"] | |
' | |
echo "$dockerfile" > Dockerfile | |
index_etlua='<main> | |
<h1> | |
Welcome to | |
<a href="https://leafo.net/lapis/" target="_blank">Lapis <%= require("lapis.version")%></a> | |
</h1> | |
<p>Edit the index.etlua file in views/pages to begin</p> | |
</main> | |
' | |
echo "$index_etlua" > views/pages/index.etlua | |
reset_css=':root { | |
--font-color-light: black; | |
--font-color-dark: white; | |
} | |
@media (prefers-color-scheme: light) { | |
:root { | |
--font-color: var(--font-color-light); | |
} | |
} | |
@media (prefers-color-scheme: dark) { | |
:root { | |
--font-color: var(--font-color-dark); | |
} | |
} | |
* { | |
font-family: | |
system-ui, | |
-apple-system, | |
BlinkMacSystemFont, | |
"Segoe UI", | |
Roboto, | |
Oxygen, | |
Ubuntu, | |
Cantarell, | |
"Open Sans", | |
"Helvetica Neue", | |
sans-serif; | |
padding: 0; | |
margin: 0; | |
color: inherit; | |
box-sizing: inherit; | |
} | |
*, | |
*::after, | |
*::before { | |
box-sizing: inherit; | |
} | |
html { | |
color-scheme: dark light; | |
box-sizing: border-box; | |
} | |
@media (prefers-reduced-motion: no-preference) { | |
html { | |
scroll-padding-top: 4rem; | |
scroll-behavior: smooth; | |
} | |
} | |
body { | |
color: var(--font-color); | |
min-height: 100svh; | |
} | |
img, | |
video, | |
svg, | |
picture { | |
display: block; | |
width: 100%; | |
} | |
input, | |
textarea, | |
button, | |
select { | |
font: inherit; | |
} | |
button { | |
cursor: pointer; | |
} | |
' | |
echo "$reset_css" > static/css/reset.css | |
live_js='/* | |
Live.js - One script closer to Designing in the Browser | |
Written for Handcraft.com by Martin Kool (@mrtnkl). | |
Version 4. | |
Recent change: Made stylesheet and mimetype checks case insensitive. | |
http://livejs.com | |
http://livejs.com/license (MIT) | |
@livejs | |
Include live.js#css to monitor css changes only. | |
Include live.js#js to monitor js changes only. | |
Include live.js#html to monitor html changes only. | |
Mix and match to monitor a preferred combination such as live.js#html,css | |
By default, just include live.js to monitor all css, js and html changes. | |
Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then, | |
as a page reload due to a change in html or css would not re-include the bookmarklet. | |
To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify | |
*/ | |
(function () { | |
var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 }, | |
resources = {}, | |
pendingRequests = {}, | |
currentLinkElements = {}, | |
oldLinkElements = {}, | |
interval = 1000, | |
loaded = false, | |
active = { "html": 1, "css": 1, "js": 1 }; | |
var Live = { | |
// performs a cycle per interval | |
heartbeat: function () { | |
if (document.body) { | |
// make sure all resources are loaded on first activation | |
if (!loaded) Live.loadresources(); | |
Live.checkForChanges(); | |
} | |
setTimeout(Live.heartbeat, interval); | |
}, | |
// loads all local css and js resources upon first activation | |
loadresources: function () { | |
// helper method to assert if a given url is local | |
function isLocal(url) { | |
var loc = document.location, | |
reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host); | |
return url.match(reg); | |
} | |
// gather all resources | |
var scripts = document.getElementsByTagName("script"), | |
links = document.getElementsByTagName("link"), | |
uris = []; | |
// track local js urls | |
for (var i = 0; i < scripts.length; i++) { | |
var script = scripts[i], src = script.getAttribute("src"); | |
if (src && isLocal(src)) | |
uris.push(src); | |
if (src && src.match(/\blive.js#/)) { | |
for (var type in active) | |
active[type] = src.match("[#,|]" + type) != null | |
if (src.match("notify")) | |
alert("Live.js is loaded."); | |
} | |
} | |
if (!active.js) uris = []; | |
if (active.html) uris.push(document.location.href); | |
// track local css urls | |
for (var i = 0; i < links.length && active.css; i++) { | |
var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2); | |
if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) { | |
uris.push(href); | |
currentLinkElements[href] = link; | |
} | |
} | |
// initialize the resources info | |
for (var i = 0; i < uris.length; i++) { | |
var url = uris[i]; | |
Live.getHead(url, function (url, info) { | |
resources[url] = info; | |
}); | |
} | |
// add rule for morphing between old and new css files | |
var head = document.getElementsByTagName("head")[0], | |
style = document.createElement("style"), | |
rule = "transition: all .3s ease-out;" | |
css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join(''); | |
style.setAttribute("type", "text/css"); | |
head.appendChild(style); | |
style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css)); | |
// yep | |
loaded = true; | |
}, | |
// check all tracking resources for changes | |
checkForChanges: function () { | |
for (var url in resources) { | |
if (pendingRequests[url]) | |
continue; | |
Live.getHead(url, function (url, newInfo) { | |
var oldInfo = resources[url], | |
hasChanged = false; | |
resources[url] = newInfo; | |
for (var header in oldInfo) { | |
// do verification based on the header type | |
var oldValue = oldInfo[header], | |
newValue = newInfo[header], | |
contentType = newInfo["Content-Type"]; | |
switch (header.toLowerCase()) { | |
case "etag": | |
if (!newValue) break; | |
// fall through to default | |
default: | |
hasChanged = oldValue != newValue; | |
break; | |
} | |
// if changed, act | |
if (hasChanged) { | |
Live.refreshResource(url, contentType); | |
break; | |
} | |
} | |
}); | |
} | |
}, | |
// act upon a changed url of certain content type | |
refreshResource: function (url, type) { | |
switch (type.toLowerCase()) { | |
// css files can be reloaded dynamically by replacing the link element | |
case "text/css": | |
var link = currentLinkElements[url], | |
html = document.body.parentNode, | |
head = link.parentNode, | |
next = link.nextSibling, | |
newLink = document.createElement("link"); | |
html.className = html.className.replace(/\s*livejs\-loading/gi, '') + " livejs-loading"; | |
newLink.setAttribute("type", "text/css"); | |
newLink.setAttribute("rel", "stylesheet"); | |
newLink.setAttribute("href", url + "?now=" + new Date() * 1); | |
next ? head.insertBefore(newLink, next) : head.appendChild(newLink); | |
currentLinkElements[url] = newLink; | |
oldLinkElements[url] = link; | |
// schedule removal of the old link | |
Live.removeoldLinkElements(); | |
break; | |
// check if an html resource is our current url, then reload | |
case "text/html": | |
if (url != document.location.href) | |
return; | |
// local javascript changes cause a reload as well | |
case "text/javascript": | |
case "application/javascript": | |
case "application/x-javascript": | |
document.location.reload(); | |
} | |
}, | |
// removes the old stylesheet rules only once the new one has finished loading | |
removeoldLinkElements: function () { | |
var pending = 0; | |
for (var url in oldLinkElements) { | |
// if this sheet has any cssRules, delete the old link | |
try { | |
var link = currentLinkElements[url], | |
oldLink = oldLinkElements[url], | |
html = document.body.parentNode, | |
sheet = link.sheet || link.styleSheet, | |
rules = sheet.rules || sheet.cssRules; | |
if (rules.length >= 0) { | |
oldLink.parentNode.removeChild(oldLink); | |
delete oldLinkElements[url]; | |
setTimeout(function () { | |
html.className = html.className.replace(/\s*livejs\-loading/gi, ''); | |
}, 100); | |
} | |
} catch (e) { | |
pending++; | |
} | |
if (pending) setTimeout(Live.removeoldLinkElements, 50); | |
} | |
}, | |
// performs a HEAD request and passes the header info to the given callback | |
getHead: function (url, callback) { | |
pendingRequests[url] = true; | |
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp"); | |
xhr.open("HEAD", url, true); | |
xhr.onreadystatechange = function () { | |
delete pendingRequests[url]; | |
if (xhr.readyState == 4 && xhr.status != 304) { | |
xhr.getAllResponseHeaders(); | |
var info = {}; | |
for (var h in headers) { | |
var value = xhr.getResponseHeader(h); | |
// adjust the simple Etag variant to match on its significant part | |
if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, ''); | |
if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1"); | |
info[h] = value; | |
} | |
callback(url, info); | |
} | |
} | |
xhr.send(); | |
} | |
}; | |
// start listening | |
if (document.location.protocol != "file:") { | |
if (!window.liveJsLoaded) | |
Live.heartbeat(); | |
window.liveJsLoaded = true; | |
} | |
else if (window.console) | |
console.log("Live.js does not support the file protocol. It needs http."); | |
})(); | |
' | |
echo "$live_js" > static/js/live.js | |
app_lua='local lapis = require("lapis") | |
local app = lapis.Application() | |
app:enable("etlua") | |
app.layout = require "views.layout" | |
app:get("/", function() | |
return {render = "pages.index"} | |
end) | |
return app | |
' | |
echo "$app_lua" > app.lua | |
layout='<!doctype html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1" /> | |
<title><%= Page_title or "Lapis Page" %></title> | |
<link rel="stylesheet" href="/static/css/reset.css" /> | |
<script src="/static/js/live.js"></script> | |
</head> | |
<body> | |
<% content_for("inner") %> | |
</body> | |
</html> | |
' | |
echo "$layout" > views/layout.etlua | |
git init | |
git add . | |
eval "$(luarocks path)" | |
echo "To start the server:" | |
echo "" | |
echo "cd $project_name/" | |
echo "lapis server" | |
echo "Open your browser at http://localhost:8080" |
This looks nice. I would use this for sure! I think it would be nice if there can be a choice between using strictly the api or using the api and etlua.
granted this looks great already!
There
- In line 11: Bash wouldn't recognize that command if it's not in
PATH
, unless you have./lua_modules/bin/lapis
instead.
Do you have a recommended solution cause the only way I can think about this is to add the path to the .bashrc
- In line 14-17: You can simply remove the mkdir static and add -p to the rests of mkdir args, it will create a directory recursively.
Never knew about this, thanks
In test.sh line 37:
index_etlua='
^-- SC2089 (warning): Quotes/backslashes will be treated literally. Use an array.
Will probably just ignore this honestly
This looks nice. I would use this for sure! I think it would be nice if there can be a choice between using strictly the api or using the api and etlua.
granted this looks great already!
I'll try and implement that
Do you have a recommended solution cause the only way I can think about this is to add the path to the .bashrc
You can replace that with function that fallbacks to ~/lua_modules/bin/lapis
lapis() {
declare cmd=./lua_modules/bin/lapis
if command -v lapis >/dev/null; then
cmd=lapis
fi
command "$cmd" "$@"
}
But this might be not the rightest answer, it all depends on how you set up your shell and etc. I usually use direnv for project-wide PATH
and Luarocks settings.
I usually use direnv for project-wide
PATH
and Luarocks settings.
I have all my rocks installed in $HOME/.luarocks. I believe it would be pretty hard to accommodate everyone's specific use case. Maybe an environment variable for the luarocks bin could work?
PATH
, unless you have./lua_modules/bin/lapis
instead.mkdir static
and add-p
to the rests ofmkdir
args, it will create a directory recursively.mkdir views
and add-p
flag formkdir views/pages
.For the rest of it, I'll let Shellcheck do the job instead:
Shellcheck output