Snippets

Joran Rood Gulp.js TOML front matter plugin

Created by Joran Rood
const plugin_error = require('plugin-error'),
      through = require('through2'),
      toml = require('@iarna/toml');

/* Opening and closing delimiters ("+++") must occur on a line
   by themselves (although delimiters can have trailing whitespace).
   Opening delimiters must occur on the first non-empty line of a file
   (lines consisting only of whitespace are considered empty). */
const open_pattern = /^(?:[\x09\x20]*(?:\x0a|\x0d\x0a))*\x2b{3}[\x09\x20]*(\x0a|\x0d\x0a)/,
      close_pattern = /(\x0a|\x0d\x0a)\x2b{3}[\x09\x20]*(?:\x0a|\x0d\x0a|$)/;

function gulp_toml_front_matter() {
    function extract(file) {
        // Find the opening delimiter
        var contents = file.contents.toString(),
            match = open_pattern.exec(contents),
            overlap,
            data;

        if ((match === null) || (match.index !== 0)) {
            // Pass through files without front matter unmodified
            return;
        }

        /* Strip the opening delimiter from the contents
           and find the closing delimiter (the final line break
           of the opening delimiter is kept so that
           the closing delimiter can be matched on the line
           directly following the opening delimiter) */
        overlap = match[1].length;
        contents = contents.slice((match[0].length - overlap));
        match = close_pattern.exec(contents);

        if (match === null) {
            /* Parse files without a closing delimiter
               as front matter and truncate their contents */
            data = toml.parse(contents.slice(overlap));
            contents = '';

        } else if (match.index === 0) {
            /* Skip parsing front matter if the closing delimiter
               directly follows the opening delimiter
               and strip the closing delimiter from the contents */
            data = {};
            contents = contents.slice(match[0].length);

        } else {
            /* Parse the front matter and strip the front matter
               from the contents along with the closing delimiter */
            data = toml.parse(contents.slice(overlap,
                (match.index + match[1].length)));
            contents = contents.slice((match.index + match[0].length));
        }

        file.contents = Buffer.from(contents);
        file.data = Object.assign({}, file.data, data);
    }

    function transform(file, encoding, next) {
        var error;

        if (file.isNull()) {
            // Return without further processing
            return next(null, file);

        } else if (file.isStream()) {
            /* Indicate that streams are _not_ supported.
               NOTE: "gulp-buffer" could be used to read streams into memory */
            return next(new plugin_error(
                'gulp-toml-matter',
                'Streams are not supported'
            ));

        } else if (file.isBuffer()) {
            try {
                extract(file);
                return next(null, file);

            } catch (error) {
                return next(new plugin_error(
                    'gulp-toml-matter',
                    error,
                    {
                        fileName: file.path,
                        showStack: true
                    }
                ));
            }
        }

        return next();
    }

    return through.obj(transform);
}

module.exports = gulp_toml_front_matter;

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.