UNPKG

spectaql

Version:

A powerful library for autogenerating static GraphQL API documentation

292 lines (246 loc) 8.6 kB
/* * grunt-compile-handlebars * https://github.com/patrickkettner/grunt-compile-handlebars * * Copyright (c) 2014 Patrick Kettner, contributors * Licensed under the MIT license. */ 'use strict'; module.exports = function(grunt) { var _merge = require('lodash/merge'); var _toArray = require('lodash/toArray'); var JSON5 = require('json5'); var path = require('path'); var handlebarsPath; var handlebars; var usedPartials = []; var usedHelpers = []; // Normalizes the input so that it is always an array for the forEach loop var getConfig = function(config) { if (!config) { return []; } var files = grunt.file.expand(config); if (files.length) { return files; } return [config]; }; // Gets the final representation of the input, whether it be object, or string var parseData = function(data, dontParse) { // grunt.file chokes on objects, so we check for it immiedietly if (typeof data === 'object') { return data; } // `data` isn't an object, so its probably a file try { // JSON5 allows us to parse single-quote JSON (which is tehcnically invalid, and thereby chokes JSON.parse) data = grunt.file.read(data); if (!dontParse) { data = JSON5.parse(data); } } catch (e) {} return data; }; // Checks if the input is a glob and if so, returns the unglobbed version of the filename var isGlob = function(filename) { if (!filename || typeof filename === 'object') { return; } var match = filename.match(/[^\*]*/); if (match[0] !== filename) { return match.pop(); } }; // Figures out the name of the file before any globs are used, so the globbed outputs can be generated var getBasename = function(filename, template, outputInInput) { var basename; var glob; template = Array.isArray(template) ? filename : template; glob = isGlob(template); if (outputInInput) { basename = _toArray(filename.split('.').pop()) ; } else if (glob) { basename = filename.slice(glob.length, filename.length).split('.'); basename.pop(); } else { basename = filename.split('/').pop().split('.'); basename.pop(); } return basename.join('.'); }; var mergeJson = function(source, globals) { var json = {} var fragment; globals.forEach(function(global) { if (grunt.util.kindOf(global) === 'object') { fragment = global; } else { if (!grunt.file.exists(global)) { return grunt.log.error('JSON file ' + global + ' not found.'); } else { try { fragment = grunt.file.readJSON(global); } catch (e) { return grunt.fail.warn(e); } } } _merge(json, fragment); }); if (typeof source === 'object') { _merge(json, source); } return json; }; var shouldRegisterFullPaths = function(itShould, type) { if (itShould && typeof itShould === 'string') { itShould = itShould.toLowerCase().indexOf(type) === 0; } return itShould; }; var getTemplateData = function(templateData, filepath, index) { var data; if (templateData === undefined) { return {}; } if (Array.isArray(templateData)) { data = templateData[index]; if (data) { return data; } grunt.log.error('You need to assign the same number of data files as source files when using array notation.'); return; } if (typeof templateData === 'object') { return templateData; } if (grunt.file.exists(templateData)) { return templateData; } if (isGlob(templateData) !== undefined) { data = filepath.replace(path.extname(filepath), path.extname(templateData)); if (data) { return data; } return grunt.log.error('No matching data file for ' + filepath + '.'); } return templateData; }; var getDest = function(destConfig, index) { var dest; if (Array.isArray(destConfig)) { dest = destConfig[index]; if (dest) { return dest; } grunt.log.error('You need to assign the same number of destination files as source files when using array notation.'); return; } if (isGlob(destConfig) !== undefined) { dest = grunt.file.expand(destConfig)[index]; if (dest) { return dest; } grunt.log.error('You need to assign the same number of destination files as source files when using glob notation.'); return; } return destConfig; }; grunt.registerMultiTask('compile-handlebars', 'Compile Handlebars templates ', function() { var fs = require('fs'); var path = require('path'); var files = this.files; var config = this.data; var helpers = getConfig(config.helpers); var partials = getConfig(config.partials); var done = this.async(); handlebarsPath = config.handlebars ? path.resolve(config.handlebars) : 'handlebars'; // Create a new instance so that we don't clobber our old defs when tasks are re-run // in the same process lifetime handlebars = require(handlebarsPath).create(); helpers.forEach(function(helper) { var fullPath = helper.replace(/\.[^/.]+$/, ''); var name = shouldRegisterFullPaths(config.registerFullPath, 'helpers') ? // full path, minus extention fullPath : // just the file's name getBasename(helper, config.helpers); if (handlebars.helpers[name] && usedHelpers.indexOf(fullPath) === -1) { grunt.log.error(name + ' is already registered, clobbering with the new value. Consider setting `registerFullPath` to true'); } else { usedHelpers.push(fullPath); } handlebars.registerHelper(name, require(fs.realpathSync(helper))); }); partials.forEach(function(partial) { var fullPath = partial.replace(/\.[^/.]+$/, ''); var name = shouldRegisterFullPaths(config.registerFullPath, 'partials') ? // full path, minus extention fullPath : // just the file's name getBasename(partial, config.partials); if (handlebars.partials[name] && usedPartials.indexOf(fullPath) === -1) { grunt.log.error(name + ' is already registered, clobbering with the new value. Consider setting `registerFullPath` to true'); } else { usedPartials.push(fullPath); } handlebars.registerPartial(name, fs.readFileSync(fs.realpathSync(partial), 'utf8')); }); var compile = function(file, filepath, index) { var dest = file.dest || ''; var template = filepath; var compiledTemplate = handlebars.compile(parseData(template, true)); var templateData = getTemplateData(config.templateData, filepath, index); var outputPath = getDest(dest, index); var appendToFile = (outputPath === file.orig.dest && grunt.file.exists(outputPath)); var operation = appendToFile ? 'appendFileSync' : 'writeFileSync'; var html = ''; var json; if (config.preHTML) { html += parseData(config.preHTML); } json = mergeJson(parseData(templateData), config.globals || []); html += compiledTemplate(json); if (config.postHTML) { html += parseData(config.postHTML); } // shamefully copied straight out of grunt's file command, to allow for appending // http://git.io/ZYGLEw grunt.file.mkdir(path.dirname(outputPath)); try { fs[operation](outputPath, html); grunt.verbose.ok(); return true; } catch (e) { grunt.verbose.error(); throw grunt.util.error('Unable to write "' + outputPath + '" file (Error code: ' + e.code + ').', e); } }; files.forEach(function(file) { if (Array.isArray(file.dest) && file.dest.length > file.src.length) { if (file.src.length > 1) { grunt.log.error('You may only have one source file when there are more destination files than source files.'); return; } else { file.dest.forEach(function(destpath, index) { compile(file, file.src[0], index); }); } } else { if (file.src.length > 0) { file.src.forEach(function(filepath, index) { compile(file, filepath, index); }); } else { file.orig.src.forEach(function(template, index) { compile(file, template, index); }); } } }); process.nextTick(done); }); };