DEV

Wordpressからはてなブログへの引っ越しの際に、日本語URLがおかしいのと、シンタックスハイライトが反映されないのを解決した

Wordpressからはてなブログに引っ越しました

この度、はてなブログにお引っ越しをしました。めっちゃ便利だし、安いし、軽いしいい感じだと思いました。 引っ越し方法も基本は簡単で、Wordpressの管理画面の標準機能に、エクスポート機能があるので、それを使って、XMLファイルをエクスポートします。そして、はてなブログにも、そのXMLファイルをインポートする機能があるので、それを使うと一瞬で完了しました。さらに記事中の画像も勝手にインポートしてくれます。

2点だけ問題がありました

でも、下記2点だけ、問題として残りました。

  • 日本語URLが、なんかおかしい(wordpressのときのURLと微妙に異なる)
  • ソースコードのシンタックスハイライトが反映されなくなった

日本語URLが、なんかおかしい

URL内の日本語は、URLエンコードされますが、そのURLエンコードされている部分の、%という文字が、%25という3文字に変換されていました。それ以外の部分はwordpressのURLと同じでした。なんでだろうなあと思いつつも、はてなブログのAPIがあることを知りまして、API経由で、全記事のURL修正コードを作成しました。コードは下記にあります。

https://github.com/edo1z/wordpress_to_hatenagithub.com

全記事のURLは、修正できたのですが、その修正が何時まで経っても反映されませんでした。管理画面上では修正できてることになっているのですが、実際のURLが2日経っても変わりませんでした。

そこで、今度は、WordpressからエクスポートしたXMLファイル内のURLを全部修正することにしました。というのも、URLエンコード済みの文字列をさらにURLエンコードしようとすると、%が%25になる場合があるのが分かったからです。XMLファイルのURLエンコード済みのURLを、全部デコードしました。そのコードも、上記のgithubにおきました。これでやっと、日本語URLも含めて、完全にWordpressと一致しました。

const fs = require('fs')
const {Transform} = require('stream')
class UrlDecodeStream extends Transform {
constructor(options) {
super(options)
this.str = ''
}
write (data) {
let str = data.toString()
const idx = str.indexOf("\n")
if(idx > -1) {
let line = this.str + str.slice(0, idx)
line = this.decodeLink(line)
this.str = str.slice(idx)
this.emit('data', line)
} else {
this.str += str
}
}
end () {
this.emit('data', this.str)
}
decodeLink (str) {
const re = /(<link>)(.+?)(<\/link>)/gm
if(!str.match(re)) return str
return str.replace(re, (match, p1, p2, p3) => {
return p1 + decodeURI(p2) + p3
})
}
}
const reader = fs.createReadStream('wp.xml')
const writer = fs.createWriteStream('new.xml')
const decoder = new UrlDecodeStream()
reader.pipe(decoder).pipe(writer)

ソースコードのシンタックスハイライトが反映されなくなった

今まで、blogger、wordpressのプラグインを何度か変更、からのはてなブログにお引っ越しだったので、シンタックスハイライト用のpre, codeタグに関するclassの付与の仕方や、そもそもcodeタグがなかったり、タグが&gtになってたりなってなかったりみたいな感じでカオスでした。

そこで、これもAPIで全記事修正することにしました。さすがに全てのソースコード表示箇所は、preタグでは囲まれていたので、記事中のpreを探して、内部の&gtを>に直して、preとcodeタグを削除して、Markdownのコード表示で囲むようにしました。その際に指定するコードの種類は、記事が登録されているカテゴリから決定するようにしました。これも、上記githubにおきました。これで全記事のソースコードでシンタックスハイライトが反映されました。

const {get, post, put, endpoint, baseUrl} = require('./hatena.api')
const xml2js = require('xml2js')
const parse = xml2js.parseString
const builder = new xml2js.Builder()
fixCodeLoop()
async function fixCodeLoop (nextPage = null) {
return new Promise( async (resolve, reject) => {
const url = nextPage ? nextPage : endpoint + '/entry'
const xml = await get(url).catch(err => reject(err))
parse(xml, async (err, res) => {
if (err) reject(err)
nextPage = getNextPage(res.feed.link)
const blogPosts = res.feed.entry
for(let i = 0; i < blogPosts.length; i++) {
await updateCode(blogPosts[i]).catch(err => reject(err))
}
if (nextPage) return fixCodeLoop(nextPage)
resolve('fin')
})
})
}
const getNextPage = links => {
for(let i = 0; i < links.length; i++) {
if (links[i].$.rel === 'next') {
return links[i].$.href
}
}
return null
}
const updateCode = async (blogPost) => {
return new Promise((resolve, reject) => {
let content = blogPost.content[0]._
const categories = blogPost.category
if(content) {
content = searchAndFixCode(content, categories)
blogPost.content[0]._ = content
update(blogPost)
.then(() => resolve())
.catch(err => reject(err))
} else {
resolve()
}
})
}
const getLanguage = (categories) => {
const javascript = ['javascript', 'node.js', 'vue.js', 'webpack', 'react', 'cordova']
const php = ['php', 'cakephp', 'fuelphp', 'laravel']
const nginx = ['nginx']
const go = ['go']
const python = ['python', 'tensorflow']
const ruby = ['ruby', 'rails', 'capistrano']
const zsh = ['shell', 'zsh', 'ubuntu', 'linux']
const java = ['java', 'android']
const swift = ['swift', 'ios']
for(let i = 0; i < categories.length; i++) {
let category = categories[i].$.term.toLowerCase()
if(python.indexOf(category) !== -1) return 'python'
if(go.indexOf(category) !== -1) return 'go'
if(java.indexOf(category) !== -1) return 'java'
if(swift.indexOf(category) !== -1) return 'swift'
if(javascript.indexOf(category) !== -1) return 'javascript'
if(php.indexOf(category) !== -1) return 'php'
if(nginx.indexOf(category) !== -1) return 'nginx'
if(ruby.indexOf(category) !== -1) return 'ruby'
if(zsh.indexOf(category) !== -1) return 'zsh'
return 'javascript'
}
}
const searchAndFixCode = (content, categories) => {
const rePre = /(<pre[\s\S]*?>([\s\S]+?)<\/pre>)/gm
const res = content.match(rePre);
if(!res) return content
const lang = getLanguage(categories)
const codeStartStr = "\n" + '```' + lang + "\n"
const codeEndStr = "\n" + '```' + "\n"
const reCode = /(<code[\s\S]*?>([\s\S]+?)<\/code>)/gm
const reGt = /&gt;/g
const reLt = /&lt;/g
return content.replace(rePre, (match, p1, p2) => {
p2 = p2.replace(reCode, '$2')
p2 = p2.replace(reGt, '>')
p2 = p2.replace(reLt, '<')
return codeStartStr + p2 + codeEndStr
})
}
const update = async (blogPost) => {
const editLink = getEditUrl(blogPost.link)
return new Promise((resolve, reject) => {
let obj = { 'entry': blogPost }
obj.entry.$ = {
'xmlns': 'http://www.w3.org/2005/Atom',
'xmlns:app': 'http://www.w3.org/2007/app'
}
delete(obj.entry.published)
delete(obj.entry['app:edited'])
delete(obj.entry.summary)
delete(obj.entry.id)
delete(obj.entry.link)
put(editLink, builder.buildObject(obj))
.then(() => resolve())
.catch(err => reject(err))
})
}
const getEditUrl = links => {
for(let i = 0; i < links.length; i++) {
if(links[i].$.rel === 'edit') {
return links[i].$.href
}
}
}