UNPKG

dynamic-content

Version:
161 lines (136 loc) 4.77 kB
path = require 'path' fs = require 'fs' os = require 'os' _ = require 'lodash' yaml = require 'js-yaml' W = require 'when' helpers = require './helpers' module.exports = (opts = {}) -> class DynamicContent constructor: -> @category = 'dynamic' @all_content = [] fs: -> extract: true ordered: true detect: (f) -> helpers.detect_file(f.path) compile_hooks: -> before_pass: before_hook.bind(@) after_file: after_hook.bind(@) write: write_hook.bind(@) category_hooks: -> after: after_category.bind(@) ###* * For dynamic files before the last compile pass: * - remove the front matter, parse into an object * - add the object to the locals, nesting as deep as the folder it's in * - add an "all" utility function to each level * * @private * * @param {Object} ctx - roots context ### before_hook = (ctx) -> # if last pass if ctx.index is ctx.file.adapters.length f = ctx.file roots = f.roots data = helpers.read(ctx.content) front_matter = _.omit(data, 'content') ctx.content = data.content # get categories and per-compile locals, add or define site key folders = path.dirname(f.file.relative).split(path.sep) locals = f.compile_options.site ?= {} file_locals = f.file_options # add special keys for url and categories front_matter._categories = folders front_matter._url = roots.config.out(f.file, ctx.adapter.output) .replace(roots.config.output_path(), '') # deep nested dynamic content # - make sure the backtraced path to a deep-nested folder exists # - push the front matter to the folder name array/object # - add special 'all' function to the array/object # - save pointer to the front matter obj under file-specific post local for f, i in folders locals[f] ?= [] locals = locals[f] if i is folders.length - 1 locals.push(front_matter) @all_content.push(front_matter) locals.all = all_fn file_locals.post = locals[locals.length - 1] ###* * After a file in the category has been compiled, grabs the content and * adds it to the locals object unless _content key is false * * @private * * @param {Object} ctx - roots context * @return {Boolean} ### after_hook = (ctx) -> locals = ctx.file_options.post locals.content = ctx.content unless locals._content is false ###* * After the category has finished, if the user has chosen to have all * dynamic content written as json, make that happen. This can only happen * if a `write` option has been provided with an `output` key. Also can * optionally contain `flattened` and `keys` keys, which reformat or filter * the data in a certain way. * * @param {Object} ctx - roots context ### after_category = (ctx) -> opt = opts.write if opt content = reformat(@all_content) if typeof opt is 'string' then return write_json(ctx, opt, content) write_json(ctx, k, filter(content, v)) for k, v of opt ###* * If a dynamic file has `_render` set to false in the locals, don't write * the file. Otherwise write as usual. * * @param {Object} ctx - roots context * @return {Boolean} whether or not to write the file as usual ### write_hook = (ctx) -> ctx.file_options.post._render isnt false ###* * Returns an array of all the dynamic content object in the folder * it was called on, as well as every folder nested under it, flattened * into a single array. * * @private * * @return {Array} Array of dynamic content objects ### all_fn = -> values = [] recurse = (obj) -> for o in Object.keys(obj) if not isNaN(parseInt(o)) then values.push(obj[o]); continue recurse(obj[o]) recurse(this) values module.exports.Helpers = helpers # private reformat = (data) -> output = {} for item in data pointer = output for level in item._categories pointer[level] ?= {} pointer[level]['items'] ?= [] pointer = pointer[level] delete item._categories pointer.items.push(item) return output filter = (data, keys) -> keys = keys.split('/') pointer = data for key in keys pointer = pointer[key] return pointer write_json = (ctx, relative_path, content) -> destination = path.join(ctx.roots.config.output_path(), relative_path) fs.writeFileSync(destination, JSON.stringify(content))