posthtml-external-link
Version:
Add 'rel="external noopener nofollow"' and 'target="_blank"' to all external links [PostHTML Plugin]
82 lines (81 loc) • 2.65 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.posthtmlExternalLink = void 0;
function posthtmlExternalLink(config) {
return function postHtmlExternalLinkPlugin(tree) {
if (!config)
return tree;
tree.walk((node) => {
if (node.tag
&& node.tag === 'a'
&& node.attrs
&& node.attrs.href) {
if (config) {
const exclude = Array.isArray(config.exclude) ? config.exclude : [config.exclude];
if (!isExternalLink(node.attrs.href, exclude)) {
return node;
}
}
// :: append rels to the element's [rel] value.
// failsafes in case [rel] is not defined yet.
const rels = node.attrs.rel
? new Set(node.attrs.rel.split(/\s+/))
: new Set();
// config.noreferrer && rels.add('noreferrer');
if (config.noreferrer)
rels.add('noreferrer');
rels.add('noopener');
rels.add('nofollow');
rels.add('external');
node.attrs.rel = Array.from(rels).join(' ');
node.attrs.target = '_blank';
}
return node;
});
return tree;
};
}
exports.posthtmlExternalLink = posthtmlExternalLink;
const cache = new Map();
function cacheApply(key, value) {
var _a;
if (cache.has(key))
return (_a = cache.get(key)) !== null && _a !== void 0 ? _a : false;
if (typeof value === 'function')
value = value();
cache.set(key, value);
return value;
}
function isExternalLink(input, exclude) {
return cacheApply(input, () => {
if (!input.startsWith('//')
&& !input.startsWith('http://')
&& !input.startsWith('https://')) {
return false;
}
let urlObj;
try {
urlObj = new URL(input);
}
catch (_a) { }
// urlObj will be undefined if "input" is an invalid URL
if (!(urlObj instanceof URL)) {
return false;
}
// handle mailto: javascript: vbscript: etc.
if (urlObj.origin === 'null') {
return false;
}
if (exclude) {
for (const i of exclude) {
if (urlObj.hostname === i) {
return false;
}
}
}
if (urlObj.hostname !== input) {
return true;
}
return false;
});
}