Skip to content
This repository has been archived by the owner on May 27, 2022. It is now read-only.

Latest commit

 

History

History
225 lines (166 loc) · 15.5 KB

about-the-blog-2.md

File metadata and controls

225 lines (166 loc) · 15.5 KB

+++ categories = ["Без категории"] date = "2018-04-29" excerpt = "Описание всей работы обёртки над Hugo, а так же все команды для работы. Где хоститься блог и как получить такой же." image = "about-the-blog-2/cover.jpg" tags = ["hugo","JavaScript", "node.js", "static site", "github pages"] thumbnail = "about-the-blog-2/thumbnail.jpg" title = "Как работает этот блог - Часть 2" +++

В прошлой части, я закончил на том, что получил набор HTML файлов. Эти файлы просто лежат у меня на диске и никто их не видит. Теперь надо решить задачу с показом нашёго сайта миру.

И у нас нет с этим проблем, так как это просто набор файлов. Есть множество хостингов, которые предоставляют эту функцию, причем дешевле, чем хостинг с чем-то вроде PHP. Но есть вариант ещё и без использования хостингов. Это же просто набор файлов! Как удачно, что некоторые сервисы предоставляют показывать статику бесплатно. Один из таких - это GitHub Pages. Есть и другие, но надо поискать 😃. Такую функцию предоставлял ещё Dropbox. Ты просто закибывал файлы в папку, у себя на компе и после они сразу появлялось на твоем сайте.

Но я использую GitHub. Из-за того, что я и так зарегистрирован на нём и он относительно надёжен. Что надо сделать для получения своего заветного сайта на их платформе?

  1. Зарегистрироваться
  2. Создать репозиторий с названием USERNAME.github.io (Вместо USERNAME - свой ник)
  3. Все!

После этого, сайт будет доступен по этому адресу, у меня это grishy.github.io. Так же можно купить свой домен второго уровня. А-ля grishy.ru. Насчет его подключения инструкцию можно найти в настройках репозитория USERNAME.github.io.

Так же надо после добавить файл CNAME с именем внутри, в корень репозитория. Это если вы подключили свой домен.

Так вот, насчёт постобработки. Мне не хватило встроенных функций и из-за этого я добавил ещё один уровень, задачи которого:

  1. Запуск локальной копии, для просмотра того, что написал. Он не до конца функционален, из-за того что показывается без нижних пунктов.
  2. Генерация статики, со всем постобработками.
  3. Автозагрузка на github изменений.

Это все оформлено в один index.js файл. Для вызова мне в корне надо написать код ниже. На самом деле script это папка и в ней храниться index.js файл.

Вот список всех команд:

> blog
$ node script help
$ node script
$ node script gen
$ node script deploy

Сделано так, из-за того что я хотел выделить все, что связано с генерацией в одну часть. В этой папке у меня хранятся бинарники Hugo, модули для Node.js и т.д. Теперь более подробно про все команды.

Запуск локальной копии ($ node script)

Это самое простое. Нужно вызвать Hugo из провального бинарника. Для Windows - это hugo.exe, для Linux - hugo, Mac я не рассматривал. После запуска у меня выводится ссылка в консоль, где собственно уже можно все посмотреть в браузере.

Решить проблему с выводом статьи после всех обработок, а не до при такой архитектуре можно сделав саму свой локальный сервер. Можно заранее сгенерировать все, провести обработки и после запустить. Сейчас, если это надо, то можно использовать Python. Запустив маленький локальный сервер на 8000 порту. Но это как-то мне не мешает пока. Тем более, что есть вариант номер два, о котором ниже.

python -m http.server 8000

Генерация статики ($ node script gen)

Мне нужно было сделать подсветку кода, вставку математических формул, нормально отображение дат на русском языке. Все, большего не надо. Примерный план действий:

  1. Получить файл
  2. Распарсить
  3. Найти элементы, которые надо изменить
  4. Изменить их!
  5. Записать назад изменения в файл

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

Получение всех файлов

Для начала возьмет файлы, которые надо обработать. В нашём случает это все .html файлы. Воспользуемся модулем glob, для взятия файлов по маске.

glob(CONFIG['public'] + '**/**/*.html', function (err, files) {
    // ...
})

Конструкция вида **/**/*.html говорит, что надо взять файлы с любым именем и любым уровнем вложенности. Дальше нам в функцию будет передана ошибка и список подходящих файлов.

Для каждого файла мы запустим обработку. Тут видно, что файл я начал писать этот скрипт, пока ещё новый стандарт не поддерживался в node.js. Из-за того, что я тут использую "сокращенный синтаксис" записи функций. Хотя там есть ещё отличия, от предыдущего способа, но чаще всего они не важны.

files.forEach((item) => {
    // ...
})

Обработка файла

Начинается все с того, что нам надо загрузить файл и распарсить его. Не работать же нас с сырым текстом и регулярками. Хотя идея с регулярками не так и плоха. Мы читаем файл, и записываем результат парсинга в переменную $.

