diff options
Diffstat (limited to 'site/cleopatra/soupault.org')
-rw-r--r-- | site/cleopatra/soupault.org | 811 |
1 files changed, 381 insertions, 430 deletions
diff --git a/site/cleopatra/soupault.org b/site/cleopatra/soupault.org index 452f442..92042c2 100644 --- a/site/cleopatra/soupault.org +++ b/site/cleopatra/soupault.org @@ -1,209 +1,136 @@ -#+BEGIN_EXPORT html -<h1><code>soupault</code> Configuration</h1> -#+END_EXPORT +#+TITLE: ~soupault~ -#+NAME: build-dir -#+BEGIN_SRC text :exports none -build -#+END_SRC +#+SERIES: ../cleopatra.html +#+SERIES_PREV: ./theme.html +#+SERIES_NEXT: ./commands.html -#+NAME: prefix -#+BEGIN_SRC text :exports none -~lthms -#+END_SRC +We use ~soupault~ to build this website[fn::~soupault~ is an awesome +free software project, with a unique approach to static website +generation. You should definitely [[https://soupault.app][check out their website]]!]. + +#+begin_export html +<nav id="generate-toc"></nav> +#+end_export -In a nutshell, the purpose of ~soupault~ is to post-process HTML files -generated by the generation processes of *~cleopatra~* +* Installation -The rest of this document proceeds as follows. We first describe the general -settings of ~soupault~. Then, we enumerate the widgets enabled for this website. -Finally, we provide a proper definition for ~soupault~ the *~cleopatra~* -generation process. + We install ~soupault~ in a local switch. We use a witness file + ~_opam/.init~ to determine whether or not our switch has always been + created during a previous invocation of *~cleopatra~*. -#+TOC: headlines 2 + #+begin_src makefile :tangle soupault.mk +OCAML_VERSION := 4.11.2 +OCAML := ocaml-base-compiler.${OCAML_VERSION} -* ~soupault~ General Settings +CONFIGURE += _opam rss.json +ARTIFACTS += out -The general ~settings~ section of ~soupault.conf~ is fairly basic, and there is -little to say that the -[[https://soupault.neocities.org/reference-manual/#getting-started][“Getting -Started”]] already discuss in length. +soupault-prebuild : _opam/init + #+end_src -We emphasize three things: + Using ~soupault~ is as simple as calling it, without any particular + command-line arguments. + + #+begin_src makefile :tangle soupault.mk +soupault-build : package-lock.json style.min.css + @cleopatra echo "Executing" "soupault" + @soupault + #+end_src -- The ~build_dir~ is set to src_text[:exports code :noweb yes]{<<build-dir>>/<<prefix>>} - in place of simply src_text[:exports code :noweb yes]{<<build-dir>>}. -- The ~ignore_extensions~ shall be updated to take into account artifacts - produces by other *~cleopatra~* generation processes. -- We disable the “clean URLs” feature of ~soupault. This option renames - a HTML files ~foo/bar.html~ into ~foo/bar/index.html~, which means when served - by a HTTP server, the ~foo/bar~ URL will work. The issue we have with this - feature is that the internal links within your websiste needs to take their - /final/ URL into account, rather than their actual name. If one day ~soupault~ - starts rewriting internal URLs when ~clean_url~ is enabled, we might - reconsider using it. + We now describe our configuration file for ~soupault~. + +* Configuration + + #+name: base-dir + #+begin_src verbatim :noweb yes :exports none +~lthms + #+end_src -#+BEGIN_SRC toml :tangle soupault.conf :noweb yes +** Global Settings + + The options of the ~[settings]~ section of a ~soupault~ + configuration are often self-explanatory, and we do not spend too + much time to detaul them. + + #+begin_src toml :tangle soupault.conf :noweb yes [settings] strict = true -verbose = false -debug = false site_dir = "site" -build_dir = "<<build-dir>>/<<prefix>>" - +build_dir = "out/<<base-dir>>" +doctype = "<!DOCTYPE html>" +clean_urls = false +generator_mode = true +complete_page_selector = "html" +default_content_selector = "main" page_file_extensions = ["html"] ignore_extensions = [ - "draft", "vo", "vok", "vos", "glob", - "html~", "org", "aux", "sass", + "v", "vo", "vok", "vos", "glob", + "html~", "org" ] - -generator_mode = true -complete_page_selector = "html" default_template_file = "templates/main.html" -default_content_selector = "main" -doctype = "<!DOCTYPE html>" -clean_urls = false -#+END_SRC - -#+BEGIN_TODO -The list of ignored extensions should be programmatically generated with the -help of *~cleopatra~*. -#+END_TODO - -* Widgets +pretty_print_html = false + #+end_src ** Setting Page Title -We use the “page title” widget to set the title of the webpage based on the -first (and hopefully the only) ~<h1>~ tag of the page. + We use the “page title” widget to set the title of the webpage + based on the first (and hopefully the only) ~<h1>~ tag of the + page. -#+BEGIN_SRC toml :tangle soupault.conf + #+begin_src toml :tangle soupault.conf [widgets.page-title] widget = "title" selector = "h1" default = "~lthms" prepend = "~lthms: " -#+END_SRC + #+end_src ** Acknowledging ~soupault~ -When creating a new ~soupault~ project (using ~soupault --init~), the default -configuration file suggests advertising the use of ~soupault~. Rather than -hard-coding the used version of ~soupault~ (which is error-prone), we rather -determine the version of ~soupault~ with the following script. + When creating a new ~soupault~ project (using ~soupault --init~), + the default configuration file suggests advertising the use of + ~soupault~. Rather than hard-coding the used version of ~soupault~ + (which is error-prone), we rather determine the version of + ~soupault~ with the following script. -#+NAME: soupault-version -#+BEGIN_SRC bash :results verbatim output + #+NAME: soupault-version + #+begin_src bash :results verbatim output soupault --version | head -n 1 | tr -d '\n' -#+END_SRC + #+end_src -The configuration of the widget ---initially provided by ~soupault~--- becomes -less subject to the obsolescence. + The configuration of the widget ---initially provided by + ~soupault~--- becomes less subject to the obsolescence[fn::That + is, as long as ~soupault~ does not change the output of its + ~--version~ option.]. -#+BEGIN_SRC toml :tangle soupault.conf :noweb yes + #+begin_src toml :tangle soupault.conf :noweb yes [widgets.generator-meta] widget = "insert_html" html = """<meta name="generator" content="<<soupault-version()>>">""" selector = "head" -#+END_SRC - -** Generating Table of Contents - -The ~toc~ widget allows for generating a table of contents for HTML files which -contains a node matching a given ~selector~ (in the case of this document, -~#generate-toc~). - -#+BEGIN_SRC toml :tangle soupault.conf -[widgets.table-of-contents] -widget = "toc" -selector = "#generate-toc" -action = "replace_element" -valid_html = true -min_level = 2 -numbered_list = true -#+END_SRC - -#+BEGIN_TODO -We could propose a patch to ~soupault~'s upstream to add numbering in titles. -#+END_TODO - -** Fixing Org Internal Links - -For some reason, Org prefix internal links to other Org documents with -~file://~. To avoid that, we provide a simple plugin which removes ~file://~ -from the begining of a URL. - -#+BEGIN_TODO -This plugin definition should be part of [[./Contents/Org.org][the ~org~ -generation process]], but that would require to aggregate “subconfig” into a -larger one. -#+END_TODO - -This plugin key component is the =fix_org_urls= function. - -- =fix_org_urls(LIST, ATTR)= :: - Enumerate the DOM elements of =LIST=, and check their =ATTR= attribute. - -#+BEGIN_SRC lua :tangle plugins/fix-org-urls.lua -function fix_org_urls(list, attr) - index, link = next(list) - - while index do - href = HTML.get_attribute(link, attr) - - if href then - href = Regex.replace(href, "^file://", "") - HTML.set_attribute(link, attr, href) - end - - index, link = next(list, index) - end -end -#+END_SRC - -We use this function to fix the URLs of tags known to be subject to Org strange -behavior. For now, only ~<a>~ has been affected. - -#+BEGIN_SRC lua :tangle plugins/fix-org-urls.lua -fix_org_urls(HTML.select(page, "a"), "href") -fix_org_urls(HTML.select(page, "img"), "src") -#+END_SRC - -The configuration of this plugin, and the associated widget, is straightforward. - -#+BEGIN_SRC toml :tangle soupault.conf :noweb tangle -[widgets.fix-org-urls] -widget = "fix-org-urls" -#+END_SRC + #+end_src ** Prefixing Internal URLs -On the one hand, internal links can be absolute, meaning they start with a -leading ~/~, and therefore are relative to the website root. On the other hand, -website (especially static website) can be placed in larger context. For -instance, my personal website lives inside the ~~lthms~ directory of the -~soap.coffee~ domain. + On the one hand, internal links can be absolute, meaning they + start with a leading ~/~, and therefore are relative to the + website root. On the other hand, website (especially static + website) can be placed in larger context. For instance, my + personal website lives inside the ~~lthms~ directory of the + ~soap.coffee~ domain[fn::To my experience in hosting webapps and + websites, this set-up is way harder to get right than I initially + expect.]. -The purpose of this plugin is to rewrite internal URLs which are relative to the -root, in order to properly prefix them. + The purpose of this plugin is to rewrite internal URLs which are relative to the + root, in order to properly prefix them. -From a high-level perspective, the plugin structure is the following. + From a high-level perspective, the plugin structure is the following. -#+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua :noweb no-export -prefix_url = config["prefix_url"] -<<validate_prefix>> + First, we validate the widget configuration. -<<prefix_func>> -<<prefix_calls>> -#+END_SRC - -1. We validate the widget configuration. -2. We propose a generic function to enumerate and rewrite tags which can have - internal URLs as attribute argument. -3. We use this generic function for relevant tags. + #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua +prefix_url = config["prefix_url"] -#+NAME: validate_prefix -#+BEGIN_SRC lua if not prefix_url then Plugin.fail("Missing mandatory field: `prefix_url'") end @@ -215,10 +142,12 @@ end if not Regex.match(prefix_url, "(.*)/$") then prefix_url = prefix_url .. "/" end -#+END_SRC + #+END_SRC + + Then, we propose a generic function to enumerate and rewrite tags + which can have. -#+NAME: prefix_func -#+BEGIN_SRC lua + #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua function prefix_urls (links, attr, prefix_url) index, link = next(links) @@ -236,33 +165,35 @@ function prefix_urls (links, attr, prefix_url) index, link = next(links, index) end end -#+END_SRC + #+END_SRC -#+NAME: prefix_calls -#+BEGIN_SRC lua + Finally, we use this generic function for relevant tags. + + #+BEGIN_SRC lua :tangle plugins/urls-rewriting.lua prefix_urls(HTML.select(page, "a"), "href", prefix_url) prefix_urls(HTML.select(page, "link"), "href", prefix_url) prefix_urls(HTML.select(page, "img"), "src", prefix_url) prefix_urls(HTML.select(page, "script"), "src", prefix_url) -#+END_SRC +prefix_urls(HTML.select(page, "use"), "href", prefix_url) + #+END_SRC -Again, configuring soupault to use this plugin is relatively straightforward. -The only important thing to notice is the use of the ~after~ field, to ensure -this plugin is run /after/ the plugin responsible for fixing Org documents URLs. + Again, configuring soupault to use this plugin is relatively + straightforward. -#+BEGIN_SRC toml :tangle soupault.conf :noweb tangle + #+BEGIN_SRC toml :tangle soupault.conf :noweb yes [widgets.urls-rewriting] widget = "urls-rewriting" -prefix_url = "<<prefix>>" -after = "fix-org-urls" -#+END_SRC +prefix_url = "<<base-dir>>" +after = "mark-external-urls" + #+END_SRC ** Marking External Links -#+BEGIN_SRC lua :tangle plugins/external-urls.lua + #+BEGIN_SRC lua :tangle plugins/external-urls.lua function mark(name) - return '<i class="url-mark fa fa-' .. name .. - '" aria-hidden="true"></i>' + return '<span class="icon"><svg><use href="/img/icons.svg#' + .. name .. + '"></use></svg></span>' end links = HTML.select(page, "a") @@ -274,85 +205,101 @@ while index do if href then if Regex.match(href, "^https?://github.com") then - icon = HTML.parse(mark('github')) + icon = HTML.parse(mark("github")) HTML.append_child(link, icon) elseif Regex.match(href, "^https?://") then - icon = HTML.parse(mark('external-link')) + icon = HTML.parse(mark("external-link")) HTML.append_child(link, icon) end end index, link = next(links, index) end -#+END_SRC - -#+BEGIN_SRC sass :tangle site/style/plugins.sass -.url-mark.fa - display: inline - font-size: 90% - width: 1em - -.url-mark.fa-github::before - content: "\00a0\f09b" + #+END_SRC -.url-mark.fa-external-link::before - content: "\00a0\f08e" -#+END_SRC - -#+BEGIN_SRC toml :tangle soupault.conf + #+BEGIN_SRC toml :tangle soupault.conf [widgets.mark-external-urls] after = "generate-history" widget = "external-urls" -#+END_SRC + #+END_SRC + +** Generating a Table of Contents + + The ~toc~ widget allows for generating a table of contents for + HTML files which contains a node matching a given ~selector~ (in + the case of this document, ~#generate-toc~). + + #+begin_src toml :tangle soupault.conf +[widgets.table-of-contents] +widget = "toc" +selector = "#generate-toc" +action = "replace_content" +valid_html = true +min_level = 2 +max_level = 3 +numbered_list = false +heading_links = true +heading_link_text = " §" +heading_links_append = true +heading_link_class = "anchor-link" + +[widgets.append-toc-title] +widget = "preprocess_element" +selector = "#generate-toc" +command = 'echo "<h2>Table of Contents</h2> $(cat)"' +after = "table-of-contents" + #+end_src ** Generating Per-File Revisions Tables *** Users Instructions -This widgets allows to generate a so-called “revisions table” of the filename -contained in a DOM element of id ~history~, based on its history. Paths should -be relative to the directory from which you start the build process (typically, -the root of your repository). The revisions table notably provides hyperlinks to -a ~git~ webview for each commit. + This widgets allows to generate a so-called “revisions table” of + the filename contained in a DOM element of id ~history~, based on + its history. Paths should be relative to the directory from which + you start the build process (typically, the root of your + repository). The revisions table notably provides hyperlinks to a + ~git~ webview for each commit. -For instance, considering the following HTML snippet + For instance, considering the following HTML snippet -#+BEGIN_SRC html + #+begin_src html <div id="history"> site/posts/FooBar.org </div> -#+END_SRC + #+end_src -This plugin will replace the content of this ~<div>~ with the revisions table of -~site/posts/FooBar.org~. + This plugin will replace the content of this ~<div>~ with the + revisions table of ~site/posts/FooBar.org~. *** Customization -The base of the URL webview for the document you are currently reading -—afterwards abstracted with the ~<<repo>>~ noweb reference— is + The base of the URL webview for the document you are currently + reading is src_verbatim[:noweb yes :exports code]{<<repo>>}. -#+NAME: repo -#+BEGIN_SRC text + #+name: repo + #+begin_src verbatim :exports none https://code.soap.coffee/writing/lthms.git -#+END_SRC + #+end_src -#+BEGIN_SRC html :tangle templates/history.html :noweb tangle -<details class="history"> + The template used to generate the revision table is the following. + + #+begin_src html :tangle templates/history.html :noweb yes +<details id="history"> <summary>Revisions</summary> <p> This revisions table has been automatically generated - from <a href="<<repo>>">the <code>git</code> history - of this website repository</a>, and the change - descriptions may not always be as useful as they - should. + from <a href="<<repo>>">the + <code>git</code> history of this website repository</a>, and the + change descriptions may not always be as useful as they should. </p> <p> - You can consult the source of this file in its current - version <a href="<<repo>>/tree/{{file}}">here</a>. + You can consult the source of this file in its current version + <a href="<<repo>>/tree/{{file}}">here</a>. </p> - <table> + <table class="fullwidth"> {{#history}} <tr> <td class="date" @@ -362,66 +309,44 @@ https://code.soap.coffee/writing/lthms.git {{#modified}} id="modified-at" {{/modified}} - > - {{date}} - </td> + >{{date}}</td> <td class="subject">{{subject}}</td> <td class="commit"> - <a href="<<repo>>/commit/{{filename}}/?id={{hash}}"> - {{abbr_hash}} - </a> + <a href="<<repo>>/commit/{{filename}}/?id={{hash}}">{{abbr_hash}}</a> </td> </tr> {{/history}} </table> </details> -#+END_SRC - -#+BEGIN_SRC sass :tangle site/style/plugins.sass -table - border-top : 2px solid black - border-bottom : 2px solid black - border-collapse : collapse - width : 35rem - -td - border-bottom : 1px solid black - padding : .5em - -#history .commit - font-size : smaller - font-family : 'Fira Code', monospace - width : 7em - text-align : center -#+END_SRC + #+end_src *** Implementation -We use the built-in [[https://soupault.neocities.org/reference-manual/#widgets-preprocess-element][=preprocess_element=]] to implement, which means we need a -script which gets its input from the standard input, and echoes its output to -the standard input. + We use the built-in [[https://soupault.neocities.org/reference-manual/#widgets-preprocess-element][=preprocess_element=]] to implement, which + means we need a script which gets its input from the standard + input, and echoes its output to the standard input. -#+BEGIN_SRC toml :tangle soupault.conf + #+begin_src toml :tangle soupault.conf [widgets.generate-history] widget = "preprocess_element" selector = "#history" command = 'scripts/history.sh templates/history.html' -action = "replace_content" -#+END_SRC +action = "replace_element" + #+end_src -This plugin proceeds as follows: + This plugin proceeds as follows: -1. Using an ad-hoc script, it generates a JSON containing for each revision - - The subject, date, hash, and abbreviated hash of the related commit - - The name of the file at the time of this commit -2. This JSON is passed to a mustache engine (~haskell-mustache~) with a - proper template -3. The content of the selected DOM element is replaced with the output of - ~haskell-mustache~ + 1. Using an ad-hoc script, it generates a JSON containing for each revision + - The subject, date, hash, and abbreviated hash of the related commit + - The name of the file at the time of this commit + 2. This JSON is passed to a mustache engine (~haskell-mustache~) with a + proper template + 3. The content of the selected DOM element is replaced with the output of + ~haskell-mustache~ -This translates in Bash like this. + This translates in Bash like this. -#+BEGIN_SRC bash :tangle scripts/history.sh :shebang "#!/usr/bin/bash" + #+begin_src bash :tangle scripts/history.sh :shebang "#!/usr/bin/bash" function main () { local file="${1}" local template="${2}" @@ -431,26 +356,27 @@ function main () { haskell-mustache ${template} ${tmp_file} rm ${tmp_file} } -#+END_SRC - -Generating the expected JSON is therefore as simple as: - -- Fetching the logs -- Reading 8 line from the logs, parse the filename from the 6th - line -- Outputing the JSON - -We will use ~git~ to get the information we need. By default, ~git~ -subcommands use a pager when its output is likely to be long. This -typically includes ~git-log~. To disable this behavior, ~git~ exposes -the ~--no-pager~ command. Besides, we also need ~--follow~ and -~--stat~ to deal with file renaming. Without this option, ~git-log~ -stops when the file first appears in the repository, even if this -“creation” is actually a renaming. Therefore, the ~git~ command line -we use to collect our history is - -#+NAME: gitlog -#+BEGIN_SRC bash :tangle scripts/history.sh :noweb yes + #+end_src + + Generating the expected JSON is therefore as simple as: + + - Fetching the logs + - Reading 8 line from the logs, parse the filename from the 6th + line + - Outputing the JSON + + We will use ~git~ to get the information we need. By default, + ~git~ subcommands use a pager when its output is likely to be + long. This typically includes ~git-log~. To disable this + behavior, ~git~ exposes the ~--no-pager~ command. Besides, we + also need ~--follow~ and ~--stat~ to deal with file + renaming. Without this option, ~git-log~ stops when the file + first appears in the repository, even if this “creation” is + actually a renaming. Therefore, the ~git~ command line we use to + collect our history is + + #+name: gitlog + #+begin_src bash :tangle scripts/history.sh :noweb yes function gitlog () { local file="${1}" git --no-pager log \ @@ -459,33 +385,35 @@ function gitlog () { --pretty=format:'%s%n%h%n%H%n%cs%n' \ "${file}" } -#+END_SRC + #+end_src -This function will generate a sequence of 8 lines containing all the -relevant information we are looking for, for each commit, namely: + This function will generate a sequence of 8 lines containing all + the relevant information we are looking for, for each commit, + namely: -- Subject -- Abbreviated hash -- Full hash -- Date -- Empty line -- Change summary -- Shortlog -- Empty line + - Subject + - Abbreviated hash + - Full hash + - Date + - Empty line + - Change summary + - Shortlog + - Empty line -For instance, the =gitlog= function will output the following lines -for the last commit of this very file: + For instance, the =gitlog= function will output the following + lines for the last commit of this very file: -#+BEGIN_SRC bash :results verbatim :exports results :noweb yes + #+begin_src bash :results verbatim :exports results :noweb yes <<gitlog>> gitlog "soupault.org" | head -n8 -#+END_SRC + #+end_src -Among other things, the 6th line contains the filename. We need to -extract it, and we do that with ~sed~. In case of file renaming, we -need to parse something of the form ~both/to/{old => new}~. + Among other things, the 6th line contains the filename. We need + to extract it, and we do that with ~sed~. In case of file + renaming, we need to parse something of the form ~both/to/{old => + new}~. -#+BEGIN_SRC bash :tangle scripts/history.sh :noweb yes + #+begin_src bash :tangle scripts/history.sh :noweb yes function parse_filename () { local line="${1}" local shrink='s/ *\(.*\) \+|.*/\1/' @@ -493,18 +421,18 @@ function parse_filename () { echo ${line} | sed -e "${shrink}" | sed -e "${unfold}" } -#+END_SRC - -The next step is to process the logs to generate the expected JSON. We -have to deal with the fact that JSON does not allow the last item of -an array to be concluded by ",". Besides, we also want to indicate -which commit is responsible for the creation of the file. To do that, -we use two variables: =idx= and =last_entry=. When =idx= is equal to -0, we know it is the latest commit. When =idx= is equal to -=last_entry=, we know we are looking at the oldest commit for that -file. - -#+BEGIN_SRC bash :tangle scripts/history.sh :noweb yes + #+end_src + + The next step is to process the logs to generate the expected + JSON. We have to deal with the fact that JSON does not allow the + last item of an array to be concluded by ",". Besides, we also + want to indicate which commit is responsible for the creation of + the file. To do that, we use two variables: =idx= and + =last_entry=. When =idx= is equal to 0, we know it is the latest + commit. When =idx= is equal to =last_entry=, we know we are + looking at the oldest commit for that file. + + #+begin_src bash :tangle scripts/history.sh :noweb yes function generate_json () { local input="${1}" local logs="$(gitlog ${input})" @@ -562,11 +490,11 @@ function generate_json () { echo -n "]}" } -#+END_SRC + #+end_src -Generating the JSON object for a given commit is as simple as + Generating the JSON object for a given commit is as simple as -#+BEGIN_SRC bash :tangle scripts/history.sh :noweb yes + #+begin_src bash :tangle scripts/history.sh :noweb yes function output_json_entry () { local subject="${1}" local abbr_hash="${2}" @@ -585,66 +513,31 @@ function output_json_entry () { echo -n ",\"filename\":\"${file}\"" echo -n "}" } -#+END_SRC + #+end_src -And we are done! We can safely call the =main= function to generate -our revisions table. + And we are done! We can safely call the =main= function to generate + our revisions table. -#+BEGIN_SRC bash :tangle scripts/history.sh + #+begin_src bash :tangle scripts/history.sh main "$(cat)" "${1}" -#+END_SRC + #+end_src ** Rendering Equations Offline *** Users instructions -Inline equations written in the DOM under the class src_css{.imath} and using -the \im \LaTeX \mi syntax can be rendered once and -for all by ~soupault~. User For instance, ~<span class="imath">\LaTeX</span>~ is -rendered \im \LaTeX \mi as expected. + Inline equations written in the DOM under the class + src_css{.imath} and using the \im \LaTeX \mi syntax can be + rendered once and for all by ~soupault~. User For instance, + ~<span class="imath">\LaTeX</span>~ is rendered \im \LaTeX \mi as + expected. -Using this widgets requires being able to inject raw HTML in input files. + Using this widgets requires being able to inject raw HTML in + input files. *** Implementation -We will use [[https://katex.org][\im \KaTeX \mi]] to render equations offline. \im \KaTeX \mi -availability on most systems is unlikely, but it is part of [[https://www.npmjs.com/package/katex][npm]], so we can -define a minimal ~package.json~ file to fetch it automatically. - -#+BEGIN_SRC json :tangle package.json -{ - "private": true, - "devDependencies": { - "katex": "^0.11.1" - } -} -#+END_SRC - -We introduce a Makefile recipe to call ~npm install~. This command produces a -file called ~package-lock.json~ that we add to ~GENFILES~ to ensure \im \KaTeX -\mi will be available when ~soupault~ is called. - -If ~Soupault.org~ has been modified since the last generation, Babel will -generate ~package.json~ again. However, if the modifications of ~Soupault.org~ -do not concern ~package.json~, then ~npm install~ will not modify -~package-lock.json~ and its “last modified” time will not be updated. This means -that the next time ~make~ will be used, it will replay this recipe again. As a -consequence, we systematically ~touch~ ~packase-lock.json~ to satisfy ~make~. - -#+BEGIN_SRC makefile :tangle katex.mk -package-lock.json : package.json - @cleopatra echo "Fetching" "npm packages" - @npm install &>> build.log - @touch $@ - -CONFIGURE += package-lock.json node_modules/ -#+END_SRC - -Once installed and available, \im \KaTeX \mi is really simple to use. The -following script reads (synchronously!) the standard input, renders it using \im -\KaTeX \mi and outputs the resut to the standard output. - -#+BEGIN_SRC js :tangle scripts/katex.js + #+begin_src js :tangle scripts/render-equations.js var katex = require("katex"); var fs = require("fs"); var input = fs.readFileSync(0); @@ -656,102 +549,160 @@ var html = katex.renderToString(String.raw`${input}`, { }); console.log(html) -#+END_SRC + #+end_src -We reuse once again the =preprocess_element= widget. The selector is ~.imath~ -(~i~ stands for inline in this context), and we replace the previous content -with the result of our script. + We reuse once again the =preprocess_element= widget. The selector + is ~.imath~ (~i~ stands for inline in this context), and we + replace the previous content with the result of our script. -#+BEGIN_SRC toml :tangle soupault.conf + #+begin_src toml :tangle soupault.conf [widgets.inline-math] widget = "preprocess_element" selector = ".imath" -command = "node scripts/katex.js" +command = "node scripts/render-equations.js" action = "replace_content" [widgets.display-math] widget = "preprocess_element" selector = ".dmath" -command = "DISPLAY=1 node scripts/katex.js" +command = "DISPLAY=1 node scripts/render-equations.js" action = "replace_content" -#+END_SRC + #+end_src + +** RSS Feed + + #+begin_src toml :tangle soupault.conf +[index] +index = true +dump_json = "rss.json" +extract_after_widgets = ["urls-rewriting"] + +[index.fields] +title = { + selector = ["h1"] +} -The \im\KaTeX\mi font is bigger than the serif font used for this -website, so we reduce it a bit with a dedicated SASS rule. +modified-at = { + selector = ["#modified-at"] +} -#+BEGIN_SRC sass :tangle site/style/plugins.sass -.imath, .dmath - font-size : smaller +created-at = { + selector = ["#created-at"] +} + #+end_src -.dmath - text-align : center -#+END_SRC +** Series Navigation -* *~cleopatra~* Generation Process Definition + #+begin_src lua :tangle plugins/series.lua +function get_title_from_path (path) + if Sys.is_file(path) then + local content_raw = Sys.read_file(path) + local content_dom = HTML.parse(content_raw) + local title = HTML.select_one(content_dom, "h1") -We introduce the ~soupault~ generation process, obviously based on the -[[https://soupault.neocities.org/][~soupault~ HTML processor]]. The structure of -a *~cleopatra~* generation process is always the same. + if title then + return String.trim(HTML.inner_html(title)) + else + Plugin.fail(path .. ' has no <h1> tag') + end + else + Plugin.fail(path .. ' is not a file') + end +end + #+end_src + + #+begin_src lua :tangle plugins/series.lua +function generate_nav_item_from_title (title, url, template) + local env = {} + env["url"] = url + env["title"] = title + local new_content = String.render_template(template, env) + return HTML.parse(new_content) +end + #+end_src -#+BEGIN_SRC makefile :tangle soupault.mk :noweb no-export -<<stages>> -<<dependencies>> -<<ad-hoc-cmds>> -#+END_SRC + #+begin_src lua :tangle plugins/series.lua +function generate_nav_items (cwd, cls, template) + local elements = HTML.select(page, cls) -In the rest of this section, we define these three components. + local i = 1 + while elements[i] do + local element = elements[i] + local url = HTML.strip_tags(element) + local path = Sys.join_path(cwd, url) + local title_str = get_title_from_path(path) -** Build Stages + HTML.replace_content( + element, + generate_nav_item_from_title(title_str, url, template) + ) -From the perspective of *~cleopatra~*, it is a rather simple component, since -the ~build~ stage is simply a call to ~soupault~, whose outputs are located in a -single (configurable) directory. + i = i + 1 + end +end + #+end_src -#+NAME: stages -#+BEGIN_SRC makefile :noweb yes -soupault-build : - @cleopatra echo Running soupault - @soupault + #+begin_src lua :tangle plugins/series.lua +cwd = Sys.dirname(page_file) -ARTIFACTS += <<build-dir>>/ -#+END_SRC +home_template = 'This article is part of the series “<a href="{{ url }}">{{ title }}</a>.”' +nav_template = '<a href="{{ url }}">{{ title }}</a>' -** Dependencies +generate_nav_items(cwd, ".series", home_template) +generate_nav_items(cwd, ".series-prev", nav_template) +generate_nav_items(cwd, ".series-next", nav_template) + #+end_src -Most of the generation processes (if not all of them) need to declare themselves -as a prerequisite for ~soupault-build~. If they do not, they will likely be -executed after ~soupault~ is called. +#+begin_src toml :tangle soupault.conf +[widgets.series] +widget = "series" +#+end_src -This file defines an auxiliary SASS sheet that needs to be declared as a -dependency of the build stage of the [[./Theme.org][~theme~ generation -process]]. +** Injecting Minified CSS -Finally, the offline rendering of equations requires \im \KaTeX \mi to be -available, so we include the ~katex.mk~ file, and make ~package-lock.json~ (the -proof that ~npm install~ has been executed) a prerequisite of ~soupault-build~. + #+begin_src lua :tangle plugins/css.lua +style = HTML.select_one(page, "style") -#+NAME: dependencies -#+BEGIN_SRC makefile -theme-build : site/style/plugins.sass -include katex.mk -soupault-build : package-lock.json -#+END_SRC +if style then + css = HTML.create_text(Sys.read_file("style.min.css")) + HTML.replace_content(style, css) +end + #+end_src -** Ad-hoc Commands + #+begin_src toml :tangle soupault.conf +[widgets.css] +widget = "css" + #+end_src -Finally, this generation process introduces a dedicated (~PHONY~) command to -start a HTTP server in order to navigate the generated website from a browser. +** Cleaning-up -#+NAME: ad-hoc-cmds -#+BEGIN_SRC makefile :noweb yes -serve : - @echo " start a python server" - @cd <<build-dir>>; python -m http.server 2>/dev/null + #+begin_src lua :tangle plugins/clean-up.lua +function remove_if_empty(html) + if String.trim(HTML.inner_html(html)) == "" then + HTML.delete(html) + end +end + #+end_src + + #+begin_src lua :tangle plugins/clean-up.lua +function remove_all_if_empty(cls) + local elements = HTML.select(page, cls) + + local i = 1 + while elements[i] do + local element = elements[i] + remove_if_empty(element) + i = i + 1 + end +end + #+end_src -.PHONY : serve -#+END_SRC + #+begin_src lua :tangle plugins/clean-up.lua +remove_all_if_empty("p") -- introduced by org-mode +remove_all_if_empty("div.code") -- introduced by coqdoc + #+end_src -This command does not assume anything about the current state of generation of -the project. In particular, it does not check whether or not the ~<<build-dir>>~ -directory exists. The responsibility to use ~make serve~ in a good setting lies -with final users. +#+begin_src toml :tangle soupault.conf +[widgets.clean-up] +widget = "clean-up" +#+end_src |