1. 87
    1. 30

      I love stories of people just making stuff for personal use. No big sell to move your own website over, just a story of solving a pain point.

      1. 16

        This! I just created a template language in a Rust project of mine. After I stopped, I looked and realized “oh, this is just Handlebars, but bad. Nice!”

        1. 7

          If you’ve got the drive to make your own version of {{tech_name}}, do it. Even if it’s not as good, there’s so much value in understanding how something works. Plus, there’s no worry about versioning moving out from under you, about some breaking change you weren’t expecting.

    2. 11

      It feels like we’ve regressed. 15 years ago it was fine to run a script from scratch on every single request, but today we insist that we have to serve every personal website from a CDN. That means no fun allowed—if you so much as want the ©2023 in the footer to update, you’ll need to rebuild and redeploy the site.

      Sounds like the author has been reading too much of the Orange Site. Go ahead, use a single server and serve up dynamic content! I do that—my blog is a CGI script. Computers are way more powerful than we give them credit for.

    3. 9

      This is amazingly blursed, I kinda want to try this.

      1. 1

        You should also try this out: https://maud.lambda.xyz/

        refreshingly simple, in fact it has so little rules that the whole site can be read in 10 minutes.

        I am using it for my site at https://rgbcu.be/ (nope, nothing here yet, but soon when I get the hosting sorted & un-fuck the code so it compiles)

        1. 1

          I used to use maud on my website! It didn’t scale to meet my needs.

          1. 1

            https://github.com/Xe/site/blob/cbdea8ba3fca9a663778af71f8df5965aeb6c090/lib/xesite_markdown/src/lib.rs#L50-L94

            why didn’t you use pulldown-cmark? It makes it possible to do the things you’ve described, with .map(…)

            I’m planning to do the HTML boilerplate stuff using Maud and writing all the blog posts in markdown, and using pulldown-cmark to create entries, etc.

    4. 8

      I love CGI scripts too, but I don’t get it, why not use tables? Then you don’t have the redundancy of the closing tags and your editors existing bracket matching works.

      1. 4

        Interesting proposal. To illustrate it, here’s how the article’s function Wide example might look if it represented HTML using Hiccup-shaped Lua tables instead of JSX-like LuaX syntax:

        function Wide(atts, children)
          return {"div", {class="wide flex justify-center mv4"},
            {"div", {class=concatClasses({
              "flex flex-column flex-row-l",
              atts.class or "items-center",
              "g4"
            })},
              {"div", {class="w-100 flex-fair-l p-dumb"},
                children[1]
              },
              {"div", {class="w-100 flex-fair-l p-dumb"},
                children[2]
              }
            }
          }
        end
        
        function concatClasses(classes)
          return table.concat(classes, " ")
        end
        
        -- usage of Wide
        Wide({}, {
          {"p",
            "Widgets can be snapped onto a Microchip..."
          },
          Video({slug="microchips"})
        })
        

        In the usage example, calling Wide required wrapping all children in {}, which is inconsistent with Hiccup conventions. Wide could be made consistent by changing it to take a variable number of arguments, though that is not trivial in Lua.

        1. 7

          We do this on the Fennel web site (Fennel compiles to Lua but has slightly different syntax for tables) and it ends up being significantly less verbose than raw HTML, plus it works with paredit and friends: https://git.sr.ht/~technomancy/fennel-lang.org/tree/main/item/main.fnl

      2. 2

        Because it would be way more verbose for describing HTML than HTML itself? You can see what I transpile it to in the article - there’s no way I’m writing that by hand all the time.

        Besides, think about content like this:

        <a href="https://bvisness.me/luax/">My new project, <strong>"LuaX"</strong></a>
        

        vs. the most sugary table-based syntax I can think of:

        { type = "a", atts = { href = "https://bvisness.me/luax/" },
          "My new project, ", { type = "strong", "\"LuaX\"" },
        }
        

        I don’t want to worry about string escaping. I don’t want to think about splitting up text nodes around other tags. I don’t want to write HTML escape codes into Lua strings so that they come through correctly in HTML. I just want to write HTML.

        1. 3

          Not that I think it should definitely be tables vs. JSX-like – but one thing that helps with syntax sugars in Lua is you can call functions without parens, like foo 42, which when using a single table parameter allows things like button { label = "foo", onclick = function() ... end }. I did a quick experiment with something like that for React-y code at one point: https://twitter.com/snikhilesh/status/982045827282563074

          I also like using imperative calls for accumulating elements (like in immediate mode ui apis) so I can use a regular for loop or if statement around my elements.

        2. 1

          You can almost-but-not-quite do this in Jinja:

          <!-- Template definitions -->
          {% macro wide(left, right, classes="") %}
          <div class="wide flex justify-center mv4">
            <div class="flex flex-column flex-row-l {{ classes or 'items-center' }} g4">
              <div class="w-100 flex-fair-l p-dumb">
                {{ left }}
              </div>
              <div class="w-100 flex-fair-l p-dumb">
                {{ right }}
              </div>
            </div>
          </div>
          {% endmacro %}
          {% macro wide_left(right, classes="") %}
          {{ wide(caller(), right, classes) }}
          {% endmacro %}
          
          <!-- Later in the article -->
          {% call wide_left(video('basics')) %}
          <p>Before we go further, let me introduce you to programming in Dreams...</p>
          {% endcall %}
          

          Unfortunately it’s not expressive enough to do the children[1]/children[2] thing. Jinja also suffers a bit from not being a real programming language, but at least it kinda has a python escape hatch.

          1. 1

            Most template systems have a real-language escape hatch, but that’s one of the things I was specifically trying to avoid.

      3. [Comment removed by author]

    5. 6

      So we have JSX-like language support for:

      Any others?

      1. 3

        Technically Elixir has something similar to that thanks to sigil macros and HEEx.

      2. 3

        Rust: https://yew.rs/docs/concepts/html#tag-structure

        Haskell: https://hackage.haskell.org/package/ihp-hsx-0.18.0#readme

        Scala 2 has XML literals, perhaps they could be used.

      3. 2

        Pushup https://pushup.adhoc.dev is not JSX exactly, but it’s HTML-first blended with Go for control flow and variable access. (I’m the Pushup creator.)

        1. 1

          Thanks. Not sure what to think about the syntax though. Why not make it like JSX instead? Would also make it easier to port tooling and editor support to.

    6. 5

      This was really fun. I really resonated with the part “These days though I understand compilers well enough to just sit down and write one, and it feels great.”

      In the past I’ve had my fun with parsec, a Haskell monadic parser combinator library. These days I just write out my own byte stream reader and it comes out so lightweight with no surprises,

    7. 4

      Very interesting write-up!

      The problem is that no preprocessor-style template language actually allows you to manipulate the HTML as data.

      Not a template language, but @bvisness you might find soupault interesting for a future adventure. It has been, for my personal use case, a very good middle ground between doing complex, ad-hoc scripting for my static website while keeping it static.

      1. 3

        In a vaguely similar vein, there’s Enlive for Clojure, a DSL for manipulating & transforming HTML expressed as regular Clojure data structures.

    8. 4

      I feel like this would be fun to use with redbean.

    9. 4

      I see this critique all the time and I still don’t understand it:

      I got into programming because I wanted to make websites. When I started out, you could just copy HTML files up to your server and you had a website. It was magical! And PHP made it even better; you could just throw in a little snippet of server-side code and you had a dynamic page.

      You can still do that! Obviously, the author is free to build their site however they wish, and making JSX for Lua sounds fun and intellectually gratifying. But there’s absolutely nothing stopping you from copying HTML and PHP files to an Apache server like it’s 2003 if that’s what brings you joy.

      1. 5

        PHP does not bring me joy, for reasons addressed in the article.

      2. 1

        Second this. You just have to resist to start with Composer and shenanigans.

    10. 2

      This is a cool project and all and I love the mindset behind it, but I can’t help but think….. simply using multiline strings and a custom syntax highlighter would be just as aesthetically appealing, e.g.:

      function Article(atts, children)
        local a = atts.article
        return [[<article>
          <header>
            <h1><a href=]]..absurl(a.slug)..'>'..a.title..[[</a></h1>
            <span class="post-details">
              <time datetime=]]..os.date("%Y-%m-%dT%H:%M:%S%z", atts.date)..[[ itemprop="datePublished">
                ]]..os.date("%B %-d, %Y", atts.date)..[[
              </time>
            </span>
          </header>
          <p>
            ]]..a.description..[[
          </p>
        </article>]]
      end
      

      But maybe I’m missing something here!

      1. 2

        I think you’re missing HTML-escaping your strings there ;)

        1. 2

          If that’s the only problem with this approach, there must be some way to leverage metatables to preserve the aesthetic.

          Here’s a quick solution I came up with:

          getmetatable('').__add = function(left, right)
              --return left..right:gsub(--[[todo: put regex here]])
              return left..right:upper()
          end
          
          a = {
              slug = 'slug',
              title = 'title',
              date = os.time() - 7 * 24 * 60 * 60,
              description = 'description',
          }
          
          print([[<article>
              <header>
                <h1><a href=]]+a.slug..'>'+a.title..[[</a></h1>
                <span class="post-details">
                  <time datetime=]]+os.date("%Y-%m-%dT%H:%M:%S%z", a.date)..[[ itemprop="datePublished">
                    ]]+os.date("%B %-d, %Y", a.date)..[[
                  </time>
                </span>
              </header>
              <p>
                ]]+a.description..[[
              </p>
            </article>]])
          

          Output

          Basically the pattern is, anything you want escaped, you use +, and anything you don’t want escaped, you use ..

        2. [Comment removed by author]

    11. 2

      That’s a nice usage for lua. I love Lua for embedding. It’s easy to work with and very powerful;

    12. 1

      if you so much as want the ©2023 in the footer to update

      please don’t

      I want my ©2023 to update dynamically, dammit.

      😱

    13. 1

      I really like this approach! I had a similar notion of avoiding template languages with Pushup which was to start with HTML and let the programmer switch to Go code for control flow and variable access, etc. It all compiles down to pure Go code in the end.

      What I like about JSX-Lua here is a dynamic language may be more pleasant to author web pages in than Go with this general approach.

      https://pushup.adhoc.dev

    14. 1

      Just something worth considering: if you’re using a dynamic backend when a static one would be sufficient, you’re unnecessarily wasting energy.

    15. 1

      This is awesome and incredibly simple! Lua FTW!

    16. 1

      Looks cool.

      Have you considered allowing </> as a closing tag? I’m not a fan of tag duplication. I think it was initially added to XML (?) to (among other things) make it more fault tolerant, but not sure if it’s worth it.

      What do you think?

      1. 2

        I’m actually using </> already since I also support “fragments” like return <>I have no parent tag :D</>. Otherwise it might be worth a shot. That said, I really don’t care at all about closing tags; I know they annoy people but it’s just not an issue for me.

        1. 1

          I see. Sure

    17. [Comment removed by author]