173 lines
4.4 KiB
JavaScript
173 lines
4.4 KiB
JavaScript
import posthtml from "posthtml";
|
|
import urls from "@11ty/posthtml-urls";
|
|
import { FilePathUtil } from "./FilePathUtil.js";
|
|
|
|
import { arrayDelete } from "./ArrayUtil.js";
|
|
|
|
class HtmlTransformer {
|
|
// feature test for Eleventy Bundle Plugin
|
|
static SUPPORTS_PLUGINS_ENABLED_CALLBACK = true;
|
|
|
|
static TYPES = ["callbacks", "plugins"];
|
|
|
|
constructor() {
|
|
// execution order is important (not order of addition/object key order)
|
|
this.callbacks = {};
|
|
this.posthtmlProcessOptions = {};
|
|
this.plugins = {};
|
|
}
|
|
|
|
get aggregateBench() {
|
|
if (!this.userConfig) {
|
|
throw new Error("Internal error: Missing `userConfig` in HtmlTransformer.");
|
|
}
|
|
return this.userConfig.benchmarkManager.get("Aggregate");
|
|
}
|
|
|
|
setUserConfig(config) {
|
|
this.userConfig = config;
|
|
}
|
|
|
|
static prioritySort(a, b) {
|
|
if (b.priority > a.priority) {
|
|
return 1;
|
|
}
|
|
if (a.priority > b.priority) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// context is important as it is used in html base plugin for page specific URL
|
|
static _getPosthtmlInstance(callbacks = [], plugins = [], context = {}) {
|
|
let inst = posthtml();
|
|
|
|
// already sorted by priority when added
|
|
for (let { fn: plugin, options } of plugins) {
|
|
inst.use(plugin(Object.assign({}, context, options)));
|
|
}
|
|
|
|
// Run the built-ins last
|
|
if (callbacks.length > 0) {
|
|
inst.use(
|
|
urls({
|
|
eachURL: (url, attrName, tagName) => {
|
|
for (let { fn: callback } of callbacks) {
|
|
// already sorted by priority when added
|
|
url = callback.call(context, url, { attribute: attrName, tag: tagName });
|
|
}
|
|
|
|
return url;
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
return inst;
|
|
}
|
|
|
|
_add(extensions, addType, value, options = {}) {
|
|
options = Object.assign(
|
|
{
|
|
priority: 0,
|
|
},
|
|
options,
|
|
);
|
|
|
|
let extensionsArray = (extensions || "").split(",");
|
|
for (let ext of extensionsArray) {
|
|
let target = this[addType];
|
|
if (!target[ext]) {
|
|
target[ext] = [];
|
|
}
|
|
|
|
target[ext].push({
|
|
// *could* fallback to function name, `value.name`
|
|
name: options.name, // for `remove` and debugging
|
|
fn: value, // callback or plugin
|
|
priority: options.priority, // sorted in descending order
|
|
enabled: options.enabled || (() => true),
|
|
options: options.pluginOptions,
|
|
});
|
|
|
|
target[ext].sort(HtmlTransformer.prioritySort);
|
|
}
|
|
}
|
|
|
|
addPosthtmlPlugin(extensions, plugin, options = {}) {
|
|
this._add(extensions, "plugins", plugin, options);
|
|
}
|
|
|
|
// match can be a plugin function or a filter callback(plugin => true);
|
|
remove(extensions, match) {
|
|
for (let removeType of HtmlTransformer.TYPES) {
|
|
for (let ext of (extensions || "").split(",")) {
|
|
this[removeType][ext] = arrayDelete(this[removeType][ext], match);
|
|
}
|
|
}
|
|
}
|
|
|
|
addUrlTransform(extensions, callback, options = {}) {
|
|
this._add(extensions, "callbacks", callback, options);
|
|
}
|
|
|
|
setPosthtmlProcessOptions(options) {
|
|
Object.assign(this.posthtmlProcessOptions, options);
|
|
}
|
|
|
|
isTransformable(extension, context) {
|
|
return (
|
|
this.getCallbacks(extension, context).length > 0 || this.getPlugins(extension).length > 0
|
|
);
|
|
}
|
|
|
|
getCallbacks(extension, context) {
|
|
let callbacks = this.callbacks[extension] || [];
|
|
return callbacks.filter(({ enabled }) => {
|
|
if (!enabled || typeof enabled !== "function") {
|
|
return true;
|
|
}
|
|
return enabled(context);
|
|
});
|
|
}
|
|
|
|
getPlugins(extension) {
|
|
let plugins = this.plugins[extension] || [];
|
|
return plugins.filter(({ enabled }) => {
|
|
if (!enabled || typeof enabled !== "function") {
|
|
return true;
|
|
}
|
|
return enabled();
|
|
});
|
|
}
|
|
|
|
static async transformStandalone(content, callback, posthtmlProcessOptions = {}) {
|
|
let posthtmlInstance = this._getPosthtmlInstance([
|
|
{
|
|
fn: callback,
|
|
enabled: () => true,
|
|
},
|
|
]);
|
|
let result = await posthtmlInstance.process(content, posthtmlProcessOptions);
|
|
return result.html;
|
|
}
|
|
|
|
async transformContent(outputPath, content, context) {
|
|
let extension = FilePathUtil.getFileExtension(outputPath);
|
|
if (!this.isTransformable(extension, context)) {
|
|
return content;
|
|
}
|
|
|
|
let bench = this.aggregateBench.get(`Transforming \`${extension}\` with posthtml`);
|
|
bench.before();
|
|
let callbacks = this.getCallbacks(extension, context);
|
|
let plugins = this.getPlugins(extension);
|
|
let posthtmlInstance = HtmlTransformer._getPosthtmlInstance(callbacks, plugins, context);
|
|
let result = await posthtmlInstance.process(content, this.posthtmlProcessOptions);
|
|
bench.after();
|
|
return result.html;
|
|
}
|
|
}
|
|
|
|
export { HtmlTransformer };
|