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
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.
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:
Command line usage:
This is what an invocation looks like:
It also copies resources (
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:
$ constexprjs --input=. --output=_out --entry /index.html --jobcount 48 --depfile devtools/deps.json --verbose Queued file #1:--------------------------------------------- /index.html /index.html added extra path /projects.html to be generated using /projects.html /index.html added extra path /tags.html to be generated using /tags.html /index.html added extra path /404.html to be generated using /404.html /index.html added extra path /about.html to be generated using /about.html /index.html added extra path /posts/constexprjs.html to be generated using /posts/constexprjs.html /index.html added extra path /posts/constexprjs_hello_world.html to be generated using /posts/constexprjs_hello_world.html /index.html added extra path /posts/constexprjs_entry_points.html to be generated using /posts/constexprjs_entry_points.html /index.html added extra path /posts/constexprjs_dependency_resolution.html to be generated using /posts/constexprjs_dependency_resolution.html /index.html added extra path /posts/constexprjs_api_docs.html to be generated using /posts/constexprjs_api_docs.html /index.html added extra path /posts/constexprjs_code_organization.html to be generated using /posts/constexprjs_code_organization.html /index.html added extra path /posts/constexprjs_syntax_highlighting.html to be generated using /posts/constexprjs_syntax_highlighting.html /index.html added extra path /posts/clipboard_cli.html to be generated using /posts/clipboard_cli.html /index.html added extra path /posts/dfbhd_mus.html to be generated using /posts/dfbhd_mus.html /index.html added extra path /posts/dfbhd_sbf.html to be generated using /posts/dfbhd_sbf.html (1/15) Finished:-------------------------------------------- /index.html Queued file #2:--------------------------------------------- /projects.html Queued file #3:--------------------------------------------- /tags.html Queued file #4:--------------------------------------------- /404.html Queued file #5:--------------------------------------------- /about.html Queued file #6:--------------------------------------------- /posts/constexprjs.html Queued file #7:--------------------------------------------- /posts/constexprjs_hello_world.html Queued file #8:--------------------------------------------- /posts/constexprjs_entry_points.html Queued file #9:--------------------------------------------- /posts/constexprjs_dependency_resolution.html Queued file #10:-------------------------------------------- /posts/constexprjs_api_docs.html Queued file #11:-------------------------------------------- /posts/constexprjs_code_organization.html Queued file #12:-------------------------------------------- /posts/constexprjs_syntax_highlighting.html Queued file #13:-------------------------------------------- /posts/clipboard_cli.html Queued file #14:-------------------------------------------- /posts/dfbhd_mus.html Queued file #15:-------------------------------------------- /posts/dfbhd_sbf.html /tags.html added extra path /tags/constexprjs.html to be generated using /tags/generator.html?constexprjs /tags.html added extra path /tags/web.html to be generated using /tags/generator.html?web /tags.html added extra path /tags/games.html to be generated using /tags/generator.html?games /tags.html added extra path /tags/utilities.html to be generated using /tags/generator.html?utilities (2/19) Finished:-------------------------------------------- /tags.html Queued file #16:-------------------------------------------- /tags/constexprjs.html Queued file #17:-------------------------------------------- /tags/web.html Queued file #18:-------------------------------------------- /tags/games.html Queued file #19:-------------------------------------------- /tags/utilities.html (3/19) Finished:-------------------------------------------- /404.html (4/19) Finished:-------------------------------------------- /about.html (5/19) Finished:-------------------------------------------- /projects.html (6/19) Finished:-------------------------------------------- /posts/constexprjs_dependency_resolution.html (7/19) Finished:-------------------------------------------- /posts/clipboard_cli.html (8/19) Finished:-------------------------------------------- /posts/constexprjs_code_organization.html (9/19) Finished:-------------------------------------------- /posts/constexprjs_api_docs.html (10/19) Finished:------------------------------------------- /posts/dfbhd_mus.html (11/19) Finished:------------------------------------------- /posts/constexprjs_hello_world.html (12/19) Finished:------------------------------------------- /posts/constexprjs_syntax_highlighting.html (13/19) Finished:------------------------------------------- /posts/dfbhd_sbf.html (14/19) Finished:------------------------------------------- /tags/generator.html?constexprjs (15/19) Finished:------------------------------------------- /tags/generator.html?web (16/19) Finished:------------------------------------------- /tags/generator.html?games (17/19) Finished:------------------------------------------- /posts/constexprjs.html (18/19) Finished:------------------------------------------- /tags/generator.html?utilities (19/19) Finished:------------------------------------------- /posts/constexprjs_entry_points.html Wrote depfile:---------------------------------------------- devtools/deps.json Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/favicon.ico Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/css/styles.css Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/bg.jpg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/icons/facebook.svg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/icons/hn.svg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/icons/moon.svg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/icons/reddit.svg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/icons/swipe.svg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/icons/twitter.svg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/js/dynamic-post.js Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/js/dynamic-pre.js Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/js/mtm.js Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/css/prism.css Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/img/dfbhd.jpg Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/media/dfbhd_downscaled.mp3 Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/media/dfbhd_unprocessed.mp3 Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/packages/katex/fonts Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/packages/katex/katex.css Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/css/asciinema-player.css Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/css/pivot.css Copying resource:------------------------------------------- /home/sagartiwari/src/website/amokfa.github.io.src/static/media/constexprjs.mp4 $ exit
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
- You can mark tags other than
script withconstexpr 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. - 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> - 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.
- Use this if you love javascript
- 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.