diff --git a/src/frontmatter.js b/src/frontmatter.js index 3e34b9e..bae2b64 100644 --- a/src/frontmatter.js +++ b/src/frontmatter.js @@ -1,74 +1,62 @@ -// get author, without decoding -// WordPress doesn't allow funky characters in usernames anyway +import * as shared from './shared.js'; + export function author(post) { - return post.data.creator[0]; + // not decoded, WordPress doesn't allow funky characters in usernames anyway + return shared.getValue(post.data, 'creator', 0); } -// get array of decoded category names, excluding 'uncategorized' export function categories(post) { - if (!post.data.category) { - return []; - } - - const categories = post.data.category - .filter(category => category.$.domain === 'category') - .map(({ $: attributes }) => decodeURIComponent(attributes.nicename)); - - return categories.filter((category) => category !== 'uncategorized'); + // array of decoded category names, excluding 'uncategorized' + const categories = shared.getOptionalValue(post.data, 'category') ?? []; + return categories + .filter((category) => category.$.domain === 'category' && category.$.nicename !== 'uncategorized') + .map((category) => decodeURIComponent(category.$.nicename)); } -// get cover image filename, previously decoded and set on post -// this one is unique as it relies on special logic executed by the parser export function coverImage(post) { + // cover image filename, previously parsed and decoded return post.coverImage; } -// get post date, previously saved as a luxon datetime object on post export function date(post) { + // a luxon datetime object, previously parsed return post.date; } -// get boolean indicating if post is a draft -// this will only be included if true, otherwise it's left off export function draft(post) { + // boolean representing the previously parsed draft status, only included when true return post.isDraft ? true : undefined; } -// get excerpt, not decoded, newlines collapsed export function excerpt(post) { - return post.data.encoded[1].replace(/[\r\n]+/gm, ' '); + // not decoded, newlines collapsed + return shared.getValue(post.data, 'encoded', 1).replace(/[\r\n]+/gm, ' '); } -// get ID, as an integer export function id(post) { + // previously parsed as a string, converted to integer here return parseInt(post.id); } -// get slug, previously decoded and set on post export function slug(post) { + // previously parsed and decoded return post.slug; } -// get array of decoded tag names export function tags(post) { - if (!post.data.category) { - return []; - } - - const categories = post.data.category - .filter(category => category.$.domain === 'post_tag') - .map(({ $: attributes }) => decodeURIComponent(attributes.nicename)); - - return categories; + // array of decoded tag names (yes, they come from nodes, not a typo) + const categories = shared.getOptionalValue(post.data, 'category') ?? []; + return categories + .filter((category) => category.$.domain === 'post_tag') + .map((category) => decodeURIComponent(category.$.nicename)); } -// get simple post title, but not decoded like other frontmatter string fields export function title(post) { - return post.data.title[0]; + // not decoded + return shared.getValue(post.data, 'title', 0); } -// get type, often this will always be "post" -// but can also be "page" or other custom types export function type(post) { + // previously parsed but not decoded, can be "post", "page", or other custom types return post.type; } diff --git a/src/parser.js b/src/parser.js index 053432c..daed8a6 100644 --- a/src/parser.js +++ b/src/parser.js @@ -21,7 +21,7 @@ export async function parseFilePromise() { if (rssData === undefined) { throw new Error('Could not find root node. This likely means your import file is malformed.') } - rssData['wetm-expression'] = 'rss'; + rssData['wetm-expression'] = 'rss'; const channelData = shared.getValue(rssData, 'channel', 0); const allPostData = shared.getValue(channelData, 'item'); @@ -46,7 +46,7 @@ export async function parseFilePromise() { function getPostTypes(allPostData) { // search export file for all post types minus some specific types we don't want const types = allPostData - .map(item => item.post_type[0]) + .map(item => shared.getValue(item, 'post_type', 0)) .filter(type => ![ 'attachment', 'revision', @@ -65,15 +65,15 @@ function getPostTypes(allPostData) { } function getItemsOfType(allPostData, type) { - return allPostData.filter(item => item.post_type[0] === type); + return allPostData.filter(item => shared.getValue(item, 'post_type', 0) === type); } function collectPosts(allPostData, postTypes) { let allPosts = []; postTypes.forEach(postType => { const postsForType = getItemsOfType(allPostData, postType) - .filter(postData => postData.status[0] !== 'trash') - .filter(postData => !(postType === 'page' && postData.post_name[0] === 'sample-page')) + .filter(postData => shared.getValue(postData, 'status', 0) !== 'trash') + .filter(postData => !(postType === 'page' && shared.getValue(postData, 'post_name', 0) === 'sample-page')) .map(postData => buildPost(postData)); if (postsForType.length > 0) { @@ -92,15 +92,15 @@ function buildPost(data) { data, // body content converted to markdown - content: translator.getPostContent(data.encoded[0]), + content: translator.getPostContent(shared.getValue(data, 'encoded', 0)), // particularly useful values for all sorts of things - type: data.post_type[0], - id: data.post_id[0], - isDraft: data.status[0] === 'draft', - slug: decodeURIComponent(data.post_name[0]), + type: shared.getValue(data, 'post_type', 0), + id: shared.getValue(data, 'post_id', 0), + isDraft: shared.getValue(data, 'status', 0) === 'draft', + slug: decodeURIComponent(shared.getValue(data, 'post_name', 0)), date: getPostDate(data), - coverImageId: getPostMetaValue(data.postmeta, '_thumbnail_id'), + coverImageId: getPostMetaValue(data, '_thumbnail_id'), // these are possibly set later in mergeImagesIntoPosts() coverImage: undefined, @@ -109,23 +109,27 @@ function buildPost(data) { } function getPostDate(data) { - const date = luxon.DateTime.fromRFC2822(data.pubDate[0] ?? '', { zone: shared.config.customDateTimezone }); + const date = luxon.DateTime.fromRFC2822(shared.getValue(data, 'pubDate', 0) ?? '', { zone: shared.config.customDateTimezone }); return date.isValid ? date : undefined; } -function getPostMetaValue(metas, key) { - const meta = metas && metas.find((meta) => meta.meta_key[0] === key); - return meta ? meta.meta_value[0] : undefined; +function getPostMetaValue(data, key) { + const metas = shared.getOptionalValue(data, 'postmeta'); + const meta = metas && metas.find((meta) => shared.getValue(meta, 'meta_key', 0) === key); + return meta ? shared.getValue(meta, 'meta_value', 0) : undefined; } function collectAttachedImages(allPostData) { const images = getItemsOfType(allPostData, 'attachment') // filter to certain image file types - .filter(attachment => attachment.attachment_url && (/\.(gif|jpe?g|png|webp)$/i).test(attachment.attachment_url[0])) + .filter(attachment => { + const url = shared.getOptionalValue(attachment, 'attachment_url', 0); + return url && (/\.(gif|jpe?g|png|webp)$/i).test(url); + }) .map(attachment => ({ - id: attachment.post_id[0], - postId: attachment.post_parent[0], - url: attachment.attachment_url[0] + id: shared.getValue(attachment, 'post_id', 0), + postId: shared.getValue(attachment, 'post_parent', 0), + url: shared.getValue(attachment, 'attachment_url', 0) })); console.log(images.length + ' attached images found.'); @@ -136,9 +140,9 @@ function collectScrapedImages(allPostData, postTypes) { const images = []; postTypes.forEach(postType => { getItemsOfType(allPostData, postType).forEach(postData => { - const postId = postData.post_id[0]; - const postContent = postData.encoded[0]; - const postLink = postData.link[0]; + const postId = shared.getValue(postData, 'post_id', 0); + const postContent = shared.getValue(postData, 'encoded', 0); + const postLink = shared.getValue(postData, 'link', 0); const matches = [...postContent.matchAll(/]*src="(.+?\.(?:gif|jpe?g|png|webp))"[^>]*>/gi)]; matches.forEach(match => { diff --git a/src/shared.js b/src/shared.js index 1a2216d..e15cb02 100644 --- a/src/shared.js +++ b/src/shared.js @@ -22,6 +22,7 @@ export function getValue(obj, propName, index) { if (index === undefined) { values.forEach((value, index) => { value['wetm-expression'] = `${expression}[${index}]`; + // console.log('>>>', value['wetm-expression']); }); return values; } else { @@ -32,7 +33,10 @@ export function getValue(obj, propName, index) { throw new Error(`Could not find ${expression}.`) } - value['wetm-expression'] = expression; + if (typeof value === 'object') { + value['wetm-expression'] = expression; + // console.log('>>>', value['wetm-expression']); + } return value; } }