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');
};