@meeg/gridsome-source-kentico-kontent
Version:
Kentico Kontent data source plugin for Gridsome.
180 lines (124 loc) • 4.96 kB
JavaScript
const changeCase = require('change-case');
const cheerio = require('cheerio');
class GridsomeRichTextHtmlTransformer {
constructor(options) {
this.options = options;
}
canTransformRichText() {
return this.options.wrapperCssClass !== null;
}
canTransformLinks() {
return this.canTransformRichText()
&& this.options.itemLinkSelector !== null
&& this.options.itemLinkComponentName !== null;
}
canTransformComponents() {
return this.canTransformRichText()
&& this.options.componentSelector !== null;
}
canTransformAssets() {
return this.canTransformRichText()
&& this.options.assetSelector !== null
&& this.options.assetComponentName !== null;
}
transformRichTextHtml(field) {
let html = field.resolveHtml();
if (!this.canTransformRichText()) {
return html;
}
const wrapperCssClass = this.options.wrapperCssClass;
html = `<div class="${wrapperCssClass}">${html}</div>`;
const $ = cheerio.load(html, { decodeEntities: false });
// Kentico Kontent can return an empty paragraph element if there is no content, which is of no use
// If the rich text element has no text content, just return an empty string
if ($(`.${wrapperCssClass}`).text().trim() === '') {
return '';
}
// Transform item links
// N.B. This shouldn't be necessary, but the `urlSlugResolver` feature of the Kentico Kontent SDK doesn't appear to work all of the time
// TODO: If this does work consistently in future, this can be removed
this.transformItemLinks($);
// Unwrap components
this.transformComponents($);
// Transform assets
this.transformAssets($);
// Return the parsed html
html = cheerio.html($(`.${wrapperCssClass}`), { decodeEntities: false });
return html;
}
transformComponents($) {
if (!this.canTransformComponents()) {
return;
}
const componentSelector = this.options.componentSelector;
const components = $(componentSelector);
components.each((index, element) => {
const component = $(element);
const componentHtml = component.html();
component.replaceWith(componentHtml);
});
}
getComponentName(codename) {
const componentNamePrefix = this.options.componentNamePrefix.length;
const componentCodename = componentNamePrefix.length > 0
? `${componentNamePrefix}-${codename}`
: codename;
return changeCase.kebabCase(componentCodename);
}
getComponentHtml(id, type) {
// Rich text components will be rendered as Vue components
const componentName = this.getComponentName(type);
const html = `<${componentName} :node="getNode('${type}', '${id}')" />`;
return html;
}
transformItemLinks($) {
if (!this.canTransformLinks()) {
return;
}
const itemLinkSelector = this.options.itemLinkSelector;
const itemLinks = $(itemLinkSelector);
itemLinks.each((index, element) => {
const itemLink = $(element);
const itemId = itemLink.data('itemId');
const linkText = itemLink.html();
const itemLinkHtml = this.getLinkHtml(itemId, linkText);
itemLink.replaceWith(itemLinkHtml);
});
}
getLinkHtml(id, text) {
// Links to content items in rich text elements will be rendered as Vue components
const componentName = this.getComponentName(this.options.itemLinkComponentName);
const html = `<${componentName} :node="getNode('item_link', '${id}')">${text}</item-link>`;
return html;
}
transformAssets($) {
if (!this.canTransformAssets()) {
return;
}
const assetSelector = this.options.assetSelector;
const assets = $(assetSelector);
assets.each((index, element) => {
const asset = $(element);
const assetImg = asset.find('img');
if (assetImg.length === 0) {
// TODO: What to do with other types of assets? Currently the
// id of an asset node is the url, but where do we get that from?
return;
}
const assetId = assetImg.attr('src');
// TODO: The asset id is available in the `data-asset-id` attribute, but
// the url is currently used as the Gridsome node id because the id is not
// available in asset data retrieved via the delivery API - the below will
// need to change if/when the id is made available
const assetHtml = this.getAssetHtml(assetId);
asset.replaceWith(assetHtml);
});
}
getAssetHtml(id) {
// Assets will be rendered as Vue components
const componentName = this.getComponentName(this.options.assetComponentName);
const html = `<${componentName} :node="getNode('asset', '${id}')" />`;
return html;
}
}
module.exports = GridsomeRichTextHtmlTransformer;