The website generators written in nim tend to fall into two types:

  1. An all-in-one website dynamic generator that is meant to build an actual 
web server. Think Karax and the like.
  2. Static website-generators written in Nim but use generic text templating.



The benefit of type #1 is that the DSL support of the compiler helps find flaws 
at compile-time. The downside is that you are now left with something that is 
not a true website. It's a custom webserver that may or may not scale well in 
hostile distributed security-locked cloud environments.

The benefit of type #2 is that you have a true website. You can use NGINX, 
Lighttpd, Apache, etc and it is pretty universal. But you just lost the benefit 
of catching bugs and organizing things during compilation. Welcome to run-time 
bug hell on a big enough project.

An alternative that is the best of both worlds, is Angular. Sort of. But, that 
isn't a nim thing anyway. Is it possible to do something in Nim? Can we get 
that meta?

Just throwing out an idea.

A tool that runs the nim compiler many times against a collection of source 
code files to ultimately generate a full website. aka `plusplus [sourcedir] 
[destdir] [versionTag]` ?

Floating out some bogus code to show what I mean:

**model_todolist.nim**
    
    
    #@ PLUSPLUS:
    #@   role = model
    
    export ToDoListModel
    export AddToDoModel
    
    type
      ToDoListModel = object
        todos: seq[string]
      
      AddToDoModel = object
        content: string
    
    
    Run

**index.nim**
    
    
    import plusplus
    import plusplus.html
    import std/json
    
    import add_todo
    import model_todolist
    
    #@ PLUSPLUS:
    #@   role = html
    #@   start = page
    #@   route = "/"
    
    HTMLGEN(todo_form):
      button(onclick = showFormJs("this_form")):
        html("click me -> to show form")
      p
      form(id = this_form, `method` = POST, action=addTheTodo, 
format=AddToDoModel style=html("display: none;")):
        input_text(name = todo_content)
        input_submit(value = html("Add"))
    
    HTMLGEN(showTodoItems):
      h1:
        html("Todo items")
      ul(id = current_items)
    
    HTMLGEN(page):
      head:
        title("ToDo App")
      body:
        showTodoItems()
        todoForm()
    
    JSGEN(showFormJs, something: string):
      Element[something].style.display = block
    
    JSGEN(getTodoItemsJs):
      ajax fetch("https://foo.bar/gettodos";  `method` = post) format 
ToDoListModel:
        for item in ajaxResponse.todos:
          li = li():
            html(item)
          Element[current_items].add li
    
    # this file creates 'index.html'; which is '/' in Lighttpd
    # alternatively, it creates 'index.html' and 'index.js' (which is loaded in 
<head>)
    
    
    Run

which outputs index.html (or index.html and index.js):
    
    
    <html>
      <head>
        <title>ToDo App</title>
      </head>
      <h1>Todo items</h1>
      <ul id='current_items'>
      </ul>
      <button onclick = 'showFormJs("this_form")'>
        click me -> to show form
      </button>
      <p />
      <form id='this_form' method=POST action='/add_todo' style='display: 
none;'>
        <input type='text' name='todo_content'>
        <input type='submit' value='Add'>
      </form>
      <script>
        let ToDoListModel = {
          todos: []
        }
        let AddToDoModel = {
          content: ''
        }
      </script>
      <script>
        fetch("https://foo.bar/gettodos";, {
          method: 'post',
          body: post,
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
          }
        })
        .then(response => response.json())
        .then(todos => {
          const todoList = todos.map(todo => todo.content);
          todoList.forEach((item)=>{
            let li = document.createElement("li");
            li.innerText = item;
            document.getElementById('current_items').appendChild(li);
          })
        })
        .catch((error) => {
          console.log(error)
        })
      </script>
      <script>
        function showFormJs(something) {
          document.getElementById(something).style.display = 'block';
        }
      </script>
    </html>
    
    
    Run

While the resulting html and JS is using generic strings for IDs and generic 
JSON objects for models, the nim source code is using real identifiers and type 
structs for compile-time verification of everything.

The resulting [destdir] contains just HTML5/JS/CSS etc. Choose your favorite 
cloud webserver to host it.

A possiblity for pages that do not show content, but only take 
action-then-redirect:

**add_todo.nim**
    
    
    import plusplus
    import plusplus.action
    import std/json
    import model_todolist
    
    #@ PLUSPLUS:
    #@   role = action
    #@   start = addTheTodo
    #@   route "/add_todo"
    
    export addTheToDo
    
    proc addTheToDo(request: Request, body: AddToDoModel): redirection =
      let newContent = body.content
      var msg = ""
      if newContent.len > 0:
        apiCall(post, "https://foo.bar/newtodo";, %{"content" = newContent}))
        msg = "something added"
      else:
        msg = "message is empty"
      result = redirectionUrl("/", msg=msg)
    
    # this file could create an actual linux app named add_todo.cgi which is 
called by Lighttpd
    # (or Apache) using mod_cgi
    
    # or things could be kept pure with a html/js redirect function.
    
    
    Run

Thoughts? Has someone already done something like this? I'm sure there are lots 
of flaws in my above example psuedo-code, but has something like

Reply via email to