var html = fs.readFileSync(item, 'utf8')

var $ = cheerio.load(html, {
    decodeEntities: false
})

Так как изменения файла будут только внутри маленьких кусочков, то их можно запустить параллельно. Так мы увеличим скорость обработки одной записи. Воспользуется пакетом async.

async.parallel([
    (callback) => {
        // 1
    },
    (callback) => {
        // 2
    },
    // ...
], function () {
    // 3
});

Он запустит параллельно функции 1 и 2. После того, как они все закончили работу, вызовется функция номер 3. Кол-во функций типа 1 и 2 может быть не ограничено, но у меня их 3. После того, как функция типа 1 и т.д. закончили свою заботу, они должны вызвать callback. Так async поймет, что эта функция закончила. Кажется не самым удобным и почему он сам не поймёт? Тут немного надо углубиться в JavaScript, делать я это конечно же не буду. Гуглите Event Loop. Просто в JS все работает асинхронно, не так как в Python, C++ и других.

Время

Как выглядит у меня до обработки:

Надо найти элемент, в котором записано время и преобразовать. Стандартно у меня время записано в формате Unix. Определяется как количество секунд, прошедших с полуночи 1 января 1970 года. Как я и сказал, вызываем callback().

$('.Unix-time').each((i, el) => {
    var block = $(el)
    var timeF = moment.unix(block.text())
    block.text(timeF.format("DD MMMM YYYY").toUpperCase())
})
callback();

Подсветка кода

Для подсветки кода, используется highlight.js. Мы находим элементы с кодом, отдаем в hl.js и что он вернул, вставляем на тоже место.

$('pre code').each((i, el) => {
    var block = $(el)
    var cls = block.attr('class')
    var code = block.text()

    if (cls == undefined) {
        block.html(hljs.highlightAuto(code).value)
    } else if (cls == 'language-nohighlight') {
        // 'Есть сказали ничего не делать, не делаем :) '
    } else {
        // [0] - вся строка (по умолчанию в регулярках)
        // [1] - язык
        var lang = /(?:language-)(.*)/g.exec(cls)[1]
        if (lang != null) {
            var hlHTML = hljs.highlight(lang, code).value
            block.attr('class', lang + ' hljs')
            // <<span class="hljs-name">path</span>
            // Одна из скобок удаляется. 
            let escHTML = us_s.escapeHTML(hlHTML)
            block.html(escHTML)
        }
    }
})

Думаю объяснять почти не надо. Если я не написал стандартно, какой язык использовать, то надо хоть как-то подсветить. Если сказал, что ничего делать не надо, пропускаем. Иначе взять из класса элемента атрибут class и вытащить из него сам язык. После преобразовать и вставить на прошлое место. Тут ещё код преобразуется в эскейп последовательности, чтобы браузер не убрал из кода символы, которые ему не нравятся. Они могут совпадать с заранее заданными.

escapeHTML("<div>Blah blah blah</div>");
// => "&lt;div&gt;Blah blah blah&lt;/div&gt;"

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

Математические формулы - MathJax

Найти все формулы в тексте, и заменить их на сгенерированные SVG картинки. Знаете, я сейчас не посмотрел на код, и мне не понятно, почему я в таком стиле написал. Зачем я использовал async.every?

async.every($('span.mathjax'), function (el, cb) {
    let block = $(el)
    let math = block.html()

    mjAPI.typeset({
        math: math,
        format: "TeX",
        svg: true
    }, function (data) {
        block.html(data.svg)
        cb(null, true)
    })
}, () => {
    callback()
});

Теперь, если я напишу в тексте:

{ {< tex >} }
    \sigma = \sqrt{ \frac{1}{N} \sum_{i=1}^N (x_i -\mu)^2}
{ {< /tex >} }

То вы увидите: {{< tex >}} \sigma = \sqrt{ \frac{1}{N} \sum_{i=1}^N (x_i -\mu)^2} {{< /tex >}}

В этом примере, как раз видно, когда вызов callback помогает. Я не знаю когда закончится обработка, но когда она закончиться, async сразу об этом узнает (последние строки).

Вот и все, после этого я сохраняю результат в HTML, перезаписывая прошлый файл.

Загрузка на GitHub ($ node script deploy)

После того, как все сгенерировалось, оно лежит в папке под названием public, вместе с блогом. Теперь надо закоммитить эту папку. Я копирую blog/public в grishy.github.io. У меня разделены данные доя блога и сам блог. Но сохраняются они одновременно. Т.е. когда я пишу node script deploy, то все изменения в обеих папках запоминаются и отправляются. Перед этим я ещё генерирую название для коммита, которое начинается со смайлика и дальше дата.

Для загрузки я использую готовый node.js модуль - simple-git. После этого, в течении 1 минуты все изменения будут видны в блоге.

Как-то много вышло, писал сразу, не особо думаю, что будет дальше 😄. Наверное будет 3 часть, где будет рассмотрена производительность, что улучшить, какие баги и подводные камни.