var util = require('util'); var fs = require('fs'); var path = require('path'); var cheerio = require('cheerio'); var wiring = module.exports; /** * Update a file containing HTML markup with new content, either * appending, prepending or replacing content matching a particular * selector. * * The following modes are available: * * - `a` Append * - `p` Prepend * - `r` Replace * - `d` Delete * * @param {String} html * @param {String} tagName * @param {String} content * @param {String} mode */ wiring.domUpdate = function domUpdate(html, tagName, content, mode) { var $ = cheerio.load(html); if (content !== undefined) { if (mode === 'a') { $(tagName).append(content); } else if (mode === 'p') { $(tagName).prepend(content); } else if (mode === 'r') { $(tagName).html(content); } else if (mode === 'd') { $(tagName).remove(); } return $.html(); } else { console.error('Please supply valid content to be updated.'); } }; /** * Insert specific content as the last child of each element matched * by the `tagName` selector. * * @param {String} html * @param {String} tagName * @param {String} content */ wiring.append = function append(html, tagName, content) { return this.domUpdate(html, tagName, content, 'a'); }; /** * Insert specific content as the first child of each element matched * by the `tagName` selector. * * @param {String} html * @param {String} tagName * @param {String} content */ wiring.prepend = function prepend(html, tagName, content) { return this.domUpdate(html, tagName, content, 'p'); }; /** * Insert specific content as the last child of each element matched * by the `tagName` selector. Writes to file. * * @param {String} path * @param {String} tagName * @param {String} content */ wiring.appendToFile = function appendToFile(path, tagName, content) { var html = this.readFileAsString(path); var updatedContent = this.append(html, tagName, content); this.writeFileFromString(path, updatedContent); }; /** * Insert specific content as the first child of each element matched * by the `tagName` selector. Writes to file. * * @param {String} path * @param {String} tagName * @param {String} content */ wiring.prependToFile = function prependToFile(path, tagName, content) { var html = this.readFileAsString(path); var updatedContent = this.prepend(html, tagName, content); this.writeFileFromString(path, updatedContent); }; /** * Generate a usemin-handler block. * * @param {String} blockType * @param {String} optimizedPath * @param {String} filesBlock * @param {String|Array} searchPath */ wiring.generateBlock = function generateBlock(blockType, optimizedPath, filesBlock, searchPath) { var blockStart, blockEnd; var blockSearchPath = ''; if (searchPath !== undefined) { if (util.isArray(searchPath)) { searchPath = '{' + searchPath.join(',') + '}'; } blockSearchPath = '(' + searchPath + ')'; } blockStart = '\n \n'; blockEnd = ' \n'; return blockStart + filesBlock + blockEnd; }; /** * Append files, specifying the optimized path and generating the necessary * usemin blocks to be used for the build process. In the case of scripts and * styles, boilerplate script/stylesheet tags are written for you. * * @param {String|Object} htmlOrOptions * @param {String} fileType * @param {String} optimizedPath * @param {Array} sourceFileList * @param {Object} attrs * @param {String} searchPath */ wiring.appendFiles = function appendFiles(htmlOrOptions, fileType, optimizedPath, sourceFileList, attrs, searchPath) { var blocks, updatedContent; var html = htmlOrOptions; var files = ''; if (typeof htmlOrOptions === 'object') { html = htmlOrOptions.html; fileType = htmlOrOptions.fileType; optimizedPath = htmlOrOptions.optimizedPath; sourceFileList = htmlOrOptions.sourceFileList; attrs = htmlOrOptions.attrs; searchPath = htmlOrOptions.searchPath; } attrs = this.attributes(attrs); if (fileType === 'js') { sourceFileList.forEach(function (el) { files += ' \n'; }); blocks = this.generateBlock('js', optimizedPath, files, searchPath); updatedContent = this.append(html, 'body', blocks); } else if (fileType === 'css') { sourceFileList.forEach(function (el) { files += ' \n'; }); blocks = this.generateBlock('css', optimizedPath, files, searchPath); updatedContent = this.append(html, 'head', blocks); } // cleanup trailing whitespace return updatedContent.replace(/[\t ]+$/gm, ''); }; /** * Computes a given Hash object of attributes into its HTML representation. * * @param {Object} attrs */ wiring.attributes = function attributes(attrs) { return Object.keys(attrs || {}).map(function (key) { return key + '="' + attrs[key] + '"'; }).join(' '); }; /** * Scripts alias to `appendFiles`. * * @param {String} html * @param {String} optimizedPath * @param {Array} sourceFileList * @param {Object} attrs */ wiring.appendScripts = function appendScripts(html, optimizedPath, sourceFileList, attrs) { return this.appendFiles(html, 'js', optimizedPath, sourceFileList, attrs); }; /** * Simple script removal. * * @param {String} html * @param {String} scriptPath */ wiring.removeScript = function removeScript(html, scriptPath) { return this.domUpdate(html, 'script[src$="' + scriptPath + '"]', '', 'd'); }; /** * Style alias to `appendFiles`. * * @param {String} html * @param {String} optimizedPath * @param {Array} sourceFileList * @param {Object} attrs */ wiring.appendStyles = function appendStyles(html, optimizedPath, sourceFileList, attrs) { return this.appendFiles(html, 'css', optimizedPath, sourceFileList, attrs); }; /** * Simple style removal. * * @param {String} html * @param {String} path */ wiring.removeStyle = function removeStyle(html, path) { return this.domUpdate(html, 'link[href$="' + path + '"]', '', 'd'); }; /** * Append a directory of scripts. * * @param {String} html * @param {String} optimizedPath * @param {String} sourceScriptDir * @param {Object} attrs */ wiring.appendScriptsDir = function appendScriptsDir(html, optimizedPath, sourceScriptDir, attrs) { var sourceScriptList = fs.readdirSync(path.resolve(sourceScriptDir)); return this.appendFiles(html, 'js', optimizedPath, sourceScriptList, attrs); }; /** * Append a directory of stylesheets. * * @param {String} html * @param {String} optimizedPath * @param {String} sourceStyleDir * @param {Object} attrs */ wiring.appendStylesDir = function appendStylesDir(html, optimizedPath, sourceStyleDir, attrs) { var sourceStyleList = fs.readdirSync(path.resolve(sourceStyleDir)); return this.appendFiles(html, 'css', optimizedPath, sourceStyleList, attrs); }; /** * Read in the contents of a resolved file path as a string. * * @param {String} filePath */ wiring.readFileAsString = function readFileAsString(filePath) { return fs.readFileSync(path.resolve(filePath), 'utf8'); }; /** * Write the content of a string to a resolved file path. * * @param {String} html * @param {String} filePath */ wiring.writeFileFromString = function writeFileFromString(html, filePath) { fs.writeFileSync(path.resolve(filePath), html, 'utf8'); };