Ghost/ghost/link-replacer/lib/link-replacer.js
Simon Backx 4c8179312d
🎨 Added support for relative links in emails (#17630)
fixes https://github.com/TryGhost/Product/issues/3687

After this change, relative URLs in emails will be replaced with
absolute URLs using the post URL. Making relative Portal URLs possible
etc.

Updates the test data generator to fix invalid URL encoding (somehow a
backslash + escaped double quote was added when it wasn't required).
2023-08-08 13:22:56 +02:00

50 lines
1.8 KiB
JavaScript

class LinkReplacer {
/**
* Replaces the links in the provided HTML
* @param {string} html
* @param {(url: URL, originalPath: string): Promise<URL|string|false>} replaceLink
* @param {object} options
* @param {string} [options.base] If you want to replace relative links, this will replace them to an absolute link and call the replaceLink method too
* @returns {Promise<string>}
*/
async replace(html, replaceLink, options = {}) {
const cheerio = require('cheerio');
const entities = require('entities');
try {
const $ = cheerio.load(html, {
xml: {
// This makes sure we use the faster and less destructive htmlparser2 parser
xmlMode: false
},
// Do not replace &, ', " and others with HTML entities (is bugged because it replaces &map_ with something weird (&#x21A6;))
decodeEntities: false
}, false);
for (const el of $('a').toArray()) {
const href = $(el).attr('href');
if (href) {
let url;
const path = entities.decode(href);
try {
url = new URL(path, options.base);
} catch (e) {
// Ignore invalid URLs
}
if (url) {
url = await replaceLink(url, path);
const str = url.toString();
$(el).attr('href', str);
}
}
}
return $.html();
} catch (e) {
// Catch errors from cheerio
return html;
}
}
}
module.exports = new LinkReplacer();