-
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
1,122 additions
and
135 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,122 +1,4 @@ | ||
'use strict'; | ||
|
||
/* global hexo */ | ||
'use strict'; | ||
|
||
const xml2js = require('xml2js'); | ||
const async = require('async'); | ||
const TurndownService = require('turndown'); | ||
const request = require('request'); | ||
const file = require('fs'); | ||
|
||
const turndownService = new TurndownService(); | ||
|
||
const captialize = function(str) { | ||
return str[0].toUpperCase() + str.substring(1); | ||
}; | ||
|
||
function replaceTwoBrace(str) { | ||
str = str.replace(/{{/g, '{ {'); | ||
return str; | ||
} | ||
|
||
hexo.extend.migrator.register('wordpress', (args, callback) => { | ||
const source = args._.shift(); | ||
|
||
if (!source) { | ||
const help = [ | ||
'Usage: hexo migrate wordpress <source>', | ||
'', | ||
'For more help, you can check the docs: http://hexo.io/docs/migration.html' | ||
]; | ||
|
||
console.log(help.join('\n')); | ||
return callback(); | ||
} | ||
|
||
const log = hexo.log; | ||
const post = hexo.post; | ||
|
||
log.i('Analyzing %s...', source); | ||
|
||
async.waterfall([ | ||
function(next) { | ||
// URL regular expression from: http://blog.mattheworiordan.com/post/13174566389/url-regular-expression-for-links-with-or-without-the | ||
if (source.match(/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\w]*))?)/)) { | ||
request(source, (err, res, body) => { | ||
if (err) throw err; | ||
if (res.statusCode === 200) next(null, body); | ||
}); | ||
} else { | ||
file.readFile(source, next); | ||
} | ||
}, | ||
function(content, next) { | ||
xml2js.parseString(content, next); | ||
}, | ||
function(xml, next) { | ||
let count = 0; | ||
|
||
async.each(xml.rss.channel[0].item, (item, next) => { | ||
if (!item['wp:post_type']) { | ||
return next(); | ||
} | ||
|
||
const title = item.title[0].replace(/"/g, '\\"'); | ||
const id = item['wp:post_id'][0]; | ||
const date = item['wp:post_date'][0]; | ||
const slug = item['wp:post_name'][0]; | ||
let content = item['content:encoded'][0]; | ||
const comment = item['wp:comment_status'][0]; | ||
const status = item['wp:status'][0]; | ||
const type = item['wp:post_type'][0]; | ||
const categories = []; | ||
const tags = []; | ||
|
||
if (!title && !slug) return next(); | ||
if (type !== 'post' && type !== 'page') return next(); | ||
if (typeof content !== 'string') content = ''; | ||
content = replaceTwoBrace(content); | ||
content = turndownService.turndown(content).replace(/\r\n/g, '\n'); | ||
count++; | ||
|
||
if (item.category) { | ||
item.category.forEach((category, next) => { | ||
const name = category._; | ||
|
||
switch (category.$.domain) { | ||
case 'category': | ||
categories.push(name); | ||
break; | ||
|
||
case 'post_tag': | ||
tags.push(name); | ||
break; | ||
} | ||
}); | ||
} | ||
|
||
const data = { | ||
title: title || slug, | ||
url: +id + '.html', | ||
id: +id, | ||
date: date, | ||
content: content, | ||
layout: status === 'draft' ? 'draft' : 'post' | ||
}; | ||
|
||
if (type === 'page') data.layout = 'page'; | ||
if (slug) data.slug = slug; | ||
if (comment === 'closed') data.comments = false; | ||
if (categories.length && type === 'post') data.categories = categories; | ||
if (tags.length && type === 'post') data.tags = tags; | ||
|
||
log.i('%s found: %s', captialize(type), title); | ||
post.create(data, next); | ||
}, err => { | ||
if (err) return next(err); | ||
|
||
log.i('%d posts migrated.', count); | ||
}); | ||
} | ||
], callback); | ||
}); | ||
hexo.extend.migrator.register('wordpress', require('./lib/migrator')); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
'use strict'; | ||
|
||
/* ! | ||
* Ported from feed-furious 1.0.0 to support async-ed camaro v4+ | ||
* Licensed MIT (c) 2017 Tuan Anh Tran <https://tuananh.org/> | ||
* https://github.com/tuananh/feed-furious | ||
*/ | ||
|
||
const { transform } = require('camaro'); | ||
|
||
const template = { | ||
wp: { | ||
items: ['//item', { | ||
title: 'title', | ||
link: 'link', | ||
date: 'wp:post_date|pubDate', | ||
description: 'excerpt:encoded|description', | ||
id: 'wp:post_id', | ||
content: 'content:encoded', | ||
comment: 'wp:comment_status', | ||
slug: 'wp:post_name', | ||
status: 'wp:status', | ||
type: 'wp:post_type', | ||
tags: ['category', '.'] | ||
}] | ||
} | ||
}; | ||
|
||
const detectFeedType = async xml => { | ||
const sample = await transform(xml, { | ||
wp: 'rss/channel/title' | ||
}); | ||
|
||
if (sample.wp) return 'wp'; | ||
throw new Error('invalid format'); | ||
}; | ||
|
||
const parseFeed = async xml => { | ||
const type = await detectFeedType(xml); | ||
const output = await transform(xml, template[type]); | ||
return output; | ||
}; | ||
|
||
module.exports = parseFeed; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
'use strict'; | ||
|
||
const TurndownService = require('turndown'); | ||
const got = require('got'); | ||
const { parse: parseUrl } = require('url'); | ||
const { exists, listDir, readFile } = require('hexo-fs'); | ||
const parseFeed = require('./feed'); | ||
const { slugize } = require('hexo-util'); | ||
const { join, parse } = require('path'); | ||
const { unescape } = require('querystring'); | ||
|
||
module.exports = async function(args) { | ||
const source = args._.shift(); | ||
const { alias } = args; | ||
let { limit } = args; | ||
const skipduplicate = typeof args.skipduplicate !== 'undefined'; | ||
const tomd = new TurndownService(); | ||
const { config, log } = this; | ||
const Post = this.post; | ||
let untitledPostCounter = 0; | ||
let errNum = 0; | ||
let skipNum = 0; | ||
let input, feed; | ||
const rExcerpt = /<a id="more"><\/a>/i; | ||
const postExcerpt = '\n<!-- more -->\n'; | ||
const posts = []; | ||
let currentPosts = []; | ||
|
||
try { | ||
if (!source) { | ||
const help = [ | ||
'Usage: hexo migrate wordpress <source> [--options]', | ||
'', | ||
'For more help, you can check the docs: https://github.com/hexojs/hexo-migrator-wordpress/blob/master/README.md' | ||
]; | ||
|
||
throw help.join('\n'); | ||
} | ||
|
||
if (/^http(s)?:\/\//i.test(source)) { | ||
input = await got(source, { resolveBodyOnly: true, retry: 0 }); | ||
} else { | ||
input = await readFile(source); | ||
} | ||
|
||
log.i('Analyzing %s...', source); | ||
|
||
feed = await parseFeed(input); | ||
} catch (err) { | ||
throw new Error(err); | ||
} | ||
|
||
if (feed) { | ||
if (typeof limit !== 'number' || limit > feed.items.length || limit <= 0) limit = feed.items.length; | ||
let postLimit = 0; | ||
|
||
for (const item of feed.items) { | ||
if (postLimit >= limit) continue; | ||
|
||
const { link, date, id, comment, slug, status, type, tags } = item; | ||
let { title, content, description } = item; | ||
|
||
const layout = status === 'draft' ? 'draft' : 'post'; | ||
content = tomd.turndown(content).replace(/\r\n/g, '\n'); | ||
|
||
if (type !== 'page') { | ||
// Apply 'limit' option to post only | ||
postLimit++; | ||
|
||
if (rExcerpt.test(content)) { | ||
content.replace(rExcerpt, postExcerpt); | ||
} else if (description) { | ||
description = tomd.turndown(description).replace(/\r\n/g, '\n'); | ||
content = description + postExcerpt + content; | ||
} | ||
} | ||
|
||
if (!title) { | ||
untitledPostCounter += 1; | ||
const untitledPostTitle = 'Untitled Post - ' + untitledPostCounter; | ||
title = untitledPostTitle; | ||
log.w('Post found but without any titles. Using %s', untitledPostTitle); | ||
} else { | ||
log.i('Post found: %s', title); | ||
} | ||
|
||
const data = { | ||
title, | ||
id, | ||
date, | ||
content, | ||
layout, | ||
tags | ||
}; | ||
|
||
if (type === 'page') data.layout = 'page'; | ||
if (slug) data.slug = slug; | ||
if (slug && slug.includes('%')) data.slug = unescape(slug); | ||
if (comment === 'closed') data.comments = false; | ||
if (tags.length && type === 'post') data.tags = tags; | ||
|
||
if (alias && link) { | ||
data.alias = parseUrl(link).pathname; | ||
} | ||
|
||
posts.push(data); | ||
} | ||
} | ||
|
||
if (skipduplicate) { | ||
const postFolder = join(config.source_dir, '_posts'); | ||
const folderExist = await exists(postFolder); | ||
const files = folderExist ? await listDir(join(config.source_dir, '_posts')) : []; | ||
currentPosts = files.map(file => slugize(parse(file).name, { transform: 1 })); | ||
} | ||
|
||
if (posts.length >= 1) { | ||
for (const post of posts) { | ||
if (currentPosts.length && skipduplicate) { | ||
if (currentPosts.includes(slugize(post.title, { transform: 1 }))) { | ||
skipNum++; | ||
continue; | ||
} | ||
} | ||
|
||
try { | ||
await Post.create(post); | ||
} catch (err) { | ||
log.error(err); | ||
errNum++; | ||
} | ||
} | ||
|
||
const postsNum = posts.length - errNum - skipNum; | ||
|
||
if (untitledPostCounter) { | ||
log.w('%d posts did not have titles and were prefixed with "Untitled Post".', untitledPostCounter); | ||
} | ||
if (postsNum) log.i('%d posts migrated.', postsNum); | ||
if (errNum) log.error('%d posts failed to migrate.', posts.length); | ||
if (skipNum) log.i('%d posts skipped.', skipNum); | ||
} | ||
}; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.