gatsby-source-wordpress
Version:
Source data from WordPress in an efficient and scalable way.
811 lines (792 loc) • 35.1 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.getImgTagMatches = exports.getImgSrcRemoteFileMatchesFromNodeString = exports.ensureSrcHasHostname = void 0;
exports.getPlaceholderUrlFromMediaItemNode = getPlaceholderUrlFromMediaItemNode;
exports.searchAndReplaceNodeStrings = exports.replaceNodeHtmlImages = exports.processNode = exports.getWpLinkRegex = void 0;
var _validUrl = require("valid-url");
var _gatsbyPluginImage = require("gatsby-plugin-image");
var _react = _interopRequireDefault(require("react"));
var _server = _interopRequireDefault(require("react-dom/server"));
var _fastJsonStableStringify = _interopRequireDefault(require("fast-json-stable-stringify"));
var _execall = _interopRequireDefault(require("execall"));
var _cheerio = _interopRequireDefault(require("cheerio"));
var _url = _interopRequireDefault(require("url"));
var _path = _interopRequireDefault(require("path"));
var _fsExtra = _interopRequireDefault(require("fs-extra"));
var _supportedExtensions = require("gatsby-transformer-sharp/supported-extensions");
var _replaceall = _interopRequireDefault(require("replaceall"));
var _gatsbyVersion = require("../../../utils/gatsby-version");
var _polyfillRemoteFile = require("gatsby-plugin-utils/polyfill-remote-file");
var _formatLogMessage = require("../../../utils/format-log-message");
var _fetchReferencedMediaItems = _interopRequireWildcard(require("../fetch-nodes/fetch-referenced-media-items"));
var _stringEncoding = require("../../../utils/string-encoding");
var _store = require("../../../store");
var _redux = require("gatsby/dist/redux");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/* eslint-disable no-useless-escape */
/**
* Takes in a MediaItem node from WPGraphQL as well as Gatsby plugin options and returns the correct placeholder URL for GatsbyImage
*
* The user must set the placeholderSizeName plugin option, or otherwise create an image size in WP where the name is `gatsby-image-placeholder`
*/
function getPlaceholderUrlFromMediaItemNode(node, pluginOptions) {
var _node$mediaDetails, _node$mediaDetails$si;
let placeholderSizeByWidth;
let placeholderSizeByName;
(_node$mediaDetails = node.mediaDetails) === null || _node$mediaDetails === void 0 ? void 0 : (_node$mediaDetails$si = _node$mediaDetails.sizes) === null || _node$mediaDetails$si === void 0 ? void 0 : _node$mediaDetails$si.forEach(size => {
var _pluginOptions$type, _pluginOptions$type$M;
if (size.name === ((pluginOptions === null || pluginOptions === void 0 ? void 0 : (_pluginOptions$type = pluginOptions.type) === null || _pluginOptions$type === void 0 ? void 0 : (_pluginOptions$type$M = _pluginOptions$type.MediaItem) === null || _pluginOptions$type$M === void 0 ? void 0 : _pluginOptions$type$M.placeholderSizeName) || `gatsby-image-placeholder`)) {
placeholderSizeByName = size;
} else if (Number(size.width) <= 20) {
placeholderSizeByWidth = size;
}
});
const placeHolderSize = placeholderSizeByName || placeholderSizeByWidth;
return placeHolderSize === null || placeHolderSize === void 0 ? void 0 : placeHolderSize.sourceUrl;
}
const findReferencedImageNodeIds = ({
nodeString,
pluginOptions,
node
}) => {
// if the lazyNodes plugin option is set we don't need to find
// image node id's because those nodes will be fetched lazily in resolvers.
if (pluginOptions.type.MediaItem.lazyNodes &&
// but not in Gatsby v4+ because lazyNodes is no longer supported
!_gatsbyVersion.usingGatsbyV4OrGreater) {
return [];
}
// get an array of all referenced media file ID's
const matchedIds = (0, _execall.default)(/"__typename":"MediaItem","id":"([^"]*)"/gm, nodeString).map(match => match.subMatches[0]).filter(id => id !== node.id);
return matchedIds;
};
const getCheerioImgDbId = cheerioImg => {
// try to get the db id from data attributes
const dataAttributeId = cheerioImg.attribs[`data-id`] || cheerioImg.attribs[`data-image-id`];
if (dataAttributeId) {
return dataAttributeId;
}
if (!cheerioImg.attribs.class) {
return null;
}
// try to get the db id from the wp-image-id classname
const wpImageClass = cheerioImg.attribs.class.split(` `).find(className => className.includes(`wp-image-`));
if (wpImageClass) {
const wpImageClassDashArray = wpImageClass.split(`-`);
const wpImageClassId = Number(wpImageClassDashArray[wpImageClassDashArray.length - 1]);
if (wpImageClassId) {
return wpImageClassId;
}
}
return null;
};
// media items are of the "post" type
const dbIdToMediaItemRelayId = dbId => dbId ? (0, _stringEncoding.b64e)(`post:${dbId}`) : null;
const getCheerioImgRelayId = cheerioImg => dbIdToMediaItemRelayId(getCheerioImgDbId(cheerioImg));
const ensureSrcHasHostname = ({
src,
wpUrl
}) => {
const {
protocol,
host
} = _url.default.parse(wpUrl);
if (src.startsWith(`/wp-content`)) {
src = `${protocol}//${host}${src}`;
}
return src;
};
exports.ensureSrcHasHostname = ensureSrcHasHostname;
const pickNodeBySourceUrlOrCheerioImg = ({
url,
cheerioImg,
mediaItemNodes
}) => {
const possibleHtmlSrcs = [
// try to match the media item source url by original html src
url,
// or by the src minus any image sizes string
(0, _fetchReferencedMediaItems.stripImageSizesFromUrl)(url)];
let imageNode = mediaItemNodes.find(mediaItemNode => {
var _ref;
return (
// either find our node by the source url
possibleHtmlSrcs.includes(mediaItemNode.sourceUrl) || possibleHtmlSrcs.includes( // try to match without -scaled in the sourceUrl as well
// since WP adds -scaled to image urls if they were too large
// at upload time but image urls in html don't have this requirement.
// the sourceUrl may have -scaled in it but the full size image is still
// stored on the server (just not in the db)
(_ref = mediaItemNode.sourceUrl || mediaItemNode.mediaItemUrl) === null || _ref === void 0 ? void 0 : _ref.replace(`-scaled`, ``))
);
});
if (!imageNode && cheerioImg) {
imageNode = mediaItemNodes.find(mediaItemNode => getCheerioImgRelayId(cheerioImg) === mediaItemNode.id);
}
return imageNode;
};
let displayedFailedToRestoreMessage = false;
const fetchNodeHtmlImageMediaItemNodes = async ({
// node, // for inspecting nodes while debugging
cheerioImages,
helpers,
wpUrl
}) => {
// get all the image nodes we've cached from elsewhere
const {
nodeMetaByUrl
} = (0, _store.getStore)().getState().imageNodes;
const previouslyCachedNodesByUrl = (await Promise.all(Object.entries(nodeMetaByUrl).map(([sourceUrl, {
id
} = {}]) => {
if (!sourceUrl || !id) {
return null;
}
sourceUrl = ensureSrcHasHostname({
wpUrl,
src: sourceUrl
});
const existingNode = helpers.getNode(id);
if (!existingNode) {
if (!displayedFailedToRestoreMessage) {
helpers.reporter.warn((0, _formatLogMessage.formatLogMessage)(`File node failed to restore from cache. This is a bug in gatsby-source-wordpress. Please open an issue so we can help you out :)`));
displayedFailedToRestoreMessage = true;
}
return null;
}
return {
sourceUrl,
...existingNode
};
}))).filter(Boolean);
const mediaItemUrls = cheerioImages
// filter out nodes we already have
.filter(({
cheerioImg
}) => {
const url = ensureSrcHasHostname({
wpUrl,
src: cheerioImg.attribs.src
});
const existingNode = pickNodeBySourceUrlOrCheerioImg({
url,
mediaItemNodes: previouslyCachedNodesByUrl
});
return !existingNode;
})
// get remaining urls
.map(({
cheerioImg
}) => {
const src = ensureSrcHasHostname({
src: cheerioImg.attribs.src,
wpUrl
});
return src;
});
// build a query to fetch all media items that we don't already have
const mediaItemNodesBySourceUrl = await (0, _fetchReferencedMediaItems.default)({
mediaItemUrls
});
// images that have been edited from the media library that were previously
// uploaded to a post/page will have a different sourceUrl so they can't be fetched by it
// in many cases we have data-id or data-image-id as attributes on the img
// we can try to use those to fetch media item nodes as well
// this will keep us from missing nodes
const mediaItemDbIds = cheerioImages.map(({
cheerioImg
}) => getCheerioImgDbId(cheerioImg)).filter(Boolean);
const mediaItemRelayIds = mediaItemDbIds.map(dbId => dbIdToMediaItemRelayId(dbId)).filter(
// filter out any media item ids we already fetched
relayId => ![...mediaItemNodesBySourceUrl, ...previouslyCachedNodesByUrl].find(({
id
} = {}) => id === relayId));
const mediaItemNodesById = await (0, _fetchReferencedMediaItems.default)({
referencedMediaItemNodeIds: mediaItemRelayIds
});
const createdNodes = [...mediaItemNodesById, ...mediaItemNodesBySourceUrl];
const mediaItemNodes = [...createdNodes, ...previouslyCachedNodesByUrl];
const htmlMatchesToMediaItemNodesMap = new Map();
for (const {
cheerioImg,
match
} of cheerioImages) {
const htmlImgSrc = ensureSrcHasHostname({
src: cheerioImg.attribs.src,
wpUrl
});
const imageNode = pickNodeBySourceUrlOrCheerioImg({
url: htmlImgSrc,
cheerioImg,
mediaItemNodes
});
cacheCreatedFileNodeBySrc({
node: imageNode,
src: htmlImgSrc
});
if (imageNode) {
// match is the html string of the img tag
htmlMatchesToMediaItemNodesMap.set(match, {
imageNode,
cheerioImg
});
}
}
return htmlMatchesToMediaItemNodesMap;
};
const getCheerioElementFromMatch = wpUrl => ({
match,
tag = `img`
}) => {
var _cheerioElement$attri, _cheerioElement$attri2;
// unescape quotes
const parsedMatch = JSON.parse(`"${match}"`);
// load our matching img tag into cheerio
const $ = _cheerio.default.load(parsedMatch, {
xml: {
// make sure it's not wrapped in <body></body>
withDomLvl1: false,
// no need to normalize whitespace, we're dealing with a single element here
normalizeWhitespace: false,
xmlMode: true,
// entity decoding isn't our job here, that will be the responsibility of WPGQL
// or of the source plugin elsewhere.
decodeEntities: false
}
});
// there's only ever one element due to our match matching a single tag
// $(tag) isn't an array, it's an object with a key of 0
const cheerioElement = $(tag)[0];
if (cheerioElement !== null && cheerioElement !== void 0 && (_cheerioElement$attri = cheerioElement.attribs) !== null && _cheerioElement$attri !== void 0 && (_cheerioElement$attri2 = _cheerioElement$attri.src) !== null && _cheerioElement$attri2 !== void 0 && _cheerioElement$attri2.startsWith(`/wp-content`)) {
cheerioElement.attribs.src = `${wpUrl}${cheerioElement.attribs.src}`;
}
return {
match,
cheerioElement,
// @todo this is from when this function was just used for images
// remove this by refactoring
cheerioImg: cheerioElement
};
};
const getCheerioElementsFromMatches = ({
imgTagMatches,
wpUrl
}) => imgTagMatches.map(getCheerioElementFromMatch(wpUrl)).filter(({
cheerioImg: {
attribs
}
}) => {
if (!attribs.src) {
return false;
}
return (0, _validUrl.isWebUri)(encodeURI(attribs.src));
});
const getLargestSizeFromSizesAttribute = sizesString => {
const sizesStringsArray = sizesString.split(`,`);
return sizesStringsArray.reduce((largest, currentSizeString) => {
const maxWidth = currentSizeString.substring(currentSizeString.indexOf(`max-width: `) + 1, currentSizeString.indexOf(`px`)).trim();
const maxWidthNumber = Number(maxWidth);
const noLargestAndMaxWidthIsANumber = !largest && !isNaN(maxWidthNumber);
const maxWidthIsALargerNumberThanLargest = largest && !isNaN(maxWidthNumber) && maxWidthNumber > largest;
if (noLargestAndMaxWidthIsANumber || maxWidthIsALargerNumberThanLargest) {
largest = maxWidthNumber;
}
return largest;
}, null);
};
const findImgTagMaxWidthFromCheerioImg = cheerioImg => {
const {
attribs: {
width,
sizes
}
} = cheerioImg || {
attribs: {
width: null,
sizes: null
}
};
if (width) {
const widthNumber = Number(width);
if (!isNaN(widthNumber)) {
return widthNumber;
}
}
if (sizes) {
const largestSize = getLargestSizeFromSizesAttribute(sizes);
if (largestSize && !isNaN(largestSize)) {
return largestSize;
}
}
return null;
};
const getFileNodeRelativePathname = fileNode => {
const fileName = `${fileNode.internal.contentDigest}/${fileNode.base}`;
return fileName;
};
const getFileNodePublicPath = fileNode => {
const fileName = getFileNodeRelativePathname(fileNode);
const publicPath = _path.default.join(process.cwd(), `public`, `static`, fileName);
return publicPath;
};
const copyFileToStaticAndReturnUrlPath = async (fileNode, helpers) => {
var _helpers$pathPrefix;
const publicPath = getFileNodePublicPath(fileNode);
if (!_fsExtra.default.existsSync(publicPath)) {
await _fsExtra.default.copy(fileNode.absolutePath, publicPath, {
dereference: true
}, err => {
if (err) {
console.error(`error copying file from ${fileNode.absolutePath} to ${publicPath}`, err);
}
});
}
const fileName = getFileNodeRelativePathname(fileNode);
const relativeUrl = `${(_helpers$pathPrefix = helpers.pathPrefix) !== null && _helpers$pathPrefix !== void 0 ? _helpers$pathPrefix : ``}/static/${fileName}`;
return relativeUrl;
};
const cacheCreatedFileNodeBySrc = ({
node,
src
}) => {
if (node) {
// save any fetched media items in our global media item cache
(0, _store.getStore)().dispatch.imageNodes.pushNodeMeta({
sourceUrl: src,
id: node.id,
modifiedGmt: node.modifiedGmt
});
}
};
const imgSrcRemoteFileRegex = /(?:src=\\")((?:(?:https?|ftp|file):\/\/|www\.|ftp\.|\/)(?:[^'"])*\.(?:jpeg|jpg|png|gif|ico|mpg|ogv|svg|bmp|tif|tiff))(\?[^\\" \.]*|)(?=\\"| |\.)/gim;
const getImgSrcRemoteFileMatchesFromNodeString = nodeString => (0, _execall.default)(imgSrcRemoteFileRegex, nodeString).filter(({
subMatches
}) => {
// if our match is json encoded, that means it's inside a JSON
// encoded string field.
const isInJSON = subMatches[0].includes(`\\/\\/`);
// we shouldn't process encoded JSON, so skip this match if it's JSON
return !isInJSON;
});
exports.getImgSrcRemoteFileMatchesFromNodeString = getImgSrcRemoteFileMatchesFromNodeString;
const getImgTagMatches = ({
nodeString
}) => (0, _execall.default)(/<img([\w\W]+?)[\/]?>/gim, nodeString
// we don't want to match images inside pre
.replace(/<pre([\w\W]+?)[\/]?>(?:(?!<\/pre>).)+(<\/pre>)/gim, ``)
// and code tags, so temporarily remove those tags and everything inside them
.replace(/<code([\w\W]+?)[\/]?>(?:(?!<\/code>).)+(<\/code>)/gim, ``));
exports.getImgTagMatches = getImgTagMatches;
const replaceNodeHtmlImages = async ({
nodeString,
node,
helpers,
wpUrl,
pluginOptions
}) => {
var _pluginOptions$html, _pluginOptions$type2, _pluginOptions$type2$;
// this prevents fetching inline html images
if (!(pluginOptions !== null && pluginOptions !== void 0 && (_pluginOptions$html = pluginOptions.html) !== null && _pluginOptions$html !== void 0 && _pluginOptions$html.useGatsbyImage) || pluginOptions !== null && pluginOptions !== void 0 && (_pluginOptions$type2 = pluginOptions.type) !== null && _pluginOptions$type2 !== void 0 && (_pluginOptions$type2$ = _pluginOptions$type2.MediaItem) !== null && _pluginOptions$type2$ !== void 0 && _pluginOptions$type2$.exclude) {
return nodeString;
}
const imageUrlMatches = getImgSrcRemoteFileMatchesFromNodeString(nodeString);
const imgTagMatches = getImgTagMatches({
nodeString
});
if (imageUrlMatches.length && imgTagMatches.length) {
const cheerioImages = getCheerioElementsFromMatches({
imgTagMatches,
wpUrl
});
const htmlMatchesToMediaItemNodesMap = await fetchNodeHtmlImageMediaItemNodes({
cheerioImages,
nodeString,
node,
helpers,
pluginOptions,
wpUrl
});
// generate gatsby images for each cheerioImage
const htmlMatchesWithImageResizes = await Promise.all(imgTagMatches.map(async ({
match
}) => {
var _imageNode$localFile, _imageNode$mimeType, _imageNode$mediaDetai, _pluginOptions$html2, _ref2, _pluginOptions$html3, _pluginOptions$html$i, _pluginOptions$html4;
const matchInfo = htmlMatchesToMediaItemNodesMap.get(match);
if (!matchInfo) {
return null;
}
const {
imageNode,
cheerioImg
} = matchInfo;
const isMediaItemNode = imageNode.__typename === `MediaItem`;
if (!imageNode) {
return null;
}
const fileNode =
// if we couldn't get a MediaItem node for this image in WPGQL
!isMediaItemNode ?
// this will already be a file node
imageNode :
// otherwise grab the file node
helpers.getNode(imageNode === null || imageNode === void 0 ? void 0 : (_imageNode$localFile = imageNode.localFile) === null || _imageNode$localFile === void 0 ? void 0 : _imageNode$localFile.id);
const extension = imageNode === null || imageNode === void 0 ? void 0 : (_imageNode$mimeType = imageNode.mimeType) === null || _imageNode$mimeType === void 0 ? void 0 : _imageNode$mimeType.replace(`${imageNode === null || imageNode === void 0 ? void 0 : imageNode.mediaType}/`, ``);
const imgTagMaxWidth = findImgTagMaxWidthFromCheerioImg(cheerioImg);
const mediaItemNodeWidth = isMediaItemNode ? imageNode === null || imageNode === void 0 ? void 0 : (_imageNode$mediaDetai = imageNode.mediaDetails) === null || _imageNode$mediaDetai === void 0 ? void 0 : _imageNode$mediaDetai.width : null;
// if a max width can't be inferred from html, this value will be passed to Sharp
let fallbackImageMaxWidth = pluginOptions === null || pluginOptions === void 0 ? void 0 : (_pluginOptions$html2 = pluginOptions.html) === null || _pluginOptions$html2 === void 0 ? void 0 : _pluginOptions$html2.fallbackImageMaxWidth;
if (
// if the image is smaller than the fallback max width,
// the images width will be used instead if we have a media item node
fallbackImageMaxWidth > mediaItemNodeWidth &&
// of course that means we have to have a media item node
// and a media item node max width
mediaItemNodeWidth && typeof mediaItemNodeWidth === `number` && mediaItemNodeWidth > 0) {
fallbackImageMaxWidth = mediaItemNodeWidth;
}
let maxWidth = // if we inferred a maxwidth from html
(_ref2 = imgTagMaxWidth &&
// and we have a media item node to know it's full size max width
mediaItemNodeWidth &&
// and this isn't an svg which has no maximum width
extension !== `svg` &&
// and the media item node max width is smaller than what we inferred
// from html
mediaItemNodeWidth < imgTagMaxWidth ?
// use the media item node width
mediaItemNodeWidth :
// otherwise use the width inferred from html
imgTagMaxWidth) !== null && _ref2 !== void 0 ? _ref2 :
// if we don't have a media item node and we inferred no width
// from html, then use the fallback max width from plugin options
fallbackImageMaxWidth;
const configuredMaxWidth = pluginOptions === null || pluginOptions === void 0 ? void 0 : (_pluginOptions$html3 = pluginOptions.html) === null || _pluginOptions$html3 === void 0 ? void 0 : _pluginOptions$html3.imageMaxWidth;
// if the configured html.maxWidth property is less than the result, then
// override the resulting width
if (configuredMaxWidth && configuredMaxWidth < maxWidth) {
maxWidth = configuredMaxWidth;
}
const quality = (_pluginOptions$html$i = pluginOptions === null || pluginOptions === void 0 ? void 0 : (_pluginOptions$html4 = pluginOptions.html) === null || _pluginOptions$html4 === void 0 ? void 0 : _pluginOptions$html4.imageQuality) !== null && _pluginOptions$html$i !== void 0 ? _pluginOptions$html$i : 70;
const {
reporter
} = helpers;
const gatsbyTransformerSharpSupportsThisFileType = _supportedExtensions.supportedExtensions[extension] || extension === `gif`;
let imageResize = null;
let publicUrl;
const imageUrl = imageNode.mediaItemUrl || imageNode.sourceUrl || imageNode.url;
try {
if (gatsbyTransformerSharpSupportsThisFileType) {
var _pluginOptions$html5;
const placeholderUrl = getPlaceholderUrlFromMediaItemNode(imageNode, pluginOptions);
const formats = [`auto`];
if (pluginOptions.html.generateWebpImages) {
formats.push(`webp`);
}
if (pluginOptions.html.generateAvifImages) {
formats.push(`avif`);
}
imageResize = await (0, _polyfillRemoteFile.gatsbyImageResolver)({
url: imageUrl,
placeholderUrl,
mimeType: imageNode.mimeType,
width: imageNode.mediaDetails.width,
height: imageNode.mediaDetails.height,
filename: _path.default.basename(imageNode.mediaDetails.file),
internal: {
contentDigest: imageNode.modifiedGmt
}
}, {
width: maxWidth,
layout: `constrained`,
placeholder: !placeholderUrl ? `none` : (pluginOptions === null || pluginOptions === void 0 ? void 0 : (_pluginOptions$html5 = pluginOptions.html) === null || _pluginOptions$html5 === void 0 ? void 0 : _pluginOptions$html5.placeholderType) || `dominantColor`,
quality,
formats
}, helpers.actions, _redux.store);
} else {
publicUrl = (0, _polyfillRemoteFile.publicUrlResolver)({
url: imageUrl,
mimeType: imageNode.mimeType,
filename: _path.default.basename(imageNode.sourceUrl || imageNode.url),
internal: {
contentDigest: imageNode.modifiedGmt
}
}, helpers.actions, _redux.store);
}
} catch (e) {
reporter.error(e);
reporter.warn((0, _formatLogMessage.formatLogMessage)(`${node.__typename} ${node.id} couldn't process inline html image ${imageUrl}`));
return null;
}
return {
match,
cheerioImg,
fileNode,
imageResize,
maxWidth,
publicUrl
};
}));
// find/replace mutate nodeString to replace matched images with rendered gatsby images
let replaceIndex = 0;
for (const matchResize of htmlMatchesWithImageResizes) {
if (!matchResize) {
continue;
}
const {
match,
imageResize,
cheerioImg,
publicUrl
} = matchResize;
let ReactGatsbyImage;
// used to create hydration data for images
let gatsbyImageHydrationData = null;
if (imageResize && (imageResize.images.sources.length > 0 || imageResize.images.fallback)) {
var _cheerioImg$attribs$a, _cheerioImg$attribs, _cheerioImg$attribs2;
gatsbyImageHydrationData = {
image: imageResize,
// Wordpress tells users to leave "alt" empty if image is decorative. But it returns undefined, not ``
alt: (_cheerioImg$attribs$a = cheerioImg === null || cheerioImg === void 0 ? void 0 : (_cheerioImg$attribs = cheerioImg.attribs) === null || _cheerioImg$attribs === void 0 ? void 0 : _cheerioImg$attribs.alt) !== null && _cheerioImg$attribs$a !== void 0 ? _cheerioImg$attribs$a : ``,
className: `${(cheerioImg === null || cheerioImg === void 0 ? void 0 : (_cheerioImg$attribs2 = cheerioImg.attribs) === null || _cheerioImg$attribs2 === void 0 ? void 0 : _cheerioImg$attribs2.class) || ``} inline-gatsby-image-wrapper`,
"data-wp-inline-image": String(++replaceIndex)
};
ReactGatsbyImage = /*#__PURE__*/_react.default.createElement(_gatsbyPluginImage.GatsbyImage, gatsbyImageHydrationData, null);
} else if (publicUrl) {
var _cheerioImg$attribs$a2, _cheerioImg$attribs3, _cheerioImg$attribs4;
ReactGatsbyImage = /*#__PURE__*/_react.default.createElement(`img`, {
src: publicUrl,
// Wordpress tells users to leave "alt" empty if image is decorative. But it returns undefined, not ``
alt: (_cheerioImg$attribs$a2 = cheerioImg === null || cheerioImg === void 0 ? void 0 : (_cheerioImg$attribs3 = cheerioImg.attribs) === null || _cheerioImg$attribs3 === void 0 ? void 0 : _cheerioImg$attribs3.alt) !== null && _cheerioImg$attribs$a2 !== void 0 ? _cheerioImg$attribs$a2 : ``,
className: `${(cheerioImg === null || cheerioImg === void 0 ? void 0 : (_cheerioImg$attribs4 = cheerioImg.attribs) === null || _cheerioImg$attribs4 === void 0 ? void 0 : _cheerioImg$attribs4.class) || ``} inline-gatsby-image-wrapper`
}, null);
}
if (ReactGatsbyImage) {
let gatsbyImageStringRaw = _server.default.renderToString(ReactGatsbyImage);
// gatsby-plugin-image needs hydration data to work on navigations - we add the hydration data to the DOM to use it in gatsby-browser.ts
if (gatsbyImageHydrationData) {
gatsbyImageStringRaw += `<script type="application/json" data-wp-inline-image-hydration="${replaceIndex}">${JSON.stringify(gatsbyImageHydrationData)}</script>`;
}
// need to remove the JSON stringify quotes around our image since we're
// threading this JSON string back into a larger JSON object string
const gatsbyImageStringJSON = JSON.stringify(gatsbyImageStringRaw);
const gatsbyImageString = gatsbyImageStringJSON.substring(1, gatsbyImageStringJSON.length - 1);
nodeString = (0, _replaceall.default)(match, gatsbyImageString, nodeString);
}
}
}
return nodeString;
};
exports.replaceNodeHtmlImages = replaceNodeHtmlImages;
const replaceFileLinks = async ({
nodeString,
helpers,
wpUrl,
pluginOptions,
node
}) => {
var _pluginOptions$html6, _pluginOptions$type3, _pluginOptions$type3$;
if (!(pluginOptions !== null && pluginOptions !== void 0 && (_pluginOptions$html6 = pluginOptions.html) !== null && _pluginOptions$html6 !== void 0 && _pluginOptions$html6.createStaticFiles) || pluginOptions !== null && pluginOptions !== void 0 && (_pluginOptions$type3 = pluginOptions.type) !== null && _pluginOptions$type3 !== void 0 && (_pluginOptions$type3$ = _pluginOptions$type3.MediaItem) !== null && _pluginOptions$type3$ !== void 0 && _pluginOptions$type3$.exclude) {
return nodeString;
}
if (node.__typename === `MediaItem`) {
// we don't want to replace file links on MediaItem nodes because they're processed specially from other node types.
// if we replace file links here then we wont be able to properly fetch the localFile node
return nodeString;
}
const hrefMatches = [
// match url pathnames in html fields, for ex /wp-content/uploads/2019/01/image.jpg
...((0, _execall.default)(/(\\"|\\'|\()([^'"()]*)(\/wp-content\/uploads\/[^'">()]+)(\\"|\\'|>|\))/gm, nodeString) || []),
// match full urls in json fields, for ex https://example.com/wp-content/uploads/2019/01/image.jpg
...((0, _execall.default)(new RegExp(`(\\"|\\'|\\()([^'"()]*)(${wpUrl}\/wp-content\/uploads\/[^'">()]+)(\\"|\\'|>|\\))`, `gm`), nodeString) || [])];
if (hrefMatches.length) {
// eslint-disable-next-line arrow-body-style
const mediaItemUrlsAndMatches = hrefMatches.map(matchGroup => {
const match = matchGroup.subMatches[2];
const url = match.startsWith(wpUrl) ? match : `${wpUrl}${match}`;
return {
matchGroup,
url
};
});
const mediaItemUrls = mediaItemUrlsAndMatches.map(({
url
}) => url).filter(_validUrl.isWebUri);
const mediaItemNodesBySourceUrl = await (0, _fetchReferencedMediaItems.default)({
mediaItemUrls
});
const findReplaceMaps = [];
await Promise.all(mediaItemNodesBySourceUrl.map(async node => {
var _node$localFile, _mediaItemNode, _mediaItemUrlsAndMatc;
let fileNode;
let mediaItemNode;
if (node.internal.type === `File`) {
fileNode = node;
mediaItemNode = await helpers.getNode(node.parent);
} else if ((_node$localFile = node.localFile) !== null && _node$localFile !== void 0 && _node$localFile.id) {
fileNode = await helpers.getNode(node.localFile.id);
mediaItemNode = node;
} else {
return null;
}
const relativeUrl = await copyFileToStaticAndReturnUrlPath(fileNode, helpers);
if (!relativeUrl || !((_mediaItemNode = mediaItemNode) !== null && _mediaItemNode !== void 0 && _mediaItemNode.mediaItemUrl) || !fileNode) {
return null;
}
const mediaItemMatchGroup = (_mediaItemUrlsAndMatc = mediaItemUrlsAndMatches.find(({
matchGroup: {
subMatches: [,, path]
}
}) => mediaItemNode.mediaItemUrl.includes(path))) === null || _mediaItemUrlsAndMatc === void 0 ? void 0 : _mediaItemUrlsAndMatc.matchGroup;
if (!mediaItemMatchGroup) {
return null;
}
const [, hostname, path] = mediaItemMatchGroup.subMatches;
cacheCreatedFileNodeBySrc({
node: mediaItemNode,
src: `${wpUrl}${path}`
});
findReplaceMaps.push({
find: `${hostname || ``}${path}`,
replace: relativeUrl
});
findReplaceMaps.push({
find: path,
replace: relativeUrl
});
return null;
}));
for (const {
find,
replace
} of findReplaceMaps.filter(Boolean)) {
nodeString = (0, _replaceall.default)(find, replace, nodeString);
}
}
return nodeString;
};
const getWpLinkRegex = wpUrl => new RegExp(`["']${wpUrl}(?!/wp-content|/wp-admin|/wp-includes)(/[^'"]+)["']`, `gim`);
// replaces any url which is a front-end WP url with a relative path
exports.getWpLinkRegex = getWpLinkRegex;
const replaceNodeHtmlLinks = ({
wpUrl,
nodeString,
node
}) => {
const wpLinkRegex = getWpLinkRegex(wpUrl);
const linkMatches = (0, _execall.default)(wpLinkRegex, nodeString);
if (linkMatches.length) {
linkMatches.forEach(({
match,
subMatches: [path]
}) => {
if (path) {
try {
// remove \, " and ' characters from match
const normalizedMatch = match.replace(/['"\\]/g, ``)
// ensure that query params are properly quoted
.replace(/\?/, `\\?`);
const normalizedPath = path.replace(/\\/g, ``);
// replace normalized match with relative path
const thisMatchRegex = new RegExp(normalizedMatch + `(?!/?wp-content|/?wp-admin|/?wp-includes)`, `g`);
nodeString = nodeString.replace(thisMatchRegex, normalizedPath);
} catch (e) {
console.error(e);
console.warn((0, _formatLogMessage.formatLogMessage)(`Failed to process inline html links in ${node.__typename} ${node.id}`));
}
}
});
}
return nodeString;
};
// replaces specific string or regex with a given string from the plugin options config
const searchAndReplaceNodeStrings = ({
nodeString,
node,
pluginOptions
}) => {
if (Array.isArray(pluginOptions === null || pluginOptions === void 0 ? void 0 : pluginOptions.searchAndReplace)) {
pluginOptions.searchAndReplace.forEach(({
search,
replace
}) => {
const searchRegex = new RegExp(search, `g`);
const stringMatches = (0, _execall.default)(searchRegex, nodeString);
if (stringMatches.length) {
stringMatches.forEach(({
match
}) => {
if (match) {
try {
nodeString = nodeString.replace(search, replace);
} catch (e) {
console.error(e);
console.warn((0, _formatLogMessage.formatLogMessage)(`Failed to process search and replace string in ${node.__typename} ${node.id}`));
}
}
});
}
});
}
return nodeString;
};
exports.searchAndReplaceNodeStrings = searchAndReplaceNodeStrings;
const processNodeString = async ({
nodeString,
node,
pluginOptions,
helpers,
wpUrl
}) => {
const nodeStringFilters = [searchAndReplaceNodeStrings, replaceNodeHtmlImages, replaceFileLinks, replaceNodeHtmlLinks];
for (const nodeStringFilter of nodeStringFilters) {
nodeString = await nodeStringFilter({
nodeString,
node,
pluginOptions,
helpers,
wpUrl
});
}
return nodeString;
};
const processNode = async ({
node,
pluginOptions,
wpUrl,
helpers,
referencedMediaItemNodeIds
}) => {
const nodeString = (0, _fastJsonStableStringify.default)(node);
// find referenced node ids
// here we're searching for node id strings in our node
// we use this to download only the media items
// that are being used in posts
// this is important for downloading images nodes that are connected somewhere
// on a node field
const nodeMediaItemIdReferences = findReferencedImageNodeIds({
nodeString,
pluginOptions,
node
});
// push them to our store of referenced id's
if (nodeMediaItemIdReferences !== null && nodeMediaItemIdReferences !== void 0 && nodeMediaItemIdReferences.length && referencedMediaItemNodeIds) {
nodeMediaItemIdReferences.forEach(id => referencedMediaItemNodeIds.add(id));
}
const processedNodeString = await processNodeString({
nodeString,
node,
pluginOptions,
helpers,
wpUrl
});
const processedNode =
// only parse if the nodeString has changed
processedNodeString !== nodeString ? JSON.parse(processedNodeString) : node;
return {
processedNode,
nodeMediaItemIdReferences
};
};
exports.processNode = processNode;
//# sourceMappingURL=process-node.js.map