How to run a Node.js script before `resources.PostProcess` task?

I’m working on cutting down the dependencies for my website, and I’ve made considerable progress, as I’ve written up a script to pre-render all math equations in my website using katex's npm package. But I’ve hit a wall. To purge unnecessary CSS, I use uncss with resources.PostCSS, which is done at the end of the build using resources.PostProcess. Now, what my npm script does, is it parses all math equations in HTML files in public folder and overwrites them accordingly.

What I wish to do is to somehow have uncss run after the node script, so that it can keep the CSS rules used by math.

Would it be possible to do this? I could use a bash script instead for building, but as I’m developing a theme, I’d like to keep everything confined to Hugo.

I also wish to somehow incorporate the npm script in Hugo’s build command.

I don’t think that Hugo can run scripts while processing other than the ones it offers already. That’s a feature that would be useful, but probably to complicated to add.

But: uncss has two parameters ignore and ignoreSheets that should help you not compiling the equations. Worst case is, you create a partial that adds a div/span with a class around your equations and use that to not process everything between <span class="myclass"></span>.

I suspect you could run “non PostCSS” stuff via … PostCSS (or Babel).

Yeah, I know that. In fact I’m using ignore right now. But I was hoping to get rid of extra CSS thanks to the static nature of the site.

Somewhere along the lines of a custom plugin for PostCSS?

Somewhere along the line of that, yea.

Thanks for the suggestion @bep. I was able to solve this issue using the PostCSS plugin workaround.
Here’s my custom postcss plugin:

const he = require('he');
const katex = require('katex');
const fs = require('fs');

function parseMath(file) {
	var html = fs.readFileSync(file, 'utf8');
	var out = parseInlineMath(html);
	return parseMathBlocks(out);
}

function parseInlineMath(html) {
	return html.replace(/\$(.+?)\$/g, function (match, p1) {
		var math = he.decode(p1);
		return katex.renderToString(math, { throwOnError: false });
	});
}

function parseMathBlocks(html) {
	return html.replace(/\$\$([\s\S]+?)\$\$/gm, function (match, p1) {
		var math = he.decode(p1);
		return katex.renderToString(math, { throwOnError: false, displayMode: true });
	})
}

module.exports = (opts = {}) => {
	const files = opts.files;

	for (const file of files) {
		var html = parseMath(file);

		fs.writeFileSync(file, html)
	}

	return { postcssPlugin: 'postcss-katex' }
}
module.exports.postcss = true

In my postcss.config.js I have:

const path = require('path');
const fs = require('fs');

const buildDir = './public';

// `public/katex.json` is a custom output format
const jsonFile = fs.readFileSync(path.join(buildDir, 'katex.json'));

var i, files = JSON.parse(jsonFile);
for (i=0; i < files.length; i++) {
	files[i] = path.join(buildDir, files[i].replace('.md', '.html'));
}

const katex = require('./postcss/katex')({
	files: files
});

module.exports = {
	map: false,
	plugins: [
		require('autoprefixer'),
                katex
	]
}

However, there’s a caveat if I want to use Hugo’s server while development. When I run hugo server -d public, the HTML files are modified, but the server isn’t aware, so it still hosts the pre-rendered HTML.
Giving the --disableFastRender flag is what I’m doing right now, but could there be a better way to let Hugo’s server know that HTML files have been modified once PostCSS is done?

OK, so when you do file writes outside of Hugo you need to run with hugo server --renderToDisk. I think you also need to do --disableFastRender (but I’m not sure, it’s on my list of things to check).

When I tested this, I had to use --disableFastRender to make things reflect on the local server. Anyways, I don’t think this is a viable option for me since Hugo overwrites contents’ HTML files whenever it detects changes in MD files, without re-executing the postcss-katex task, so the new HTML doesn’t contain processed math. I don’t know of any way to tell Hugo to execute the postcss task on file changes(please tell me if there is any way), so I guess I’ll have to leave this method here.

It does if “Hugo thinks that” a file used by that task is changed. This is a long story and I have a PR almost ready that improves this) but it should be doable even in your case.

Hmm. Glad to hear it’s possible. Could you please give me some trails to follow? What I’m doing currently is that I have site.css which is built using postCSS and resources.PostProcess using the config shown about.

Since site.css is detached from contents, how can I bind the two together? Or how do I tell Hugo to execute the postCSS task, whenever a RegularPage having Params.math = true is modified?

So, if you the files you change lives outside of the Hugo project, e.g. in /foo/myfiles/data.json, then you can mount /foo/myfiles into /assets/css/somewhere. The somewhere does not matter, but 1) It will be watched for changes and 2) If will be associated with CSS changes, so to speak.

Oh, interesting. So Hugo only watches changes to the directory as a whole, not individual files? So if foo/bar.md was modified, then foo/** will be built again?

We watch both files and directories, but this is a long story and it’s late on Saturday …

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.