Home Manual Reference Source Test Repository

lib/parse/front-matter.js

/**
 * Interface to gray-matter for parsing files with YAML frontmatter.
 */
import Promise from 'bluebird';
import fs from 'fs';
import matter from 'gray-matter';
import jsYaml from 'js-yaml';

/**
 * The frontmatter delimiter character.
 * @type {string}
 */
const FRONTMATTER_DELIMITER = '-';

const frontMatterOptions = {
  parser: jsYaml.safeLoad,
};

/**
 * Parse a file with front matter.
 * @param {string} str String to parse.
 * @param {Object} options Additional options.
 * @return {JSON} JSON object.
 */
export function parse(str = '', options = {}) {
  return matter(str, Object.assign({}, frontMatterOptions, options));
}

/**
 * Stringify a document.
 * @param  {string} str Content to append to YAML.
 * @param  {Object} data Data to convert to YAML and prepend to document.
 * @return {string} Content with prepended YAML data.
 */
export function stringify(str = '', data = {}) {
  return matter.stringify(str, data);
}

/**
 * This is a fast way to check if a file has frontmatter without reading all of
 * its contents into memory.
 * @param {string} filePath Path to file on the file system.
 * @return {Promise.<boolean>} Promise which resolves to true if the file has
 *   frontmatter.
 */
export function fileHasFrontmatter(filePath) {
  return new Promise((resolve, reject) => {
    let chunks = '';
    fs
      .createReadStream(filePath, {
        encoding: 'utf8',
        start: 0,
        // Only read the first 3 characters from the file, as those are the ones
        // which must match frontmatter delimiter.
        end: 2,
      })
      .on('data', chunk => {
        chunks += chunk;
      })
      .on('close', () => {
        for (const char of chunks) {
          // If one character doesn't match then this file doesn't have
          // frontmatter.
          if (char !== FRONTMATTER_DELIMITER) {
            resolve(false);
            return;
          }
        }

        // If every character matches then this file has frontmatter.
        resolve(true);
      })
      .on('error', reject);
  });
}