Как стать автором
Обновить

Начинаем использовать Handlebars для создания статических сайтов

Предупреждение: Матёрый фронтендщик скорее всего не увидит для себя здесь ничего нового. Код в статье представляется без каких-либо гарантий и не претендует на идеологическую верность.


На днях мой друг верстальщик, работая над очередным статическим сайтом, рассказал, что всё собирается начать пользоваться шаблонизатором. Посматривал он в сторону Handlebars, но нигде не видел простой, готовой тулзы. Которая позволяла бы собирать шаблоны в статический html, могла бы работать через FileWatcher Webstorm'а и не требовала бы развёртывания окружения с сотнями пакетов, гульпом и прочими вебпаками.


Другу надо помогать, поэтому я написал скрипт, который бы отвечал его запросам. Заодно решил выложить результат на Хабр с мини-инструкцией для тех, кто хотел бы начать пользоваться шаблонизатором для вёрстки своих одностраничников, но не знает что почём.


Все инструкции по пользованию шаблонизатором можно найти на официальном сайте Handlebars, а также можно прочесть старый обзор на Хабре, поэтому приступим сразу к сути.


  1. Первым делом устанавливаем NodeJS, если ещё не установлена. Я использовал самую свежую версию (v14) и тестировал скрипт только на ней, впрочем и на v12 должно завестись.
  2. Далее устанавливаем глобально пакет handlebars npm install -g handlebars
  3. Создаём папку проекта. В ней создаём (index или_другое_название).hbs, который будет служить корневым шаблоном и будет преобразован в html.
  4. Копируем в папку проекта наш скрипт и правим раздел config:
    • entryPoints — корневые шаблоны, указываем здесь наш 'index.hbs'
    • partials — по ходу работы мы можем создать шаблоны, которые сами не компилируются в html, но могут быть использованы внутри корневых шаблонов, например какие-нибудь повторно используемые блоки
    • data — здесь указываются значения, которые Hundlebars будет подставлять в шаблоны (смотри документацию)
    • helpers — для подключения своих хелперов
    • setup — для тонкого конфигурирования handlebars, чтобы не лезть в глубь скрипта
  5. Для преобразования шаблонов в статичный html открываем терминал в папке проекта и выполняем node handlebars.compile.js, ну или настраиваем вотчер в вашей_версии_Idea.

Пример в картинках

Корневой шаблон



Partial-шаблон



Раздел конфигурации в handlebars.compile.js



Пример настройки файлвотчера (увы, для каждого проекта отдельно)



Что получилось



И как это выглядит



Ниже под спойлером код самого скрипта. Не стал заливать его на гит: по-хорошему можно его довести до ума и сделать npm-пакет, но пускай этим занимается кто-нибудь другой, более сведущий в javascript-разработке


handlebars.compile.js
const config = {
  entryPoints: [
  ],
  partials: [
  ],
  data: {
  },
  helpers: [
  ],
  setup: (handlebars) =>
  {
  }
};

(async function doWork(configuration) {
  const {promises: _fs} = require("fs");
  const _path = require( 'path');

  const getBaseFileName = (filePath) => _path.basename(filePath, _path.extname(filePath));

  async function requireGlobal(id)
  {
      const getNpmRoot = new Promise((resolve, reject) => {
          require("child_process").exec("npm root -g", (err, stdout, stderr) => {
              if(err) { reject(err.message); }
              else if(stderr) { reject(stderr); }
              else { resolve(stdout.trim()); }
          });
      });

      try
      {
        const npmRoot = await getNpmRoot;
        const packagePath = _path.resolve(`${npmRoot}/${id}`);
        return require(packagePath);
      }
      catch(e)
      {
        throw `Can not find global installed ${id}. Exception: ${e.message}`;
      }

  }

  function addHelper(helper, hbs)
  {
    if (helper && typeof helper.register === 'function')
    {
      helper.register(hbs);
    }
    else
    {
      console.error(`WARNING: helper have not a 'register' function, cannot add`);
    }
  }

  function resolveFile(filePath)
  {
    const absolutePath = _path.resolve(__dirname, filePath);
    console.info(`Load file from ${absolutePath}`);
    return _fs.readFile(absolutePath, "utf8");
  }

  async function addPartial(input, hbs)
  {
    const partial = await resolveFile(input);
    hbs.registerPartial(getBaseFileName(input), partial);
  }

  async function renderTemplate(input, context, hbs)
  {
    const template = await resolveFile(input);
    const hbsRender = hbs.compile(template);
    const htmlContents = hbsRender(context);
    const entryPointDir = _path.dirname(input);
    const output = _path.resolve(entryPointDir, `${getBaseFileName(input)}.html`);
    await _fs.writeFile(output, htmlContents, 'utf8');
    console.info(`Wrote ${output}`);
  }

  async function compile({
    entryPoints = ['index.hbs'],
    partials = [],
    data = {},
    helpers = [],
    setup = (handlebars) => {}
  })
  {

    const Handlebars = await requireGlobal('handlebars');

    setup(Handlebars);

    helpers.forEach(helper => addHelper(helper, Handlebars));

    await Promise.all(partials.map( fileName => addPartial(fileName, Handlebars)));
    await Promise.all(entryPoints.map( fileName => renderTemplate(fileName, data, Handlebars)));
  }

  try
  {
    await compile(configuration);
    console.info("Done!")
  }
  catch (e)
  {
    console.error(e);
  }
})(config);
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.