143 lines
3.2 KiB
JavaScript
143 lines
3.2 KiB
JavaScript
"use strict";
|
|
const DEFAULT_OPTIONS = require("./defaultOptions");
|
|
const evaluateValue = require("evaluate-value");
|
|
const list2Array = require("list-to-array");
|
|
const parseMetaRefresh = require("http-equiv-refresh");
|
|
const parseSrcset = require("parse-srcset");
|
|
|
|
const CONTENT_ATTR = "content";
|
|
const PING_ATTR = "ping";
|
|
const SRCSET_ATTR = "srcset";
|
|
|
|
const DELIMITER = ",";
|
|
const EMPTY_STRING = "";
|
|
const EMPTY_TAG_GROUP = Object.freeze({});
|
|
const FUNCTION_TYPE = "function";
|
|
const PRETTY_DELIMITER = ", ";
|
|
|
|
|
|
|
|
const plugin = options =>
|
|
{
|
|
const {eachURL, filter} = Object.assign({}, DEFAULT_OPTIONS, options);
|
|
|
|
if (typeof eachURL !== FUNCTION_TYPE)
|
|
{
|
|
throw new TypeError("eachURL option must be a function");
|
|
}
|
|
|
|
// const tagMatchers = Object.keys(filter).map(tagName => ({ tag: tagName }));
|
|
|
|
// Called by PostHTML
|
|
return tree =>
|
|
{
|
|
const promises = [];
|
|
|
|
tree.walk(node =>
|
|
// tree.match(tagMatchers, (node) =>
|
|
{
|
|
if (node.attrs === undefined) {
|
|
return node;
|
|
}
|
|
|
|
const tagGroup = filter[node.tag] || EMPTY_TAG_GROUP;
|
|
|
|
Object.keys(node.attrs).forEach(attrName =>
|
|
{
|
|
const isAcceptedTagAttr = attrName in tagGroup && evaluateValue(tagGroup[attrName], node, attrName);
|
|
|
|
if (isAcceptedTagAttr)
|
|
{
|
|
switch (attrName)
|
|
{
|
|
case CONTENT_ATTR:
|
|
{
|
|
promises.push( transformMetaRefresh(node, attrName, eachURL) );
|
|
break;
|
|
}
|
|
case PING_ATTR:
|
|
{
|
|
promises.push( transformCommaSeparated(node, attrName, eachURL) );
|
|
break;
|
|
}
|
|
case SRCSET_ATTR:
|
|
{
|
|
promises.push( transformSrcset(node, attrName, eachURL) );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
promises.push( transformDefault(node, attrName, eachURL) );
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
return node;
|
|
});
|
|
|
|
return Promise.all(promises).then(() => tree);
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const transformCommaSeparated = ({attrs, tag}, attrName, transformer) =>
|
|
{
|
|
const urls = list2Array( attrs[attrName], DELIMITER );
|
|
|
|
if (urls.length > 0)
|
|
{
|
|
const promises = urls.map(value => Promise.resolve( transformer(value, attrName, tag) ));
|
|
|
|
return Promise.all(promises).then(newUrls => attrs[attrName] = newUrls.join(PRETTY_DELIMITER));
|
|
}
|
|
};
|
|
|
|
|
|
|
|
const transformDefault = ({attrs, tag}, attrName, transformer) =>
|
|
{
|
|
return Promise.resolve( transformer( attrs[attrName], attrName, tag ) )
|
|
.then(newUrl => attrs[attrName] = newUrl);
|
|
};
|
|
|
|
|
|
|
|
const transformMetaRefresh = ({attrs, tag}, attrName, transformer) =>
|
|
{
|
|
const {timeout, url} = parseMetaRefresh( attrs[attrName] );
|
|
|
|
if (timeout !== null)
|
|
{
|
|
return Promise.resolve( transformer(url || "", attrName, tag) )
|
|
.then(newUrl => attrs[attrName] = `${timeout}; url=${newUrl}`);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
const transformSrcset = ({attrs, tag}, attrName, transformer) =>
|
|
{
|
|
const values = parseSrcset( attrs[attrName] );
|
|
|
|
if (values.length > 0)
|
|
{
|
|
const promises = values.map(({d, h, url, w}) => Promise.resolve( transformer(url, attrName, tag) )
|
|
.then(newUrl =>
|
|
{
|
|
d = d !== undefined ? ` ${d}x` : EMPTY_STRING;
|
|
h = h !== undefined ? ` ${h}h` : EMPTY_STRING;
|
|
w = w !== undefined ? ` ${w}w` : EMPTY_STRING;
|
|
|
|
return `${newUrl}${w}${h}${d}`;
|
|
}));
|
|
|
|
return Promise.all(promises).then(newValues => attrs[attrName] = newValues.join(PRETTY_DELIMITER));
|
|
}
|
|
};
|
|
|
|
|
|
|
|
module.exports = plugin;
|