Constexpr.js

What is constexpr?

Constexpr is a feature in programming languages that allows evaluation of parts of code at compile time.

What is constexpr.js?

constexpr.js is a static site generator which doesn't force you to learn a domain specific language. When using this tool, you use javascript and usual DOM manipulation methods to generate the website. The tool will render the page using chrome, and once it has finished rendering, it will save the rendered state of the page as a new html file. This new html file will look exactly like the original page after it has finished rendering. For example, the tool converts this page into this page.

It also strips the constexpr javascript out, potentially reducing download size for the website users drastically. Any piece of javascript code that just generates some html can be made constexpr.

The generated page doesn't have to be completely static. In the above example, the heading is being animated with javascript.

How to use it?

You will have to divide the javascript being used in your page into two groups. Runtime javascript and compile time javascript, and annotate all compile time script tags with constexpr attribute: <script constexpr> ... </script> <script constexpr src="/generate_page.js"></script>
Runtime code must not depend on the compile time code, since that code will be stripped out before writing the output file.

Once the HTML generation code has finished rendering, it must call the window._ConstexprJS_.compile() function. This function is injected into the page by the compiler.

The compiler can be installed like this: npm i -g constexpr.js
Command line usage: constexpr.js --input="<input_directory>" --output="<output_directory>" [--exclusions=path1:path2] [--verbose] [--jobs=n] [--noheadless] [--jobtimeout] [--depfile=<depfile>]
This is what an invocation looks like:

$ ./build.sh Using port:------------------------------------------------- 9046 Found path:------------------------------------------------- /404.html Found path:------------------------------------------------- /_template.html Ignoring path:---------------------------------------------- /demos/constexpr.js/input.html Ignoring path:---------------------------------------------- /demos/constexpr.js/output.html Found path:------------------------------------------------- /index.html Found path:------------------------------------------------- /posts/constexpr.js.html Found path:------------------------------------------------- /posts/intellij_logos.html Found path:------------------------------------------------- /posts/modifying_idea.html Found path:------------------------------------------------- /projects.html Using job count:-------------------------------------------- 5 Using job timeout:------------------------------------------ 999999999 Queued file #1:--------------------------------------------- /404.html Queued file #2:--------------------------------------------- /_template.html Queued file #3:--------------------------------------------- /index.html Queued file #4:--------------------------------------------- /posts/constexpr.js.html Queued file #5:--------------------------------------------- /posts/intellij_logos.html Page /_template.html signalled an abortion, message:-------- "is a template" (1/7) (abortion):------------------------------------------- /_template.html Queued file #6:--------------------------------------------- /posts/modifying_idea.html (2/7) Finished:--------------------------------------------- /index.html Queued file #7:--------------------------------------------- /projects.html (3/7) Finished:--------------------------------------------- /404.html (4/7) Finished:--------------------------------------------- /posts/intellij_logos.html (5/7) Finished:--------------------------------------------- /posts/modifying_idea.html (6/7) Finished:--------------------------------------------- /posts/constexpr.js.html (7/7) Finished:--------------------------------------------- /projects.html Copying resource:------------------------------------------- /static/css/global_styles.css Excluding resource:----------------------------------------- /collections/nav.json Copying resource:------------------------------------------- /static/css/prism.css Excluding resource:----------------------------------------- /static/js/constexpr/third_party/prism.js Excluding resource:----------------------------------------- /collections/posts.json Excluding resource:----------------------------------------- /collections/intellij_logos.json Copying resource:------------------------------------------- /static/img/intellij_logos/2009.09.01.png Copying resource:------------------------------------------- /static/img/intellij_logos/2009.09.09.png Copying resource:------------------------------------------- /static/img/intellij_logos/2009.09.13.png Copying resource:------------------------------------------- /static/img/intellij_logos/2009.11.03.png Copying resource:------------------------------------------- /static/img/intellij_logos/2010.00.11.png Copying resource:------------------------------------------- /static/img/intellij_logos/2010.06.22.png Copying resource:------------------------------------------- /static/img/intellij_logos/2010.10.16.png Copying resource:------------------------------------------- /static/img/intellij_logos/2010.10.30.png Copying resource:------------------------------------------- /static/img/intellij_logos/2011.00.14.png Copying resource:------------------------------------------- /static/img/intellij_logos/2011.06.19.png Copying resource:------------------------------------------- /static/img/intellij_logos/2011.06.22.png Copying resource:------------------------------------------- /static/img/intellij_logos/2011.08.05.png Copying resource:------------------------------------------- /static/img/intellij_logos/2011.10.15.png Copying resource:------------------------------------------- /static/img/intellij_logos/2011.11.01.png Copying resource:------------------------------------------- /static/img/intellij_logos/2012.00.10.png Copying resource:------------------------------------------- /static/img/intellij_logos/2012.03.23.png Copying resource:------------------------------------------- /static/img/intellij_logos/2012.05.27.png Copying resource:------------------------------------------- /static/img/intellij_logos/2012.10.07.png Copying resource:------------------------------------------- /static/img/intellij_logos/2012.10.28.png Copying resource:------------------------------------------- /static/img/intellij_logos/2012.10.30.png Copying resource:------------------------------------------- /static/img/intellij_logos/2012.11.26.png Copying resource:------------------------------------------- /static/img/intellij_logos/2013.00.14.png Copying resource:------------------------------------------- /static/img/intellij_logos/2013.04.15.png Copying resource:------------------------------------------- /static/img/intellij_logos/2013.04.22.png Copying resource:------------------------------------------- /static/img/intellij_logos/2013.10.04.png Copying resource:------------------------------------------- /static/img/intellij_logos/2013.10.27.png Copying resource:------------------------------------------- /static/img/intellij_logos/2014.00.21.png Copying resource:------------------------------------------- /static/img/intellij_logos/2014.01.07.png Copying resource:------------------------------------------- /static/img/intellij_logos/2014.02.05.png Copying resource:------------------------------------------- /static/img/intellij_logos/2014.02.11.png Copying resource:------------------------------------------- /static/img/intellij_logos/2014.03.07.png Copying resource:------------------------------------------- /static/img/intellij_logos/2014.08.28.png Copying resource:------------------------------------------- /static/img/intellij_logos/2014.09.20.png Copying resource:------------------------------------------- /static/img/intellij_logos/2015.03.29.png Copying resource:------------------------------------------- /static/img/intellij_logos/2015.04.13.png Copying resource:------------------------------------------- /static/img/intellij_logos/2015.04.19.png Copying resource:------------------------------------------- /static/img/intellij_logos/2015.08.04.png Copying resource:------------------------------------------- /static/img/intellij_logos/2015.09.21.png Copying resource:------------------------------------------- /static/img/intellij_logos/2015.11.03.png Copying resource:------------------------------------------- /static/img/intellij_logos/2015.11.17.png Copying resource:------------------------------------------- /static/img/intellij_logos/2016.04.11.png Copying resource:------------------------------------------- /static/img/intellij_logos/2016.06.19.png Copying resource:------------------------------------------- /static/img/intellij_logos/2016.11.13.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.03.18.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.05.26.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.05.27.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.05.28.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.05.30.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.08.05.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.08.06.png Copying resource:------------------------------------------- /static/img/intellij_logos/2017.09.24.png Copying resource:------------------------------------------- /static/img/intellij_logos/2018.00.22.png Copying resource:------------------------------------------- /static/img/intellij_logos/2018.04.14.png Copying resource:------------------------------------------- /static/img/intellij_logos/2018.08.08.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.00.14.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.03.04.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.04.07.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.04.10.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.04.29.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.05.08.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.05.12.png Copying resource:------------------------------------------- /static/img/intellij_logos/2019.08.02.png Copying resource:------------------------------------------- /static/img/intellij_logos/2020.00.21.png Copying resource:------------------------------------------- /static/img/intellij_logos/2020.04.21.png Copying resource:------------------------------------------- /static/img/intellij_logos/2020.08.16.png Copying resource:------------------------------------------- /static/img/intellij_logos/2021.00.21.png Copying resource:------------------------------------------- /static/css/pivot.css Copying resource:------------------------------------------- /static/css/asciinema-player.css Copying resource:------------------------------------------- /static/files/tips.csv Copying resource:------------------------------------------- /static/files/build.cast Excluding resource:----------------------------------------- /collections/projects.json ~/src/fctorial.github.io.src $  ~/src/fctorial.github.io.src $    
00:12-00:00
The tool also copies resources (css, images etcetra) that are requested by pages being rendered. HTML files/resources inside paths given in --exclusions are not processed/copied.
A json object with the command line args, compilation results and dependencies will be written to the path specified by --depfile option.

