Skip to content

Instantly share code, notes, and snippets.

@bezoerb
Created April 24, 2022 21:45
Show Gist options
  • Save bezoerb/c88576602636606cc771da990fa3ef0a to your computer and use it in GitHub Desktop.
Save bezoerb/c88576602636606cc771da990fa3ef0a to your computer and use it in GitHub Desktop.
const path = require("path");
const critical = require("critical");
/**
* Mitt: Tiny (~200b) functional event emitter / pubsub.
* @name mitt
* @returns {Mitt}
*/
function mitt(all) {
all = all || new Map();
return {
/**
* A Map of event names to registered handler functions.
*/
all,
/**
* Register an event handler for the given type.
* @param {string|symbol} type Type of event to listen for, or `'*'` for all events
* @param {Function} handler Function to call in response to given event
* @memberOf mitt
*/
on(type, handler) {
const handlers = all.get(type);
if (handlers) {
handlers.push(handler);
}
else {
all.set(type, [handler]);
}
},
/**
* Remove an event handler for the given type.
* If `handler` is omitted, all handlers of the given type are removed.
* @param {string|symbol} type Type of event to unregister `handler` from (`'*'` to remove a wildcard handler)
* @param {Function} [handler] Handler function to remove
* @memberOf mitt
*/
off(type, handler) {
const handlers = all.get(type);
if (handlers) {
if (handler) {
handlers.splice(handlers.indexOf(handler) >>> 0, 1);
}
else {
all.set(type, []);
}
}
},
/**
* Invoke all handlers for the given type.
* If present, `'*'` handlers are invoked after type-matched handlers.
*
* Note: Manually firing '*' handlers is not supported.
*
* @param {string|symbol} type The event type to invoke
* @param {Any} [evt] Any value (object is recommended and powerful), passed to each handler
* @memberOf mitt
*/
emit(type, evt) {
let handlers = all.get(type);
if (handlers) {
handlers
.slice()
.map((handler) => {
handler(evt);
});
}
handlers = all.get('*');
if (handlers) {
handlers
.slice()
.map((handler) => {
handler(type, evt);
});
}
}
};
}
const emitter = mitt()
const maxInstances = 5;
let instances = 0;
const runCritical = async (options) => {
if (instances < maxInstances) {
instances = instances + 1;
const { html } = await critical.generate(options);
instances = instances - 1;
emitter.emit('decrease', { instances })
return html;
}
return new Promise((resolve) => {
const delayedRun = async () => {
if (instances < maxInstances) {
emitter.off('decrease',delayedRun)
const result = await runCritical(options);
resolve(result);
}
}
emitter.on('decrease', delayedRun);
})
}
module.exports = function (config, options) {
config.addTransform("critical-css", async function (content, outputPath) {
if (outputPath && outputPath.endsWith(".html")) {
// Config will change from Eleventy v1.0.0
const outputDir =
(this._config && this._config.dir && this._config.dir.output) ||
(config && config.dir && config.dir.output) ||
path.dirname(outputPath);
// Generate HTML with critical CSS
const html = await runCritical({
assetPaths: [path.dirname(outputPath)],
base: outputDir,
html: content,
concurrency: 10,
inline: true,
rebase: ({ originalUrl }) => originalUrl,
...options,
});
return html;
}
return content;
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment