Я уверен, что любой инженер, которому приходилось работать над CMS (системой управления контентом), создавая или интегрируя, скажет вам, что это абсолютный кошмар. Многие CMS пытаются делать слишком много вещей и хотят воплотить все надежды и мечты в устрашающем API.

Я всегда думал, что есть особое место, которое не выделялось в CMS-land; передача от разработчика, создающего сайт, к тому, кто просто обновляет контент на регулярной основе. Вы не хотите, чтобы разработчик обновлял содержание сайта, потому что это дорого; время и деньги, которые должны быть легко заменены. Это причина, по которой кураторы контента просят CMS.

Я наткнулся на Вапид в прошлом году и подумал, что он на правильном пути к решению этой проблемы. Идея - это то, что я имел в виду некоторое время; напишите любые теги шаблонов на своих HTML-страницах и попросите CMS заполнить поля на основе найденных тегов. Я мог использовать его какое-то время, а затем для любой машины, на которую я пытался его установить, это просто ошибка. Было опубликовано множество обходных путей, но входной барьер для людей (особенно тех, кто только знаком с HTML / CSS) слишком высок, когда терминал не предоставляет ничего полезного.

Так что с меня хватит, пришло время просто построить свой собственный.

Требования

Мне очень понравилась презентация Vapid, в которой просто создаются любые шаблоны тегов, которые вы хотите, а CMS просто находит их и создает поле для ввода внутри. Я хотел позаимствовать эту идею, но ни одна из популярных библиотек, использующих эту систему тегов (усы, ручки, нунджаки и т. Д.), Похоже, не имела API, предоставляющего список найденных тегов. Я понял, что мне нужно сделать собственный компилятор.

Я также хотел, чтобы это было очень просто для людей, которые знали, как создать простую веб-страницу или сайт, используя только HTML / CSS. Никаких причудливых скриптов или фреймворков, просто поместите свои файлы в каталог и работайте как старые добрые времена FTP.

Сборка компилятора

Общая суть такова:

  • Просмотрите каждый файл HTML
  • Найдите теги шаблона
  • При обнаружении выполните поиск, чтобы узнать, существует ли тег (заменить) или нет (сохранить)
  • Напишите файл, представляющий хранилище всех найденных тегов

Просматривать каждый HTML-файл довольно просто. Я использую пакет glob npm, чтобы найти все файлы, заканчивающиеся на .html, в целевом каталоге, а затем прочитать каждый из них. Я также попытаюсь инициализировать место для хранения тегов здесь.

import fs from 'fs-extra';
import glob from 'glob';
function writeFile(filePath, fileContents) {
  fs.ensureFileSync(filePath);
  fs.writeFileSync(filePath, fileContents, 'utf-8');
}
function getHtmlFilePaths(directory) {
  return glob.sync(`${directory}/**/*.html`);
}
function getFileContents(path) {
  return fs.readFileSync(path, 'utf-8');
}
let database = {};
try {
  // Get the existing data
  database = require(DATABASE_JSON_PATH);
} catch (err) {
  // First time running; build the database later
  console.info(`Awaiting at ${DATABASE_JSON_PATH}`);
}
const htmlFilePaths = getHtmlFilePaths(HTML_WEB_DIR);
htmlFilePaths.forEach((htmlPath) => {
  const templatedHtml = getFileContents(htmlPath);
  // More to come...
})

Далее идет замена с использованием регулярного выражения. Регулярное выражение для этих тегов довольно легко найти, но я просто включу его здесь, чтобы вы не прекращали читать этот пост. На этапе замены мы также хотим записать найденный тег.

// ...inside the forEach method
const TEMPLATE_TAG_REGEX = /{{\s*([\w\.]+)\s*}}/g;
const replacedHtml = templatedHtml.replace(TEMPLATE_TAG_REGEX, (match, tagName) => {
  // If the tag isn't in the store, init it
  if (!database[tagName]) {
    database[tagName] = { value: match }
  }
  return database[tagName].value;
});

Я инициализирую значение как соответствующий тег (например, {{ pageTitle }}), а затем использую его в качестве предоставленной замены для нового файла .html. Если в магазине было какое-то значение, он бы его вернул.

Последняя часть - написать новый файл .html, и это тоже довольно просто. Кстати, я использую fs-extra вместо встроенного пакета fs, поскольку в нем есть несколько лучших инструментов.

// ...final step inside the map
const newFilePath = htmlPath.replace(HTML_WEB_DIR, '');
writeFile(HTML_DIST_DIR + newFilePath, replacedHtml);

Я сохраняю структуру каталогов, выполняя строковую разницу между каталогом, в котором я нашел файлы, и каждым .html путем к файлу. На этом этапе последний фрагмент кода компилятора пишет наш магазин.

writeFile(DATABASE_JSON_PATH, JSON.stringify(database));

Есть еще пара вещей, которые вы должны / могли бы сделать здесь. В своей версии я также копирую все остальные файлы, отличные от .html, в каталог HTML_DIST_DIR. Я также веду запись о том, на каких страницах был обнаружен каждый тег, так как я думаю, что это поможет отобразить, какие страницы используют тег в CMS. В моей версии есть еще кое-что, но мы вернемся к этому позже.

О базе данных

На этом этапе вы можете видеть, что хранилище - это просто .json файл; нет учетных данных, нет процесса для запроса в фоновом режиме. Я не люблю работать с базами данных, потому что для их настройки требуется много накладных расходов. Я решил, что для меня самый простой способ заставить эту работу работать - это просто выполнить поиск ключа в объекте. Проект предназначен для клонирования и развертывания с одного компьютера (человека, управляющего контентом), но я также думаю, что его можно легко изменить для фактического подключения к базе данных. Может быть, в будущем я сделаю это как класс, который может куда-то подключаться, но сейчас сделать это локально намного проще. Особенно для небольших проектов, таких как сайты-портфолио.

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

Создание CMS

Я знал, что это будет простое приложение, которое будет запускаться локально на машине куратора контента. Я не собираюсь описывать здесь всю настройку приложения, но вы можете сделать его любым удобным для вас способом. Существуют ссылки для навигации, которые перенаправляют на страницу, где вы можете редактировать контент, который представляет тег. После редактирования приложение сделает вызов, чтобы обновить магазин с этим значением. Еще сделал кнопку для компиляции сайта из CMS. Я также добавил возможность фильтровать список тегов, чтобы упростить поиск тегов, если их много.

Типы значений

Одна из вещей, которые я рассматривал при создании магазина, - это ключ, определяющий, какой тип значения хранится. Я основывал это на Типах контента Vapid, где вы можете дополнительно определить, какой тип контента ожидается для тега. Я планировал создать текст для простых строк, а затем также хотел иметь полноценный редактор разметки для более сложного форматирования. Затем, подумав об этом, я сказал, почему бы просто не сделать все как редактор разметки? Таким образом, мне не нужно было бы заниматься проверкой или переводом в компиляторе; Я просто напишу все, что там было.

Обработка уценки

Он состоит из двух частей.

  • Клиентский редактор уценки
  • Локальный процесс в компиляторе для перевода разметки в HTML.

Редактор уценки на стороне клиента был простым. Я нашел ресурс CDN (Сеть доставки контента) для SimpleMDE, простого редактора уценки. Хотя я мог установить и связать его, чтобы его можно было использовать в автономном режиме, я просто хотел приступить к работе с редактором, и размещение сценария в <head> странице - самый простой способ.

Более сложной частью было решение для компилятора. Во-первых, если у разметки была только одна строка текста, я не хотел заключать этот текст в теги <p>. Кажется, это то, что делают многие переводчики уценки, но я обнаружил, что markdown-it имеет отдельный метод для обработки встроенного текста, называемый renderInline(). Это означало, что я мог проверять наличие символов новой строки в строке, и если их не было, мы вернем только встроенную версию уценки. Вот изменение внутри цикла forEach по сравнению с предыдущим.

const { value } = database[tagName];
if (/\n/.test(value)) {
  return md.render(value);
}
return md.renderInline(value);

md здесь представляет новый экземпляр markdown-it, который взят прямо из документации. Но было еще кое-что, что я хотел включить. В markdown вы также должны уметь писать HTML. По умолчанию это отключено. Чтобы включить его, вам нужно передать его как параметр при создании экземпляра.

import MarkdownIt from 'markdown-it';
const md = new MarkdownIt({ html: true });

Теперь, когда вы пишете HTML в редакторе разметки внутри CMS, он будет отображаться на этой странице как HTML!

Представляем handsoff

Я назвал проект ручным управлением, чтобы связать с передачей разработчика и что это было довольно невмешательством для человека, занимающегося HTML / CSS, для создания сайта, а также для куратора контента для управления. Код для этого находится здесь. Я приглашаю вас клонировать и использовать его для любого сайта, который вы захотите создать. 😎