summaryrefslogtreecommitdiffstats
path: root/site/cleopatra/soupault.org
blob: dcadb8377dc1f04cebf6355be32cd973fb4dfd19 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
#+BEGIN_EXPORT html
<h1><code>soupault</code> Configuration</h1>
#+END_EXPORT

In a nutshell, the purpose of ~soupault~ is to post-process HTML files generated
by the generation processes of *~cleopatra~*. It is parameterized by two
settings, the ~<<build-dir>> directory where ~soupault~ generates its output,
and an eventual ~<<subdir>>~ wherein the website contents lives. The latter
allows to generate only a subpart of a larger website.

For the present website, these two settings are initialized as follows.

#+NAME: build-dir
#+BEGIN_SRC text
build
#+END_SRC

#+NAME: prefix
#+BEGIN_SRC text
~lthms
#+END_SRC

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.

#+TOC: headlines 2

* ~soupault~ General Settings

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.

We emphasize three things:

- The ~build_dir~ is set to ~<<build-dir>>/<<prefix>>~ in place of simply
  ~<<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.

#+BEGIN_SRC toml :tangle soupault.conf :noweb tangle
[settings]
strict = true
verbose = false
debug = false
site_dir = "site"
build_dir = "<<build-dir>>/<<prefix>>"

page_file_extensions = ["html"]
ignore_extensions = [
  "draft", "vo", "vok", "vos", "glob",
  "html~", "org", "aux", "sass",
]

generator_mode = true
complete_page_selector = "html"
default_template = "templates/main.html"
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

** 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.

#+BEGIN_SRC toml :tangle soupault.conf
[widgets.page-title]
widget = "title"
selector = "h1"
default = "~lthms"
prepend = "~lthms: "
#+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.

#+NAME: soupault-version
#+BEGIN_SRC bash :results verbatim output :exports both
soupault --version | head -n 1 | tr -d '\n'
#+END_SRC

The configuration of the widget ---initially provided by ~soupault~--- becomes
less subject to the obsolescence.