Notes

  1. You can use any web development technology (and any number of technologies) to generate the html without any fear of bloat. Just make sure that window._ConstexprJS_.compile() is called after the page has finished rendering.

    Pivottable.js demo:

    dayFriSatSunThurTotals
    sexsmoker
    FemaleNo6.2535.4246.6161.49149.77
    Yes18.7843.0314.0020.9396.74
    MaleNo5.00104.21133.9658.83302.00
    Yes21.9377.7452.8230.58183.07
    Totals51.96260.40247.39171.83731.58

    This page also uses prism.js for syntax highlighting and asciinema-player for the ./build.sh output, along with jquery and papaparse. A total of 1.1mb of javascript that you don't have to download because it's constexpr.
  2. You can mark tags other than script with constexpr as well. In the above example, the box at the top is marked constexpr, so it isn't present in the output page. This can be used to differentiate original website from generated website: <style constexpr> body { border: 2px solid red; } </style>
  3. Client code in the page can signal the compiler to skip the current file by calling window._ConstexprJS_.abort(message).
  4. In the original webpage, you'll see a console error when the code tries to call the compilation trigger functions, since those functions are injected by the compiler. You can add this snippet near the top to fix that error: <script constexpr> if (!window._ConstexprJS_) { window._ConstexprJS_ = { compile: () => {}, abort: () => {} } } </script>
  5. There might be multiple rendering tasks running in your page. You can manage all those tasks using this refcounting mechanism. All the tasks will call startLoading() when they begin loading, and endLoading() when they've finished loading. The compilation will be triggered when all the tasks have finished.
  6. You should keep all list data separate from the html in json files. constexpr code in the html will fetch these json files and render the page using them. The directory containing this data should be excluded using --exclusions flag, so that the resources inside it aren't copied over.
  7. You can include dev utilites like this in the original website. It reloads the page whenever it's focused. It won't be in the output since it's used as constexpr.
  8. This whole website is rendered using javascript and constexpr.js The html files contains only the page specific stuff. All the styling and theming is done by constexpr code. Nothing other than the demo uses runtime javascript: $ tokei -t=javascript =============================================================================== Language Files Lines Code Comments Blanks =============================================================================== JavaScript 1 2 1 0 1 =============================================================================== Total 1 2 1 0 1 =============================================================================== The original sources can be found here.