dynamic-content
Version:
dynamic content functionality for roots
161 lines (136 loc) • 4.77 kB
text/coffeescript
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))