Я завел свой первый блог в 2010 году. Платформа WordPress была оптимальным выбором в то время. Она была простой в использовании, а также предлагала тысячи тем и плагинов. Но у WordPress есть свои минусы. Движок требователен к ресурсам и подвержен множеству уязвимостей.

Одно из альтернативных решений  —  разместить сайт на CloudFront или любой другой CDN (сети доставки контента). CDN хорошо масштабируется и обеспечивает безопасность сайта. Но в моем случае блог был только архивом  —  я не хотел держать свой сервер в рабочем состоянии. Именно поэтому я выбрал GitHub Pages.

Привязать домен своего сайта к GitHub Pages может бесплатно любой разработчик. Правда, здесь размещаются только статические сайты, так что мне пришлось трансформировать свой блог на WordPress.

К счастью, WordPress может экспортировать (Tools/Export в админке) весь контент сайта в XML-файл. Мне же потребовалось только разработать простой генератор статических сайтов, который генерирует контент из экспорта. Для разработки я выбрал TypeScript  —  язык, который имеет много продвинутых и простых в использовании библиотек.

Для начала нужно было найти несложный в применении XML-парсер. Google помог быстро отыскать fast-xml-parser. Этот парсер создает из XML дерево JS-объектов, которое легко обработать.

Еще нужен был простой движок шаблонов. Для этой цели лучше всего подходит ejs. Освоить его легко (он позволяет встраивать JS в HTML-код) и быстро (в фоновом режиме движок компилирует шаблон в JS). Более простого и оптимального решения для небольших проектов нельзя и представить.

Когда у меня было все необходимое, я разработал небольшой генератор статических сайтов.

Чтобы создать ejs-шаблоны, загрузил HTML-файлы, сгенерированные WordPress, и добавил к ним теги ejs. Создал два шаблона  —  для постов и для оглавления.

Структура экспортируемого XML очень проста. Это RSS-лента, создаваемая из элементов. Каждый элемент имеет свой тип (пост, вложение и т.д.). Мне нужны были только посты и вложения. Код выглядит следующим образом:

(async function () {
const parser = new XMLParser();
let wp_export = parser.parse(readFileSync('wordpress-export.xml'));
let posts = wp_export.rss.channel.item;

let pinned_posts: any[] = []
let post_list: any[] = []
for (const post of posts) {

// загрузка вложений
if (post['wp:post_type'] == 'attachment') {
const url = post['wp:attachment_url'];
for (const post_meta of post['wp:postmeta']) {
if (post_meta['wp:meta_key'] == '_wp_attached_file') {
const file_path = post_meta['wp:meta_value']
const full_path = `wp-content/uploads/${file_path}`
mkdirSync(dirname(full_path), { recursive: true });
const file = createWriteStream(full_path);
http.get(url, (resp) => {
resp.pipe(file);
file.on("finish", () => {
file.close();
});
})
}
}
}

// генерация страницы поста (если он опубликован)
if (post['wp:post_type'] == 'post' && post['pubDate']) {
post['content:encoded'] = post['content:encoded'].split(/\r?\n|\r|\n/g).reduce((accumulator: string, currentValue: string) => accumulator + `<p>${currentValue}</p>`)
const content = await ejs.renderFile("template.ejs", { post: post }, { async: true })
mkdirSync(`archives/${post['wp:post_id']}`, { recursive: true });
writeFileSync(`archives/${post['wp:post_id']}/index.html`, content)
const element = {
id: post['wp:post_id'],
title: post.title,
summary: truncate(post['content:encoded'].replace(/<[^>]*>?/gm, ''), 300)
}
if (pinned_post_ids.includes(post['wp:post_id'])) {
pinned_posts.push(element)
} else {
post_list.push(element)
}
}
}

// генерация toc
pinned_posts.sort((a, b) => { return b.id - a.id })
let merged_posts = pinned_posts.concat(post_list.sort((a, b) => { return b.id - a.id }))

// readme.md
let readme = `
# my-wordpress-blog
Это резервная копия моего блога на WordPress. (http://lf.estontorise.hu/)

`
for (const post of merged_posts)
readme += `[${post.title}](https://thebojda.github.io/my-wordpress-blog/archives/${post.id})\n\n`
writeFileSync('README.md', readme)

// index.html
const content = await ejs.renderFile("template_toc.ejs", { posts: merged_posts }, { async: true })
writeFileSync(`index.html`, content)
})()

Код выполняет итерацию по элементам и проверяет их типы. Если тип “вложение”, то считывается значение метаданных _wp_attached_file, содержащее URL вложения, и загружается с помощью модуля HTTP.

Если тип элемента  —  “post” и он опубликован (pubDate не пуст), то генерируется страница. Контент страницы заключен в теге content:encoded в формате HTML с небольшим изменением. Каждая строка является отдельным абзацем, поэтому необходимо преобразовать разрывы строк в абзацы. Эту задачу выполняет следующая строка кода:

post['content:encoded'] = 
post['content:encoded']
.split(/\r?\n|\r|\n/g)
.reduce((accumulator: string, currentValue: string) =>
accumulator + `<p>${currentValue}</p>`)

Когда я начинал вести блог 10 лет назад, я ничего не знал о SEO. Поэтому ссылки на посты выглядят следующим образом: …/archives/123, где последнее число  —  это id поста. В лучшем случае URL-адрес поста будет более выразительным и содержать ключевые слова. Но при обоих вариантах вы столкнетесь с одинаковой проблемой: GitHub Pages не поддерживает HTML-страницы без расширения “.html”.

Если загрузить HTML-файл без расширения, браузер примет его, но не покажет. Поэтому необходимо преобразовать эти URL-адреса в каталоги, содержащие файл index.html. Например, /archives/123 должен быть преобразован в /archives/123/index.html. С появлением этой новой структуры все работает отлично.

Последний блок кода генерирует HTML ToC’а и файл readme.md. Второй пригодится, если кто-то найдет страницу на GitHub.

Завершив генерацию статических страниц, я загрузил сайт на GitHub и активировал GitHub Pages в настройках.

После того как я установил запись CNAME в админке DNS-провайдера, GitHub проверил ее. Установив флажок Enforce HTTPS, GitHub сгенерировал сертификат HTTPS, и вскоре сайт был готов. Здесь можно посмотреть результат. Блог на венгерском языке, поэтому вы вряд ли поймете его контент, но сможете убедиться в том, что все работает и URL-адреса совпадают с URL-адресами WordPress.

Как я уже писал, этот блог  —  только архив, который не планируется пополнять новыми постами. Но при желании я мог бы установить движок WordPress на своем локальном компьютере, писать новые посты и обновлять созданные страницы в репозитории. Следовательно, вы можете использовать WordPress для ведения блога, а GitHub  —  для хостинга страниц.

Таким недолгим оказался мой переход с WordPress на GitHub Pages. Надеюсь, это краткое руководство поможет и вам перенести свой блог на GitHub Pages.

Все необходимое  —  в GitHub-репозитории.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Laszlo Fazekas: How I Migrated My WordPress Site to GitHub Pages

Предыдущая статья5 библиотек JavaScript, которые повысят вашу эффективность
Следующая статьяПодключение Kafka в Spring Boot