#+BEGIN_SRC toml :tangle soupault.conf :noweb tangle
[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")
#+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

** 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.

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.

#+BEGIN_SRC lua  :tangle plugins/urls-rewriting.lua :noweb no-export
prefix_url = config["prefix_url"]
<<validate_prefix>>

<<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.

#+NAME: validate_prefix
#+BEGIN_SRC lua
if not prefix_url then
  Plugin.fail("Missing mandatory field: `prefix_url'")
end

if not Regex.match(prefix_url, "^/(.*)") then
  prefix_url = "/" .. prefix_url
end

if not Regex.match(prefix_url, "(.*)/$") then
  prefix_url = prefix_url .. "/"
end
#+END_SRC

#+NAME: prefix_func
#+BEGIN_SRC lua
function prefix_urls (links, attr, prefix_url)
  index, link = next(links)

  while index do
    href = HTML.get_attribute(link, attr)

    if href then
      if Regex.match(href, "^/") then
        href = Regex.replace(href, "^/*", "")
        href = prefix_url .. href
      end

      HTML.set_attribute(link, attr, href)
    end
    index, link = next(links, index)
  end
end
#+END_SRC

#+NAME: prefix_calls
#+BEGIN_SRC 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

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.

#+BEGIN_SRC toml :tangle soupault.conf :noweb tangle
[widgets.urls-rewriting]
widget = "urls-rewriting"
prefix_url = "<<prefix>>"
after = "fix-org-urls"
#+END_SRC

** Marking External Links

#+BEGIN_SRC lua :tangle plugins/external-urls.lua
function mark(name)
  return '<i class="url-mark fa fa-' .. name ..
         '" aria-hidden="true"></i>'
end

links = HTML.select(page, "a")

index, link = next(links)

while index do
  href = HTML.get_attribute(link, "href")

  if href then
    if Regex.match(href, "^https?://github.com") then
      icon = HTML.parse(mark('github'))
      HTML.append_child(link, icon)
    elseif Regex.match(href, "^https?://") then
      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"

.url-mark.fa-external-link::before
    content: "\00a0\f08e"
#+END_SRC

#+BEGIN_SRC toml :tangle soupault.conf
[widgets.mark-external-urls]
after = "generate-history"
widget = "external-urls"
#+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.

For instance, considering the following HTML snippet

#+BEGIN_SRC html
<div id="history">
  site/posts/FooBar.org
</div>
#+END_SRC

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

#+NAME: repo
#+BEGIN_SRC text
https://code.soap.coffee/writing/lthms.git
#+END_SRC

#+BEGIN_SRC html :tangle templates/history.html :noweb tangle
<details class="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.
  </p>

  <p>
    You can consult the source of this file in its current
    version <a href="<<repo>>/tree/{{file}}">here</a>.
  </p>

  <table>
  {{#history}}
  <tr>
    <td class="date">{{date}}</a></td>
    <td class="subject">{{subject}}</a></td>
    <td class="commit">
      <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
#history
  table
    @include margin-centered(2rem)
    border-top: 2px solid $primary-color
    border-bottom: 2px solid $primary-color
    width: calc(100% + 4rem)
    border-collapse: collapse;

  td
    border-bottom: 1px solid $primary-color
    padding: .5em
    vertical-align: top

  td.commit
    font-size: smaller

  td.commit
    font-family: 'Fira Code', monospace
    color: $code-fg-color
    font-size: 80%
    white-space: nowrap;
#+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.

#+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

#+BEGIN_TODO
This plugin should be reimplemented using ~libgit2~ or other ~git~ libraries, in
a language more suitable than bash.
#+END_TODO

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~

This translates in Bash like this.

#+BEGIN_SRC bash :tangle scripts/history.sh :shebang "#!/usr/bin/bash"
function main () {
  local file="${1}"
  local template="${2}"

  tmp_file=$(mktemp)
  generate_json ${file} > ${tmp_file}
  haskell-mustache ${template} ${tmp_file}
  rm ${tmp_file}
}
#+END_SRC

The difficult part of this script is the definition of the =generate_json=
function. From a high-level perspective, this function is divided into three
steps.

1. We get an initial (but partial) set of data about the ~git~ commit of
   ~${file}~, from the most recent to the oldest
2. For each commit, we check whether or not ~${file}~ was renamed or not
3. Finally, we output a result (because we are writing a bash script)

#+BEGIN_SRC bash :tangle scripts/history.sh :noweb no-export
function generate_json () {
  local file="${1}"
  local logs=`<<git-log>>`

  if [ ! $? -eq 0 ]; then
      exit 1
  fi

  <<remane-tracking>>

  <<result-echoing>>
}
#+END_SRC

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.
We introduce =_git=, a wrapper around ~git~ with the proper option.

#+BEGIN_SRC bash :tangle scripts/history.sh
function _git () {
  git --no-pager "$@"
}
#+END_SRC

Afterwards, we use =_git= in place of ~git~.

Using the ~git-log~ ~--pretty~ command-line argument, we can generate
one JSON object per commit which contains most of the information we need, using
the following format string.

#+NAME: pretty-format
#+BEGIN_SRC json
{ "subject" : "%s", "abbr_hash" : "%h", "hash" : "%H", "date" : "%cs" }
#+END_SRC

Besides, we also need ~--follow~ 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 initial history is

#+NAME: git-log
#+BEGIN_SRC bash :noweb no-export
_git log -n4 --follow --pretty=format:'<<pretty-format>>' "${file}"
#+END_SRC

To manipulate JSON, we rely on three operators (yet to be defined):

- =jget OBJECT FIELD= ::
  In an =OBJECT=, get the value of a given =FIELD=
- =jset OBJECT FIELD VALIE= ::
  In an =OBJECT=, set the =VALUE= of a given =FIELD=
- =jappend ARRAY VALUE= ::
  Append a =VALUE= at the end of an =ARRAY=

#+NAME: remane-tracking
#+BEGIN_SRC bash :noweb no-export
local name="${file}"
local revisions='[]'

while read -r rev; do
  rev=$(jset "${rev}" "filename" "\"${name}\"")
  revisions=$(jappend "${revisions}" "${rev}")

  local hash=$(jget "${rev}" "hash")
  local rename=$(previous_name "${name}" "${hash}")

  if [[ ! -z "${rename}" ]]; then
      name=${rename}
  fi
done < <(echo "${logs}")
#+END_SRC

#+BEGIN_SRC bash :tangle scripts/history.sh
function previous_name () {
  local name=${1}
  local hash=${2}

  local unfold='s/ *\(.*\){\(.*\) => \(.*\)}/\1\2 => \1\3/'

  _git show --stat=10000 ${hash} \
      | sed -e "${unfold}" \
      | grep "=> ${name}" \
      | xargs \
      | cut -d' ' -f1
}
#+END_SRC

#+NAME: result-echoing
#+BEGIN_SRC bash :noweb no-export
jset "$(jset "{}" "file" "\"${file}\"")" \
     "history" \
     "${revisions}"
#+END_SRC

The last missing pieces are the definitions of the three JSON operators.  We use
[[https://stedolan.github.io/jq/][~jq~]] to manipulate JSON data. Since ~jq~
processes JSON from its standard input, we first define a helper (similar to
=_git=) to deal with JSON from variables seamlessly.

#+BEGIN_SRC bash :tangle scripts/history.sh
function _jq () {
  local input="${1}"
  local filter="${2}"

  echo "${input}" | jq -jcM "${filter}"
}
#+END_SRC

- *-j* tells ~jq~ not to print a new line at the end of its outputs
- *-c* tells ~jq~ to print JSON in a compact format (rather than prettified)
- *-M* tells ~jq~ to output monochrome outputs

Internally, =jget=, =jset=, and =jappend= are implemented with ~jq~
[[https://stedolan.github.io/jq/manual/#Basicfilters][basic filters]].

#+BEGIN_SRC bash :tangle scripts/history.sh
function jget () {
  local obj="${1}"
  local field="${2}"

  _jq "${obj}" ".${field}"
}

function jset () {
  local obj="${1}"
  local field="${2}"
  local val="${3}"

  _jq "${obj}" "setpath([\"${field}\"]; ${val})"
}
function jappend () {
  local arr="${1}"
  local val="${2}"

  _jq "${arr}" ". + [ ${val} ]"
}
#+END_SRC

Everything is defined. We can call =main= now.

#+BEGIN_SRC bash :tangle scripts/history.sh
main "$(cat)" "${1}"
#+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.

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
	@echo "    init  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_TODO
This script should be generalized to handle both display and inline
mode. Currently, only inline mode is supported.
#+END_TODO

#+BEGIN_SRC js :tangle scripts/katex.js
var katex = require("katex");
var fs = require("fs");
var input = fs.readFileSync(0);
var displayMode = process.env.DISPLAY != undefined;

var html = katex.renderToString(String.raw`${input}`, {
    throwOnError : false,
    displayModed : displayMode
});

console.log(html)
#+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.

#+BEGIN_SRC toml :tangle soupault.conf
[widgets.inline-math]
widget = "preprocess_element"
selector = ".imath"
command = "node scripts/katex.js"
action = "replace_content"

[widgets.display-math]
widget = "preprocess_element"
selector = ".dmath"
command = "DISPLAY=1 node scripts/katex.js"
action = "replace_content"
#+END_SRC

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.

#+BEGIN_SRC sass :tangle site/style/plugins.sass
.imath, .dmath
  font-size : smaller

.dmath
  text-align : center
#+END_SRC

* *~cleopatra~* Generation Process Definition

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.

#+BEGIN_SRC makefile :tangle soupault.mk :noweb no-export
<<stages>>
<<dependencies>>
<<ad-hoc-cmds>>
#+END_SRC

In the rest of this section, we define these three components.

** Build Stages

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.

#+NAME: stages
#+BEGIN_SRC makefile :noweb no-export
soupault-build :
	@cleopatra echo Running  soupault
	@soupault

ARTIFACTS += <<build-dir>>/
#+END_SRC

** Dependencies

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.

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]].

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~.

#+NAME: dependencies
#+BEGIN_SRC makefile
theme-build : site/style/plugins.sass
include katex.mk
soupault-build : package-lock.json
#+END_SRC

** Ad-hoc Commands

Finally, this generation process introduces a dedicated (~PHONY~) command to
start a HTTP server in order to navigate the generated website from a browser.

#+NAME: ad-hoc-cmds
#+BEGIN_SRC makefile :noweb no-export
serve :
	@echo "   start  a python server"
	@cd <<build-dir>>; python -m http.server 2>/dev/null

.PHONY : serve
#+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.