ConstexprJS

What is constexpr

It's a feature defined by the C++ standard. Quoting cppreference:

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time. Such variables and functions can then be used where only compile time constant expressions are allowed

So if you create a valid constexpr function in C++, call it with constant values, and assign the result to a constexpr variable, the entire computation will happen at compile time. Other languages also have this feature. It's called const in rust, comptime in zig, macros in lisps that compile, and so on.

What is constexprjs?

It's a static site generator without a DSL or a templating language (liquid, handlebars, haml). Rather, it employs JavaScript to generate HTML, a function it excels at given that this was the primary reason for JavaScript's creation. It executes some of the javascript in your website before deployment. The whole browser runtime is available at your disposal when generating sites with it.

Demo

This whole website is built using constexprjs.

How does it work?

The compiler renders the pages using chrome, and saves the rendered state as new pages when they finish rendering. It also strips out the javascript that was used for generating HTML, potentially reducing download size for the website users drastically. For example, this website is rendered using >100k lines of javascript, none of which you need to download or execute to view this site. You can test this by disabling the javascript in your browser. ❯ tokei static/packages/ static/js/constexpr/ =============================================================================== Language Files Lines Code Comments Blanks =============================================================================== CSS 4 1095 1081 12 2 JavaScript 32 110804 77808 20057 12939 ------------------------------------------------------------------------------- Markdown 1 77 0 53 24 |- HTML 1 15 10 3 2 (Total) 92 10 56 26 =============================================================================== Total 37 111976 78889 20122 12965 =============================================================================== Any piece of javascript code that just generates some HTML can be made constexpr.

The generated pages don't have to be completely static. For example the mobile view navigation, theme switcher, the bottom left viewport, and the analytics system, use javascript.

Setup and usage

The CLI can be installed from npm:

npm i -g constexprjs@latest

Command line usage: $ constexprjs --help usage: constexprjs [-h] [-v] --input INPUT_DIRECTORY --output OUTPUT_DIRECTORY [--entry ENTRYPOINTS] [--skip-resources] [--jobcount JOBCOUNT] [--jobtimeout JOBTIMEOUT] [--depfile DEPFILE] [--headless] [--verbose] Evaluate and strip JS in your website ahead of time optional arguments: -h, --help show this help message and exit -v, --version show program's version number and exit --input INPUT_DIRECTORY Input website root directory --output OUTPUT_DIRECTORY Output directory --entry ENTRYPOINTS Add an HTML file to be used as entry point, paths must be relative to the website root, can be used multiple times, must provide at least one entry point --skip-resources Do not copy resources to the output directory --jobcount JOBCOUNT Number of compilation jobs to run in parallel --jobtimeout JOBTIMEOUT Time in milliseconds for which the compiler will wait for the pages to render --depfile DEPFILE A JSON object containing the command line arguments, file dependency, compilation results will be written to this path --headless Run chrome in headless mode, can be used for running in environments without display server --verbose Enable verbose logging

This is what an invocation looks like:

It also copies resources (css, images etcetra) that are fetched by pages being rendered to the output directory.

See this page for a hello world demo of constexprjs.

Plugins

You can use any web development technology (and any number of technologies) to generate the HTML without any fear of bloat. Pivottable.js demo:

day Fri Sat Sun Thur Totals
sex smoker
Female No 6.25 35.42 46.61 61.49 149.77
Yes 18.78 43.03 14.00 20.93 96.74
Male No 5.00 104.21 133.96 58.83 302.00
Yes 21.93 77.74 52.82 30.58 183.07
Totals 51.96 260.40 247.39 171.83 731.58

Asciinema demo:

This page also uses reactjs for laying out the website, prism.js for syntax highlighting, katex for math formulae, and viz.js for graphs, along with jquery and papaparse. None of which you need to download or execute because it's all constexpr.

Performance

As long as everything is setup correctly (use only local resources in constexpr code, trigger compilation as soon as rendering finishes) compilation times should be very reasonably. In any case compilation time should never be an issue because the generated website will look and work exactly the same as the original website (the one you write) if things are set up properly, so you will only have to run the compiler once per deployment.

Cons

One area in which constexprjs can't compete with other static site generators is ease of use. You cannot use this static site generator if you aren't a web developer. You have to be proficient in javascript, css, and doing things by hand.

However you can think of constexprjs as a tool for building an easy-to-use static site generator. I built one for this website. Now I can write HTML files with bare minimum markup and don't have to worry about the web development aspect of building a website. I can copy over this template and just start writing a page.

Note that you have to write html, css, js when building themes for traditional SSGs as well (Example). Constexprjs just has a higher barrier for entry, it doesn't come with batteries included (all it does is evaluate js in your pages ahead of time), and there is no plugin system. You're supposed to use the standard web dev techniques.

Tips

  1. You can mark tags other than script with constexpr as well. Add this code to your page to differentiate original page from the generated page: <style constexpr> body { border: 2px solid red; } </style> This code will add a red border to your page which will only appear on the original website.
  2. In the original webpage, you'll see a console error when the code tries to call the compilation trigger functions. Because those functions are injected by the compiler. You can add this snippet to fix that error: <script constexpr> if (!window._ConstexprJS_) { window._ConstexprJS_ = { compile: () => {}, abort: () => {}, addPath: () => {}, addExclusion: () => {}, addDependency: () => {}, log: () => {} } } </script>
  3. You should keep all structured data separate from the html in json files. constexpr javascript should fetch these json files and render the site using them.
  4. Use this if you love javascript
  5. Use this if you hate javascript

Roadmap

  • A shell executor function
  • Incremental compilation
  • Make resource collector recognize query/hash strings
  • NPM template for creating a constexprjs project
  • Github action for deploying a ConstexprJS website

See pages tagged with constexprjs for more guides.