aboutsummaryrefslogtreecommitdiffstats
path: root/build
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-01-25 12:57:36 +0000
committers-ol <s-ol@users.noreply.github.com>2020-01-25 12:57:36 +0000
commit2b91c3d0af33099f7b260a41efac252655104c50 (patch)
tree4636152e118d90381a584f2636da34efbe8dd568 /build
parentcorrection in portfolio (diff)
parentupdate README.md (diff)
downloadmmm-2b91c3d0af33099f7b260a41efac252655104c50.tar.gz
mmm-2b91c3d0af33099f7b260a41efac252655104c50.zip
Merge branch 'ba'
Diffstat (limited to 'build')
-rw-r--r--build/bundle_fileder.moon155
-rw-r--r--build/fsck.moon37
-rw-r--r--build/import.moon53
-rw-r--r--build/layout.moon109
-rw-r--r--build/render_all.moon32
-rw-r--r--build/render_fileder.moon93
-rw-r--r--build/server.moon231
-rw-r--r--build/util.moon16
8 files changed, 353 insertions, 373 deletions
diff --git a/build/bundle_fileder.moon b/build/bundle_fileder.moon
deleted file mode 100644
index c9f8c35..0000000
--- a/build/bundle_fileder.moon
+++ /dev/null
@@ -1,155 +0,0 @@
-require 'mmm'
-import get_path from require 'build.util'
-import Fileder, Key from require 'mmm.mmmfs.fileder'
-import opairs from require 'mmm.ordered'
-import to_lua from require 'moonscript.base'
-
--- usage:
--- moon bundle_fileder.moon <path_to_root> <dirname> <facets>... -- <children>...
-{ path_to_root, dirname } = arg
-
-assert path_to_root, "please specify the relative root path"
-assert dirname, "please specify the fileder dirname"
-path = get_path path_to_root
-
-facets = {}
-children_bundles = {}
-
-do
- addto = facets
- for file in *arg[3,]
- continue if file == 'Tupdefault.lua'
-
- if file == '--'
- addto = children_bundles
- continue
- table.insert addto, file
-
--- load a fs file as a fileder facet
-load_facet = (filename) ->
- key = (filename\match '(.*)%.%w+') or filename
- key = Key key\gsub '%$', '/'
- key.filename = filename
-
- file = assert (io.open filename, 'r'), "couldn't open facet file '#{filename}'"
- value = file\read '*all'
- file\close!
-
- key, value
-
--- escape a string for lua aparser
-escape = (str) -> string.format '%q', tostring str
-
--- dump a fileder subtree as Lua source
-dump_fileder = do
- _dump = (fileder, root=false) ->
- code = "Fileder {"
-
- for key, val in pairs fileder.facets
- if key.original
- key = "fromcache(#{escape key}, #{escape key.original})"
- else
- key = escape key
- code ..= "\n[#{key}] = #{escape val},"
-
- for child in *fileder.children
- code ..= "\n#{_dump child},"
-
- code ..= "\n}"
- code
-
-
- (fileder) -> "
-local mmmfs = require 'mmm.mmmfs'
-local Key, Fileder = mmmfs.Key, mmmfs.Fileder
-local function fromcache(str, orig)
- local key = Key(str)
- key.original = Key(orig)
- return key
-end
-
-return #{_dump fileder, true}
- "
-
-renders = {
- {
- inp: '^text/moonscript %-> (.*)'
- out: 'text/lua -> %1'
- render: (val, fileder, old, new) ->
- lua, err = to_lua val
-
- if not lua
- error "Error compiling #{old}: #{err}"
-
- "-- this moonscript facet has been transpiled to
--- '#{new}' for execution in the browser.
--- refer to the original facet as the source.
-#{lua}"
- },
- {
- inp: '^image/'
- out: 'URL -> %0'
- render: (val, fileder, old, new) -> "#{fileder.path}/#{old.filename}", "[binary removed]"
- },
- {
- inp: '^video/'
- out: 'URL -> %0'
- render: (val, fileder, old, new) -> "#{fileder.path}/#{old.filename}", "[binary removed]"
- },
-}
-
-with io.open '$bundle.lua', 'w'
- \write dump_fileder with fileder = Fileder 'name: alpha': dirname
- \mount path, true
-
- order = nil
- children = {}
-
- for facet in *facets
- if facet == '$order'
- order = [line for line in io.lines facet]
- continue
- key, value = load_facet facet
- .facets[key] = value
-
- extra_facets = {}
- for key, value in pairs .facets
- for { :inp, :out, :render } in *renders
- continue unless key.type\match inp
-
- built_key = Key key.name, key.type\gsub inp, out
- built_key.original = key
-
- -- dont overwrite existing keys
- continue if \has built_key
-
- rendered, replace = render value, fileder, key, built_key
-
- extra_facets[built_key] = rendered
- .facets[key] = replace if replace
-
- break
-
- for k,v in pairs extra_facets
- .facets[k] = v
-
- for child in *children_bundles
- -- @BUG: child bundles are malformed due to Tup bug ($ symbol)
- child = child\gsub '/%.lua$', '/$bundle.lua'
-
- dirname = assert child\match '^([%w-_%.]+)/%$bundle%.lua$'
- children[dirname] = dofile child
-
- if order
- -- order from order file
- for i, name in pairs order
- child = assert children[name], "child in $order but not fs: #{name}"
- table.insert .children, child
- children[name] = nil
-
- -- sort remainder alphabeticalally
- for name, child in opairs children
- table.insert .children, child
- warn "child #{name} not in $order!" if order
-
- \close!
diff --git a/build/fsck.moon b/build/fsck.moon
new file mode 100644
index 0000000..df882a9
--- /dev/null
+++ b/build/fsck.moon
@@ -0,0 +1,37 @@
+add = (tmpl) ->
+ package.path ..= ";#{tmpl}.lua"
+ package.moonpath ..= ";#{tmpl}.moon"
+
+add '?'
+add '?.server'
+add '?/init'
+add '?/init.server'
+
+require 'mmm'
+
+import get_store from require 'mmm.mmmfs.stores'
+
+-- usage:
+-- moon server.moon [FLAGS] [STORE] [host] [port]
+-- * FLAGS - any of the following:
+-- --[no-]rw - enable/disable POST?PUT/DELETE operations (default: on if local)
+-- --[no-]unsafe - enable/disable server-side code execution when writable is on (default: on if local or --no-rw)
+-- * STORE - see mmm/mmmfs/stores/init.moon:get_store
+-- * host - interface to bind to (default localhost, set to 0.0.0.0 for public hosting)
+-- * port - port to serve from, default 8000
+
+flags = {}
+arguments = for a in *arg
+ if flag = a\match '^%-%-no%-(.*)$'
+ flags[flag] = false
+ continue
+ elseif flag = a\match '^%-%-(.*)$'
+ flags[flag] = true
+ continue
+ else
+ a
+
+{ store, host, port } = arguments
+
+store = get_store store, verbose: true
+store\fsck!
diff --git a/build/import.moon b/build/import.moon
new file mode 100644
index 0000000..92d4397
--- /dev/null
+++ b/build/import.moon
@@ -0,0 +1,53 @@
+add = (tmpl) ->
+ package.path ..= ";#{tmpl}.lua"
+ package.moonpath ..= ";#{tmpl}.moon"
+
+add '?'
+add '?.server'
+add '?/init'
+add '?/init.server'
+
+require 'mmm'
+require 'lfs'
+import Key from require 'mmm.mmmfs.fileder'
+import SQLStore from require 'mmm.mmmfs.stores.sql'
+
+-- usage:
+-- moon import.moon <root> [output.sqlite3]
+{ root, store } = arg
+
+assert root, "please specify the root directory"
+
+-- load a fs file as a fileder facet
+load_facet = (filename, filepath) ->
+ key = (filename\match '(.*)%.%w+') or filename
+ key = Key key\gsub '%$', '/'
+ key.filename = filename
+
+ file = assert (io.open filepath, 'rb'), "couldn't open facet file '#{filename}'"
+ value = file\read '*all'
+ file\close!
+
+ key, value
+
+with SQLStore :file, verbose: true
+ import_fileder = (fileder, dirpath) ->
+ for file in lfs.dir dirpath
+ continue if '.' == file\sub 1, 1
+ continue if file == 'Tupdefault.lua'
+ continue if file == 'index.html'
+ continue if file == '$order'
+
+ filepath = "#{dirpath}/#{file}"
+ switch lfs.attributes filepath, 'mode'
+ when 'file'
+ key, value = load_facet file, filepath
+ \create_facet fileder, key.name, key.type, value
+ when 'directory'
+ next_fileder = \create_fileder fileder, file
+ -- \create_facet next_fileder, 'name', 'alpha', file
+ import_fileder next_fileder, filepath
+ else
+ warn "unknown entry type '#{attr.mode}'"
+
+ import_fileder '', root
diff --git a/build/layout.moon b/build/layout.moon
deleted file mode 100644
index 7985371..0000000
--- a/build/layout.moon
+++ /dev/null
@@ -1,109 +0,0 @@
-import header, aside, footer, div, svg, script, g, circle, h1, span, b, a, img from require 'mmm.dom'
-import navigate_to from (require 'mmm.mmmfs.util') require 'mmm.dom'
-
-pick = (...) ->
- num = select '#', ...
- i = math.ceil math.random! * num
- select i, ...
-
-iconlink = (href, src, alt, style) -> a {
- class: 'iconlink',
- target: '_blank',
- rel: 'me',
- :href,
- img :src, :alt, :style
-}
-
-logo = svg {
- class: 'sun'
- viewBox: '-0.75 -1 1.5 2'
- xmlns: 'http://www.w3.org/2000/svg'
- baseProfile: 'full'
- version: '1.1'
-
- g {
- transform: 'translate(0 .18)'
-
- g { class: 'circle out', circle r: '.6', fill: 'none', 'stroke-width': '.12' }
- g { class: 'circle in', circle r: '.2', stroke: 'none' }
- }
-}
-
-{
- header: header {
- div {
- h1 {
- navigate_to '/', logo
- span {
- span 'mmm', class: 'bold'
- '&#8203;'
- '.s&#8209;ol.nu'
- }
- }
- span "fun stuff with code and wires"
- -- pick 'fun', 'cool', 'weird', 'interesting', 'new'
- -- pick 'stuff', 'things', 'projects', 'experiments', 'news'
- -- "with"
- -- pick 'mostly code', 'code and wires', 'silicon', 'electronics'
- }
- aside {
- navigate_to '/about', 'about me'
- navigate_to '/portfolio', 'portfolio'
- navigate_to '/games', 'games'
- navigate_to '/projects', 'other'
- a {
- href: 'mailto:s%20[removethis]%20[at]%20s-ol.nu'
- 'contact'
- script "
- var l = document.currentScript.parentElement;
- l.href = l.href.replace('%20[at]%20', '@');
- l.href = l.href.replace('%20[removethis]', '') + '?subject=Hey there :)';
- "
- }
- }
- }
- footer: footer {
- span {
- 'made with \xe2\x98\xbd by '
- a 's-ol', href: 'https://twitter.com/S0lll0s'
- ", #{os.date '%Y'}"
- }
- div {
- class: 'icons',
- iconlink 'https://github.com/s-ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/github.svg', 'github'
- iconlink 'https://merveilles.town/@s_ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mastodon.svg', 'mastodon'
- iconlink 'https://twitter.com/S0lll0s', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/twitter.svg', 'twitter'
- iconlink 'https://webring.xxiivv.com/#random', 'https://webring.xxiivv.com/icon.black.svg', 'webring',
- { height: '1.3em', 'margin-left': '.3em', 'margin-top': '-0.12em' }
- }
- }
- get_meta: =>
- title = (@get 'title: text/plain') or @gett 'name: alpha'
-
- l = (str) ->
- str = str\gsub '[%s\\n]+$', ''
- str\gsub '\\n', ' '
- e = (str) -> string.format '%q', l str
-
- meta = "
- <meta charset=\"UTF-8\">
- <title>#{l title}</title>
- "
-
- if page_meta = @get '_meta: mmm/dom'
- meta ..= page_meta
- else
- meta ..= "
- <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
-
- <meta property=\"og:title\" content=#{e title} />
- <meta property=\"og:type\" content=\"website\" />
- <meta property=\"og:url\" content=\"https://mmm.s-ol.nu#{@path}/\" />
- <meta property=\"og:site_name\" content=\"mmm\" />"
-
- if desc = @get 'description: text/plain'
- meta ..= "
- <meta property=\"og:description\" content=#{e desc} />"
-
- meta
-}
diff --git a/build/render_all.moon b/build/render_all.moon
new file mode 100644
index 0000000..b09957e
--- /dev/null
+++ b/build/render_all.moon
@@ -0,0 +1,32 @@
+add = (tmpl) ->
+ package.path ..= ";#{tmpl}.lua"
+ package.moonpath ..= ";#{tmpl}.moon"
+
+add '?'
+add '?.server'
+add '?/init'
+add '?/init.server'
+
+require 'mmm'
+import Fileder from require 'mmm.mmmfs.fileder'
+import get_store from require 'mmm.mmmfs.stores'
+import render from require 'mmm.mmmfs.layout'
+
+-- usage:
+-- moon render_all.moon [STORE] [startpath]
+{ store, startpath } = arg
+
+export STATIC
+STATIC = true
+
+store = get_store store
+tree = Fileder store
+tree = tree\walk startpath if startpath
+
+for fileder in coroutine.wrap tree\iterate
+ print "rendering '#{fileder.path}'..."
+ os.execute "mkdir -p 'out/#{fileder.path}'"
+
+ with io.open "out/#{fileder.path}/index.html", 'w'
+ \write render (fileder\get 'text/html'), fileder
+ \close!
diff --git a/build/render_fileder.moon b/build/render_fileder.moon
deleted file mode 100644
index 76c7856..0000000
--- a/build/render_fileder.moon
+++ /dev/null
@@ -1,93 +0,0 @@
-require 'mmm'
-import get_path from require 'build.util'
-import tohtml from require 'mmm.component'
-import Browser from require 'mmm.mmmfs.browser'
-import get_meta, header, footer from require 'build.layout'
-
-export BROWSER
-
--- usage:
--- moon render.moon <path_to_root>
-{ path_to_root } = arg
-
-assert path_to_root, "please specify the relative root path"
-path = get_path path_to_root
-
-do
- seed = (str) ->
- len = #str
- rnd = -> math.ceil math.random! * len
-
- math.randomseed len
-
- return if len == 0
-
- upper, lower = 0, 0
- for i=1,4
- upper += str\byte rnd!
- upper *= 0x100
-
- lower += str\byte rnd!
- lower *= 0x100
-
- math.randomseed upper, lower
-
- seed path
-
-root = dofile '$bundle.lua'
-assert root, "couldn't load $bundle.lua"
-root\mount path, true
-
-BROWSER = Browser root, path
-
-with io.open 'index.html', 'w'
- \write [[
-<!DOCTYPE html>
-<html>
- <head>
- <link rel="stylesheet" type="text/css" href="/main.css" />
- <!--
- <link rel="preload" as="fetch" href="/mmm/dom/init.lua" />
- <link rel="preload" as="fetch" href="/mmm/component/init.lua" />
- <link rel="preload" as="fetch" href="/mmm/mmmfs/init.lua" />
- <link rel="preload" as="fetch" href="/mmm/mmmfs/fileder.lua" />
- <link rel="preload" as="fetch" href="/mmm/mmmfs/browser.lua" />
- -->
-
- <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,400" rel="stylesheet">
- ]]
- \write "
- #{get_meta root}
- </head>
- <body>
- #{header}
-
- #{assert (tohtml BROWSER), "couldn't render BROWSER"}
-
- #{footer}
- "
- \write [[
- <script src="/highlight.pack.js"></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/marked/0.5.1/marked.min.js"></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.min.js"></script>
- <script src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
- <script src="/fengari-web.js"></script>
- <script type="application/lua" src="/mmm.bundle.lua"></script>
- <script type="application/lua">require 'mmm'</script>
- ]]
- \write "
- <script type=\"application/lua\">
- on_load = on_load or {}
- table.insert(on_load, function()
- local path = #{string.format '%q', path}
- local browser = require 'mmm.mmmfs.browser'
- local root = dofile '/$bundle.lua'
- root:mount('', true)
-
- BROWSER = browser.Browser(root, path, true)
- end)
- </script>
- </body>
-</html>
- "
- \close!
diff --git a/build/server.moon b/build/server.moon
new file mode 100644
index 0000000..7c0ff46
--- /dev/null
+++ b/build/server.moon
@@ -0,0 +1,231 @@
+add = (tmpl) ->
+ package.path ..= ";#{tmpl}.lua"
+ package.moonpath ..= ";#{tmpl}.moon"
+
+add '?'
+add '?.server'
+add '?/init'
+add '?/init.server'
+
+require 'mmm'
+
+import dir_base, Key, Fileder from require 'mmm.mmmfs.fileder'
+import convert, MermaidDebugger from require 'mmm.mmmfs.conversion'
+import get_store from require 'mmm.mmmfs.stores'
+import render from require 'mmm.mmmfs.layout'
+import Browser from require 'mmm.mmmfs.browser'
+import decodeURI from require 'http.util'
+
+lfs = require 'lfs'
+server = require 'http.server'
+headers = require 'http.headers'
+
+class Server
+ new: (@store, opts={}) =>
+ opts = {k,v for k,v in pairs opts}
+ opts.host = 'localhost' unless opts.host
+ opts.port = 8000 unless opts.port
+ opts.onstream = @\stream
+ opts.onerror = @\error
+
+ @server = server.listen opts
+
+ @flags = opts.flags
+
+ if @flags.rw == nil
+ @flags.rw = opts.host == 'localhost' or opts.host == '127.0.0.1'
+
+ if @flags.unsafe == nil
+ @flags.unsafe = not @flags.rw or opts.host == 'localhost' or opts.host == '127.0.0.1'
+
+ export UNSAFE
+ UNSAFE = @flags.unsafe
+ require 'mmm.mmmfs'
+
+ listen: =>
+ assert @server\listen!
+
+ _, ip, port = @server\localname!
+ print "[#{@@__name}]",
+ "running at #{ip}:#{port}",
+ "[#{table.concat [flag for flag,on in pairs @flags when on], ', '}]"
+
+ assert @server\loop!
+
+ handle_interactive: (fileder, facet) =>
+ root = Fileder @store
+ browser = Browser root, fileder.path, facet.name
+
+ deps = [[
+ <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.min.js"></script>
+ <script type="text/javascript" src="//unpkg.com/mermaid@8.4.0/dist/mermaid.min.js"></script>
+ <script type="text/javascript" src="//unpkg.com/marked@0.7.0/marked.min.js"></script>
+ <link rel="stylesheet" type="text/css" href="//unpkg.com/codemirror@5.49.2/lib/codemirror.css" />
+ <script type="text/javascript" src="//unpkg.com/codemirror@5.49.2/lib/codemirror.js"></script>
+ <script type="text/javascript" src="//unpkg.com/codemirror@5.49.2/mode/lua/lua.js"></script>
+ <script type="text/javascript" src="//unpkg.com/codemirror@5.49.2/mode/markdown/markdown.js"></script>
+ <script type="text/javascript" src="//unpkg.com/codemirror@5.49.2/addon/display/autorefresh.js"></script>
+ <script type="text/javascript" src="/static/fengari-web/:text/javascript"></script>
+ <script type="text/lua" src="/static/mmm/:text/lua"></script>
+ <script type="text/lua">require 'mmm'; require 'mmm.mmmfs'</script>]]
+
+ render browser\todom!, fileder, noview: true, scripts: deps .. "
+ <script type=\"text/lua\">
+ on_load = on_load or {}
+ table.insert(on_load, function()
+ local path = #{string.format '%q', fileder.path}
+ local facet = #{string.format '%q', facet.name}
+ local browser = require 'mmm.mmmfs.browser'
+ local fileder = require 'mmm.mmmfs.fileder'
+ local web = require 'mmm.mmmfs.stores.web'
+
+ local store = web.WebStore({ verbose = true })
+ local root = fileder.Fileder(store, store:get_index(nil, -1))
+
+ local err_and_trace = function (msg) return debug.traceback(msg, 2) end
+ local ok, browser = xpcall(browser.Browser, err_and_trace, root, path, facet, true)
+ if not ok then error(browser) end
+ end)
+ </script>"
+
+ handle_debug: (fileder, facet) =>
+ debugger = MermaidDebugger!
+ fileder\find facet, nil, nil, nil, debugger
+ convert 'text/mermaid-graph', 'text/html', debugger\render!, fileder, facet.name
+
+ handle: (method, path, facet, value) =>
+ if not @flags.rw and method != 'GET' and method != 'HEAD'
+ return 403, 'editing not allowed'
+
+ switch method
+ when 'GET', 'HEAD'
+ root = Fileder @store
+ export BROWSER
+ BROWSER = :root
+ fileder = root\walk path -- Fileder @store, path
+
+ if not fileder
+ -- fileder not found
+ return 404, "fileder '#{path}' not found"
+
+ val = switch facet.name
+ when '?index', '?tree'
+ -- serve fileder index
+ -- '?index': one level deep
+ -- '?tree': recursively
+ depth = if facet.name == '?tree' then -1 else 1
+ index = @store\get_index path, depth
+ convert 'table', facet.type, index, fileder, facet.name
+ else
+ if facet.type == 'text/html+interactive'
+ @handle_interactive fileder, facet
+ else if base = facet.type\match '^DEBUG %-> (.*)'
+ facet.type = base
+ @handle_debug fileder, facet
+ else if not fileder\has_facet facet.name
+ 404, "facet '#{facet.name}' not found in fileder '#{path}'"
+ else
+ fileder\get facet
+
+ if val
+ 200, val
+ else
+ 406, "cant convert facet '#{facet.name}' to '#{facet.type}'"
+ when 'POST'
+ if facet
+ @store\create_facet path, facet.name, facet.type, value
+ 200, 'ok'
+ else
+ 200, @store\create_fileder dir_base path
+ when 'PUT'
+ if facet
+ @store\update_facet path, facet.name, facet.type, value
+ 200, 'ok'
+ else
+ cmd, args = value\match '^([^\n]+)\n(.*)'
+ switch cmd
+ when 'swap'
+ child_a, child_b = args\match '^([^\n]+)\n([^\n]+)$'
+ assert child_a and child_b, "invalid arguments"
+
+ @store\swap_fileders path, child_a, child_b
+ 200, 'ok'
+ when nil
+ 400, "invalid request"
+ else
+ 501, "unknown command #{cmd}"
+ when 'DELETE'
+ if facet
+ @store\remove_facet path, facet.name, facet.type
+ else
+ @store\remove_fileder path
+ 200, 'ok'
+ else
+ 501, "not implemented"
+
+ err_and_trace = (msg) -> debug.traceback msg, 2
+ stream: (sv, stream) =>
+ req = stream\get_headers!
+ method = req\get ':method'
+ path = req\get ':path'
+ path = decodeURI path
+
+ path_facet, type = path\match '(.*):(.*)'
+ path_facet or= path
+ path, facet = path_facet\match '(.*)/([^/]*)'
+
+ facet = if facet == '' and type == '' and method ~= 'GET' and method ~= 'HEAD'
+ nil
+ else
+ type or= 'text/html+interactive'
+ type = type\match '%s*(.*)'
+ Key facet, type
+
+ value = stream\get_body_as_string!
+ ok, status, body = xpcall @.handle, err_and_trace, @, method, path, facet, value
+ if not ok
+ warn "Error handling request (#{method} #{path} #{facet}):\n#{status}"
+ body = "Internal Server Error:\n#{status}"
+ status = 500
+
+ res = headers.new!
+ response_type = if status > 299 then 'text/plain'
+ else if facet and facet.type == 'text/html+interactive' then 'text/html'
+ else if facet then facet.type
+ else 'text/plain'
+ res\append ':status', tostring status
+ res\append 'content-type', response_type
+
+ stream\write_headers res, method == 'HEAD'
+ if method ~= 'HEAD'
+ stream\write_chunk body, true
+
+ error: (sv, ctx, op, err, errno) =>
+ msg = "#{op} on #{tostring ctx} failed"
+ msg = "#{msg}: #{err}" if err
+
+-- usage:
+-- moon server.moon [FLAGS] [STORE] [host] [port]
+-- * FLAGS - any of the following:
+-- --[no-]rw - enable/disable POST?PUT/DELETE operations (default: on if local)
+-- --[no-]unsafe - enable/disable server-side code execution when writable is on (default: on if local or --no-rw)
+-- * STORE - see mmm/mmmfs/stores/init.moon:get_store
+-- * host - interface to bind to (default localhost, set to 0.0.0.0 for public hosting)
+-- * port - port to serve from, default 8000
+
+flags = {}
+arguments = for a in *arg
+ if flag = a\match '^%-%-no%-(.*)$'
+ flags[flag] = false
+ continue
+ elseif flag = a\match '^%-%-(.*)$'
+ flags[flag] = true
+ continue
+ else
+ a
+
+{ store, host, port } = arguments
+
+store = get_store store
+server = Server store, :flags, :host, port: port and tonumber port
+server\listen!
diff --git a/build/util.moon b/build/util.moon
deleted file mode 100644
index b9c9613..0000000
--- a/build/util.moon
+++ /dev/null
@@ -1,16 +0,0 @@
-require 'lfs'
-
-get_path = (root) ->
- cwd = lfs.currentdir!
- path = ''
-
- while root\find '^%.%./'
- root = root\match '^%.%./(.*)'
- cwd, trimmed = cwd\match '(.*)(/[^/]+)$'
- path = trimmed .. path
-
- path
-
-{
- :get_path
-}