Basic Template

A simple website with Xranklin.jl and Norg.jl!

A few days ago I asked Thibaut Lienart how to setup Franklin.jl to work with Norg.jl. Norg.jl is a Julia package to parse norg files, and Franklin.jl is a static website generator written in Julia. He kindly answered and told me to try making it work with Xranklin.jl, the next version of Franklin.jl. If you are reading this, it means it worked! Here are the steps to make it work!

This is far from perfect (and you can see there is work to do if we want a proper integration), but this is still doable, and that means we can create a glue package for (X|F)ranklin.jl integration!

You can also check how the Norg specification is currently rendered or visit the GitHub repository for this website.

Create a new website and install dependencies.

First things first, you need a working website setup. The easiest way to do that is to create it using Franklin.jl and then switch to Xranklin.jl.

    
           using Franklin
   newsite("NorgInXranklin")
   
    

Then, you can switch to Xranklin.jl. The website generated is not yet compatible with the new library so for now simply delete menu1.md, menu2.md, menu3.md and most of the content in index.md. We don't need them anyway. You can then check that the website works. If it doesn't, keep deleting unnecessary things.

    
           using Xranklin
   serve(debug=true)
   
    

Great! now we can install Norg.jl. The latest version is not released yet, so we will install the developpment version.

    
           Pkg.add(url="https://github.com/Klafyvel/Norg.jl", rev="main")
   
    

You can quickly check that Norg.jl works as intended.

    
           using Norg
   norg"* Hello, world!"
   
    

You should get the following result:

    
           (K"NorgDocument", 1, 9)
   └─ (K"Heading1", 2, 9)
   └─ (K"ParagraphSegment", 4, 8)
   ├─ Hello
   ├─ ,
   ├─
   ├─ world
   └─ !
   
    

Invoking Norg.jl

Now is time to invoke Norg.jl to generate an HTML page from existing .norg files. This should happen within utils.jl. We need a lx_norg function there, based on the literate.jl template.

I ended up with the following code (a bit hacky):

    
           """
   \\norg{rpath}
   Try to find a norg file, resolve it and return it.
   ## Notes
   1. the `rpath` is taken relative to the website folder
   2. the `rpath` must end with `.norg` and must not start with a `/`
   """
   function lx_norg(p::Vector{String})::String
     lc = cur_lc()
     c = Xranklin._lx_check_nargs(:norg, p, 1)
     isempty(c) || return c

     rpath = Xranklin.unixify(strip(p[1]))
     if !endswith(rpath, ".norg")
       @warn """
       \\norg{...}
       The relative path
       <$rpath>
       does not end with '.norg'.
       """
       return Xranklin.failed_lxc("norg", p)
     end

     # try to form the full path to the norg file and check it's there
     fpath = Xranklin.path(:folder) / rpath
     if !isfile(fpath)
       @warn """
       \\norg{...}
       Couldn't find a norg file at path
       <$fpath>
       (resolved from '$rpath').
       """
       return Xranklin.failed_lxc("norg", p)
     end

     # here fpath is the full path to an existing literate script
     return _process_norg_file(lc, rpath, fpath)
   end


   function is_meta(ast, node)
     if Norg.kind(node) ≠ Norg.K"Verbatim"
       return false
     end
     c1,c2 = first(children(node), 2)
     Norg.textify(ast, c1) == "document" && Norg.textify(ast, c2) == "meta"
   end

   function parse_meta(ast, metanode)
     d = Dict{Symbol, String}()
     metacontent = Norg.textify(ast, last(children(metanode)))
     for metaline in filter(!isempty, split(metacontent, '\n'))
       k,v = split(metaline, r"\s*:\s*")
       d[Symbol(k)] = v        
     end
     d
   end

   """
   _process_norg_file(rpath, fpath)
   Helper function to process a norg file located at `rpath` (`fpath`).
   We pass `fpath` because it's already been resolved.
   """
   function _process_norg_file(
     lc::Xranklin.LocalContext,
     rpath::String,
     fpath::String
   )::String
     # Step 1: read the .norg file
     s = open(fpath) do io
       read(io, String)
     end

     # Step 2: parse the .norg file
     ast = norg(s)

     # Step 3: dirty trick to parse the `@document.meta` tag.
     metanode = first(filter(x->is_meta(ast, x), collect(PreOrderDFS(ast.root))))
     if !isnothing(metanode)
       metadict = parse_meta(ast, metanode)
       for (k,v) in metadict
         setlvar!(k, v)
       end
     end

     # Step 4: generate HTML
     return string(Norg.codegen(HTMLTarget(), ast))
   end

   
    
CC BY-SA 4.0 "Septimia Zenobia". Last modified: 1.6774109113708124e9. Website built with Franklin.jl and the Julia programming language.