脱WordPressをしました
久しぶりに見た目を変更しました。そもそも更新がかなり久しぶりです。
WordPressは完全にやめてしまって、静的ブログジェネレーターを作成し、静的サイトとしてブログを上げています。
TypeScriptをまじめに触ってみたかったこともあり、TypeScriptで書きましたが、TypeScriptの性能を生かし切れてはいない気がするので普通のVanilla-node(こういう言い方する?)でもよかった気がします。
TypeScript
- const fs = require('fs').promises
- const fse = require('fs-extra')
- const pug = require('pug')
- const stylus = require('stylus')
- const CleanCSS = require('clean-css')
- const minify = require('html-minifier').minify
- class Blog {
- entryList = []
- constructor() {
- (async () => {
- this.entryList = await this.getList()
- await this.cleanDist()
- this.list()
- this.entry()
- this.static()
- })()
- }
- // dist
- async cleanDist(): Promise<number> {
- await fse.remove('dist')
- await fs.mkdir('dist')
- return 1
- }
- // 記事一覧の取得
- async getList(): Promise<any> {
- // 過去記事の取得
- const oldList1 = JSON.parse(await fs.readFile('entry/old/entry-01.json', 'utf8'))
- const oldList2 = JSON.parse(await fs.readFile('entry/old/entry-02.json', 'utf8'))
-
- let oldList = [...oldList1, ...oldList2]
-
- oldList.map(entry => {
- delete entry.id
- delete entry.date_gmt
- delete entry.guid
- delete entry.modified_gmt
- delete entry.type
- delete entry.link
- delete entry.excerpt
- delete entry.author
- delete entry.featured_media
- delete entry.comment_status
- delete entry.ping_status
- delete entry.sticky
- delete entry.template
- delete entry.format
- delete entry.meta
- delete entry.categories
- delete entry.post_meta.NoAMP
- delete entry.post_meta.ogpimg
- delete entry.post_meta.thumb
- delete entry._links
- delete entry.content.protected
-
- entry.listDate = entry.date.split('T')[0]
- entry.title = entry.title.rendered
- entry.css = entry.post_meta.css
- entry.noindex = entry.post_meta.noindex
- entry.ver = 1
- entry.color = this.randomColor()
-
- delete entry.post_meta
-
- return entry
- })
- // 新記事の取得
- let newList = await fs.readdir('./entry', {withFileTypes: true})
- newList = newList.filter(dirent => {
- return dirent.isFile()
- })
- newList = newList.map(file => {
- let entry = require(`./entry/${file.name}`)
- entry.slug = file.name.split('.')[0]
- entry.content.rendered = pug.render(entry.content.rendered)
- entry.color = this.randomColor()
- entry.ver = 2
- entry.listDate = entry.date.split('T')[0]
- return entry
- })
- let list = [...oldList, ...newList]
- list.sort((a, b) => {
- return (a.date < b.date ? 1 : -1)
- })
-
- list = list.filter((entry) => {
- return entry.status === 'publish'
- })
-
- return list
- }
- // stylus
- async stylusCompile(file: string): Promise<string> {
- const data: string = await fs.readFile(`stylus/${file}`, 'utf-8')
- return stylus(data).set('filename', `stylus/${file}`).render()
- }
- // ランダムカラー
- randomColor(): string {
- return `hsl(${Math.floor(Math.random() * (360 + 1 - 0)) + 0}, 85%, 65%)`
- }
- // html minfy
- htmlMinify(html: string): string {
- return minify(html, {
- removeAttributeQuotes: true,
- collapseWhitespace: true,
- removeEmptyAttributes: true,
- removeRedundantAttributes: true,
- })
- }
- // 一覧生成
- async list(): Promise<void> {
- const compiledFunction = pug.compileFile(`theme/list.pug`)
- const css: string = (new CleanCSS().minify(await this.stylusCompile('list.styl'))).styles
- const tagList = JSON.parse(await fs.readFile('tag.json', 'utf8'))
- let oldList = this.entryList
- let html: string = compiledFunction({
- metaTitle: 'q-Az',
- description: 'JavaScript、PHP、CSS、HTML、WordPress などウェブ制作の話を中心に解説したり実験したりしています。',
- css,
- oldList,
- tagList
- })
- html = this.htmlMinify(html)
-
- fs.writeFile(`dist/index.html`, html)
- // tag用の一覧
- await fs.mkdir('dist/tag')
- tagList.forEach(async (tag) => {
- const oldTagList = oldList.filter((entry) => {
- return entry.tags.includes(tag.id)
- })
- let tagHtml: string = compiledFunction({
- metaTitle: `${tag.name} | q-Az`,
- description: tag.description,
- css,
- oldList: oldTagList,
- tagList,
- tagActive: tag.slug
- })
- tagHtml = this.htmlMinify(tagHtml)
- await fs.mkdir(`dist/tag/${tag.slug}`)
- fs.writeFile(`dist/tag/${tag.slug}/index.html`, tagHtml)
- })
- }
- // 記事生成
- async entry(): Promise<void> {
- const tagList = JSON.parse(await fs.readFile('tag.json', 'utf8'))
- const oldList = this.entryList
-
- const compiledFunction = pug.compileFile(`theme/entry.pug`)
- const entryCss: string = await this.stylusCompile('entry.styl')
- oldList.forEach(async entry => {
- const css = (new CleanCSS().minify(entryCss + entry.css + `.mainHead{background-color:${entry.color}}`)).styles
- let description = entry.content.rendered.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'').replace(/s+/g, '')
- description = description.slice(0, 150)
-
- let html: string = compiledFunction({
- metaTitle: `${entry.title} | q-Az`,
- description,
- css,
- entry,
- tagList
- })
- // 何記事かminify時エラーが出るので無視
- try {
- html = this.htmlMinify(html)
- } catch {}
- await fs.mkdir(`dist/${entry.slug}`)
- fs.writeFile(`dist/${entry.slug}/index.html`, html)
- })
- }
- // staticフォルダのコピー
- async static(): Promise<void> {
- await fse.copy('./static', './dist')
- }
-
- }
- new Blog(
とりあえず動けばいい精神で書いていたのでエラー処理等全く入れてませんし、似た処理なのに同じこと何度も書いたりしてますが、200行ちょいでブログができるコードが出来たので個人的には満足です。改良したいところ出来るところ多々あるんですが、まずは公開しないとモチベーションも下がってくるのでとりあえず。
フォルダ構成も書こうかと思いましたが、自分のブログでしか使えないコードなので、仮に(仮に)一般に配れる形までも持っていけたらその時GitHubにでも公開しよう(需要)かと思います。
方針とか
今回作成した方針としては
- WordPressの過去記事をそのまま移行する
- 記事はPugで書く
- Stylusを使う
- IEを完全に無視
という感じでやってみました。
WordPressの過去記事は、WordPressのREST APIをたたいて記事一覧を全部持って来る(json形式)、新記事はjsonだと融通が利かない部分や制限が多いので、1記事1jsファイルで管理
という普通しない方法で管理しています。この記事は「refresh.js」。jsファイルで記事管理をする謎な感じですが、ただのobjectでしかないので、見た目はほぼほぼjsonです。
テンプレート文字列が使えたりするのがjsだとjsonより優れているので、改行などを使って書きたい記事ではjsのobjectが圧倒的に使いやすいです。
過去記事を書き直したいときは、新記事でslugさえ合わせればそのまま上書きしてくれるので、古い記事が多すぎるので直したいところ。
IEに関してはGridのせいで全然違う見た目になっていますが、これはこれであり(?)な見た目になっています。
記事一覧にページネーションがなかったり(120記事全部出し)、なんか大事なもの含め色々欠けていますがこれから増やしていければよいかなと思ってます。
WordPressじゃないと出来ないもの
WordPressというよりはPHPなどによる動的処理が必要なものですが
- 記事のコメント欄
- お問い合わせ
- 記事検索
は静的サイトでは基本出来ません。無理やりというか違うアプローチで出来るところもありますが、基本は無理なのでこの辺はどうしようか未来の課題です。
とりあえず公開の形まで持ってこれたので今回はこれでOKで。