mirror of
https://github.com/10h30/wordpress-export-to-markdown.git
synced 2026-06-05 15:09:59 +09:00
+6
-1
@@ -24,11 +24,16 @@ export function coverImage(post) {
|
||||
}
|
||||
|
||||
// get post date, previously saved as a luxon datetime object on post
|
||||
// this value is also used for year/month folders, date prefixes, etc. as needed
|
||||
export function date(post) {
|
||||
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) {
|
||||
return post.isDraft ? true : undefined;
|
||||
}
|
||||
|
||||
// get excerpt, not decoded, newlines collapsed
|
||||
export function excerpt(post) {
|
||||
return post.data.encoded[1].replace(/[\r\n]+/gm, ' ');
|
||||
|
||||
+6
-1
@@ -156,5 +156,10 @@ function normalize(value, type, onError) {
|
||||
}
|
||||
|
||||
export function buildSamplePostPath(overrideConfig) {
|
||||
return shared.buildPostPath('', luxon.DateTime.now(), 'my-post', overrideConfig);
|
||||
const samplePost = {
|
||||
date: luxon.DateTime.now(),
|
||||
slug: 'my-post'
|
||||
};
|
||||
|
||||
return shared.buildPostPath(samplePost, overrideConfig);
|
||||
}
|
||||
|
||||
+20
-12
@@ -41,8 +41,13 @@ function getPostTypes(channelData) {
|
||||
'nav_menu_item',
|
||||
'custom_css',
|
||||
'customize_changeset',
|
||||
'oembed_cache',
|
||||
'user_request',
|
||||
'wp_block',
|
||||
'wp_global_styles',
|
||||
'wp_navigation'
|
||||
'wp_navigation',
|
||||
'wp_template',
|
||||
'wp_template_part'
|
||||
].includes(type));
|
||||
return [...new Set(types)]; // remove duplicates
|
||||
}
|
||||
@@ -52,15 +57,12 @@ function getItemsOfType(channelData, type) {
|
||||
}
|
||||
|
||||
function collectPosts(channelData, postTypes) {
|
||||
// this is passed into getPostContent() for the markdown conversion
|
||||
const turndownService = translator.initTurndownService();
|
||||
|
||||
let allPosts = [];
|
||||
postTypes.forEach(postType => {
|
||||
const postsForType = getItemsOfType(channelData, postType)
|
||||
.filter(postData => postData.status[0] !== 'trash' && postData.status[0] !== 'draft')
|
||||
.filter(postData => postData.status[0] !== 'trash')
|
||||
.filter(postData => !(postType === 'page' && postData.post_name[0] === 'sample-page'))
|
||||
.map(postData => buildPost(postData, turndownService));
|
||||
.map(postData => buildPost(postData));
|
||||
|
||||
if (postsForType.length > 0) {
|
||||
console.log(`${postsForType.length} posts of type "${postType}" found.`);
|
||||
@@ -72,19 +74,20 @@ function collectPosts(channelData, postTypes) {
|
||||
return allPosts;
|
||||
}
|
||||
|
||||
function buildPost(data, turndownService) {
|
||||
function buildPost(data) {
|
||||
return {
|
||||
// full raw post data
|
||||
data,
|
||||
|
||||
// contents of the post in markdown
|
||||
content: translator.getPostContent(data, turndownService),
|
||||
// body content converted to markdown
|
||||
content: translator.getPostContent(data.encoded[0]),
|
||||
|
||||
// these are not written to file, but help with other things
|
||||
// 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]),
|
||||
date: luxon.DateTime.fromRFC2822(data.pubDate[0], { zone: shared.config.customDateTimezone }),
|
||||
date: getPostDate(data),
|
||||
coverImageId: getPostMetaValue(data.postmeta, '_thumbnail_id'),
|
||||
|
||||
// these are possibly set later in mergeImagesIntoPosts()
|
||||
@@ -93,6 +96,11 @@ function buildPost(data, turndownService) {
|
||||
};
|
||||
}
|
||||
|
||||
function getPostDate(data) {
|
||||
const date = luxon.DateTime.fromRFC2822(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;
|
||||
@@ -171,7 +179,7 @@ function populateFrontmatter(posts) {
|
||||
throw `Could not find a frontmatter getter named "${key}".`;
|
||||
}
|
||||
|
||||
post.frontmatter[alias || key] = frontmatterGetter(post);
|
||||
post.frontmatter[alias ?? key] = frontmatterGetter(post);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ export function load() {
|
||||
{
|
||||
name: 'frontmatter-fields',
|
||||
type: 'list',
|
||||
default: ['title', 'date', 'categories', 'tags', 'coverImage']
|
||||
default: ['title', 'date', 'categories', 'tags', 'coverImage', 'draft']
|
||||
},
|
||||
{
|
||||
name: 'image-file-request-delay',
|
||||
|
||||
+33
-14
@@ -7,31 +7,50 @@ export function camelCase(str) {
|
||||
return str.replace(/-(.)/g, (match) => match[1].toUpperCase());
|
||||
}
|
||||
|
||||
export function buildPostPath(type, date, slug, overrideConfig) {
|
||||
export function getSlugWithFallback(post) {
|
||||
return post.slug ? post.slug : 'id-' + post.id;
|
||||
}
|
||||
|
||||
export function buildPostPath(post, overrideConfig) {
|
||||
const pathConfig = overrideConfig ?? config;
|
||||
|
||||
// start with base output dir and post type
|
||||
const pathSegments = [pathConfig.output, type];
|
||||
// start with output folder
|
||||
const pathSegments = [pathConfig.output];
|
||||
|
||||
if (pathConfig.dateFolders === 'year' || pathConfig.dateFolders === 'year-month') {
|
||||
pathSegments.push(date.toFormat('yyyy'));
|
||||
// add folder for post type if exists
|
||||
if (post.type) {
|
||||
pathSegments.push(post.type);
|
||||
}
|
||||
|
||||
if (pathConfig.dateFolders === 'year-month') {
|
||||
pathSegments.push(date.toFormat('LL'));
|
||||
// add drafts folder if this is a draft post
|
||||
if (post.isDraft) {
|
||||
pathSegments.push('_drafts');
|
||||
}
|
||||
|
||||
// create slug fragment, possibly date prefixed
|
||||
let slugFragment = slug;
|
||||
if (pathConfig.prefixDate) {
|
||||
slugFragment = date.toFormat('yyyy-LL-dd') + '-' + slugFragment;
|
||||
// add folders for date year/month as appropriate
|
||||
if (post.date) {
|
||||
if (pathConfig.dateFolders === 'year' || pathConfig.dateFolders === 'year-month') {
|
||||
pathSegments.push(post.date.toFormat('yyyy'));
|
||||
}
|
||||
|
||||
if (pathConfig.dateFolders === 'year-month') {
|
||||
pathSegments.push(post.date.toFormat('LL'));
|
||||
}
|
||||
}
|
||||
|
||||
// use slug fragment as folder or filename as specified
|
||||
// get slug with fallback
|
||||
let slug = getSlugWithFallback(post);
|
||||
|
||||
// prepend date to slug as appropriate
|
||||
if (pathConfig.prefixDate && post.date) {
|
||||
slug = post.date.toFormat('yyyy-LL-dd') + '-' + slug;
|
||||
}
|
||||
|
||||
// use slug as folder or filename as specified
|
||||
if (pathConfig.postFolders) {
|
||||
pathSegments.push(slugFragment, 'index.md');
|
||||
pathSegments.push(slug, 'index.md');
|
||||
} else {
|
||||
pathSegments.push(slugFragment + '.md');
|
||||
pathSegments.push(slug + '.md');
|
||||
}
|
||||
|
||||
return path.join(...pathSegments);
|
||||
|
||||
+6
-5
@@ -2,7 +2,10 @@ import turndownPluginGfm from '@guyplusplus/turndown-plugin-gfm';
|
||||
import turndown from 'turndown';
|
||||
import * as shared from './shared.js';
|
||||
|
||||
export function initTurndownService() {
|
||||
// init single reusable turndown service object upon import
|
||||
const turndownService = initTurndownService();
|
||||
|
||||
function initTurndownService() {
|
||||
const turndownService = new turndown({
|
||||
headingStyle: 'atx',
|
||||
bulletListMarker: '-',
|
||||
@@ -87,7 +90,7 @@ export function initTurndownService() {
|
||||
return node.nodeName === 'PRE' && !node.querySelector('code');
|
||||
},
|
||||
replacement: (content, node) => {
|
||||
const language = node.getAttribute('data-wetm-language') || '';
|
||||
const language = node.getAttribute('data-wetm-language') ?? '';
|
||||
return '\n\n```' + language + '\n' + node.textContent + '\n```\n\n';
|
||||
}
|
||||
});
|
||||
@@ -95,9 +98,7 @@ export function initTurndownService() {
|
||||
return turndownService;
|
||||
}
|
||||
|
||||
export function getPostContent(postData, turndownService) {
|
||||
let content = postData.encoded[0];
|
||||
|
||||
export function getPostContent(content) {
|
||||
// insert an empty div element between double line breaks
|
||||
// this nifty trick causes turndown to keep adjacent paragraphs separated
|
||||
// without mucking up content inside of other elements (like <code> blocks)
|
||||
|
||||
+7
-8
@@ -46,7 +46,7 @@ async function writeMarkdownFilesPromise(posts) {
|
||||
let skipCount = 0;
|
||||
let delay = 0;
|
||||
const payloads = posts.flatMap(post => {
|
||||
const destinationPath = buildPostPath(post);
|
||||
const destinationPath = shared.buildPostPath(post);
|
||||
if (checkFile(destinationPath)) {
|
||||
// already exists, don't need to save again
|
||||
skipCount++;
|
||||
@@ -55,7 +55,7 @@ async function writeMarkdownFilesPromise(posts) {
|
||||
const payload = {
|
||||
item: post,
|
||||
type: post.type,
|
||||
name: post.slug,
|
||||
name: shared.getSlugWithFallback(post),
|
||||
destinationPath,
|
||||
delay
|
||||
};
|
||||
@@ -96,9 +96,12 @@ async function loadMarkdownFilePromise(post) {
|
||||
if (shared.config.quoteDate) {
|
||||
outputValue = `"${outputValue}"`;
|
||||
}
|
||||
} else if (typeof value === 'boolean') {
|
||||
// output unquoted
|
||||
outputValue = value.toString();
|
||||
} else {
|
||||
// single string value
|
||||
const escapedValue = (value || '').replace(/"/g, '\\"');
|
||||
const escapedValue = (value ?? '').replace(/"/g, '\\"');
|
||||
if (escapedValue.length > 0) {
|
||||
outputValue = `"${escapedValue}"`;
|
||||
}
|
||||
@@ -118,7 +121,7 @@ async function writeImageFilesPromise(posts) {
|
||||
let skipCount = 0;
|
||||
let delay = 0;
|
||||
const payloads = posts.flatMap(post => {
|
||||
const postPath = buildPostPath(post);
|
||||
const postPath = shared.buildPostPath(post);
|
||||
const imagesDir = path.join(path.dirname(postPath), 'images');
|
||||
return post.imageUrls.flatMap(imageUrl => {
|
||||
const filename = shared.getFilenameFromUrl(imageUrl);
|
||||
@@ -185,10 +188,6 @@ async function loadImageFilePromise(imageUrl) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function buildPostPath(post) {
|
||||
return shared.buildPostPath(post.type, post.date, post.slug);
|
||||
}
|
||||
|
||||
function checkFile(path) {
|
||||
return fs.existsSync(path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user