wiring.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. var util = require('util');
  2. var fs = require('fs');
  3. var path = require('path');
  4. var cheerio = require('cheerio');
  5. var wiring = module.exports;
  6. /**
  7. * Update a file containing HTML markup with new content, either
  8. * appending, prepending or replacing content matching a particular
  9. * selector.
  10. *
  11. * The following modes are available:
  12. *
  13. * - `a` Append
  14. * - `p` Prepend
  15. * - `r` Replace
  16. * - `d` Delete
  17. *
  18. * @param {String} html
  19. * @param {String} tagName
  20. * @param {String} content
  21. * @param {String} mode
  22. */
  23. wiring.domUpdate = function domUpdate(html, tagName, content, mode) {
  24. var $ = cheerio.load(html);
  25. if (content !== undefined) {
  26. if (mode === 'a') {
  27. $(tagName).append(content);
  28. } else if (mode === 'p') {
  29. $(tagName).prepend(content);
  30. } else if (mode === 'r') {
  31. $(tagName).html(content);
  32. } else if (mode === 'd') {
  33. $(tagName).remove();
  34. }
  35. return $.html();
  36. } else {
  37. console.error('Please supply valid content to be updated.');
  38. }
  39. };
  40. /**
  41. * Insert specific content as the last child of each element matched
  42. * by the `tagName` selector.
  43. *
  44. * @param {String} html
  45. * @param {String} tagName
  46. * @param {String} content
  47. */
  48. wiring.append = function append(html, tagName, content) {
  49. return this.domUpdate(html, tagName, content, 'a');
  50. };
  51. /**
  52. * Insert specific content as the first child of each element matched
  53. * by the `tagName` selector.
  54. *
  55. * @param {String} html
  56. * @param {String} tagName
  57. * @param {String} content
  58. */
  59. wiring.prepend = function prepend(html, tagName, content) {
  60. return this.domUpdate(html, tagName, content, 'p');
  61. };
  62. /**
  63. * Insert specific content as the last child of each element matched
  64. * by the `tagName` selector. Writes to file.
  65. *
  66. * @param {String} path
  67. * @param {String} tagName
  68. * @param {String} content
  69. */
  70. wiring.appendToFile = function appendToFile(path, tagName, content) {
  71. var html = this.readFileAsString(path);
  72. var updatedContent = this.append(html, tagName, content);
  73. this.writeFileFromString(path, updatedContent);
  74. };
  75. /**
  76. * Insert specific content as the first child of each element matched
  77. * by the `tagName` selector. Writes to file.
  78. *
  79. * @param {String} path
  80. * @param {String} tagName
  81. * @param {String} content
  82. */
  83. wiring.prependToFile = function prependToFile(path, tagName, content) {
  84. var html = this.readFileAsString(path);
  85. var updatedContent = this.prepend(html, tagName, content);
  86. this.writeFileFromString(path, updatedContent);
  87. };
  88. /**
  89. * Generate a usemin-handler block.
  90. *
  91. * @param {String} blockType
  92. * @param {String} optimizedPath
  93. * @param {String} filesBlock
  94. * @param {String|Array} searchPath
  95. */
  96. wiring.generateBlock = function generateBlock(blockType, optimizedPath, filesBlock, searchPath) {
  97. var blockStart, blockEnd;
  98. var blockSearchPath = '';
  99. if (searchPath !== undefined) {
  100. if (util.isArray(searchPath)) {
  101. searchPath = '{' + searchPath.join(',') + '}';
  102. }
  103. blockSearchPath = '(' + searchPath + ')';
  104. }
  105. blockStart = '\n <!-- build:' + blockType + blockSearchPath + ' ' + optimizedPath + ' -->\n';
  106. blockEnd = ' <!-- endbuild -->\n';
  107. return blockStart + filesBlock + blockEnd;
  108. };
  109. /**
  110. * Append files, specifying the optimized path and generating the necessary
  111. * usemin blocks to be used for the build process. In the case of scripts and
  112. * styles, boilerplate script/stylesheet tags are written for you.
  113. *
  114. * @param {String|Object} htmlOrOptions
  115. * @param {String} fileType
  116. * @param {String} optimizedPath
  117. * @param {Array} sourceFileList
  118. * @param {Object} attrs
  119. * @param {String} searchPath
  120. */
  121. wiring.appendFiles = function appendFiles(htmlOrOptions, fileType, optimizedPath, sourceFileList, attrs, searchPath) {
  122. var blocks, updatedContent;
  123. var html = htmlOrOptions;
  124. var files = '';
  125. if (typeof htmlOrOptions === 'object') {
  126. html = htmlOrOptions.html;
  127. fileType = htmlOrOptions.fileType;
  128. optimizedPath = htmlOrOptions.optimizedPath;
  129. sourceFileList = htmlOrOptions.sourceFileList;
  130. attrs = htmlOrOptions.attrs;
  131. searchPath = htmlOrOptions.searchPath;
  132. }
  133. attrs = this.attributes(attrs);
  134. if (fileType === 'js') {
  135. sourceFileList.forEach(function (el) {
  136. files += ' <script ' + attrs + ' src="' + el + '"></script>\n';
  137. });
  138. blocks = this.generateBlock('js', optimizedPath, files, searchPath);
  139. updatedContent = this.append(html, 'body', blocks);
  140. } else if (fileType === 'css') {
  141. sourceFileList.forEach(function (el) {
  142. files += ' <link ' + attrs + ' rel="stylesheet" href="' + el + '">\n';
  143. });
  144. blocks = this.generateBlock('css', optimizedPath, files, searchPath);
  145. updatedContent = this.append(html, 'head', blocks);
  146. }
  147. // cleanup trailing whitespace
  148. return updatedContent.replace(/[\t ]+$/gm, '');
  149. };
  150. /**
  151. * Computes a given Hash object of attributes into its HTML representation.
  152. *
  153. * @param {Object} attrs
  154. */
  155. wiring.attributes = function attributes(attrs) {
  156. return Object.keys(attrs || {}).map(function (key) {
  157. return key + '="' + attrs[key] + '"';
  158. }).join(' ');
  159. };
  160. /**
  161. * Scripts alias to `appendFiles`.
  162. *
  163. * @param {String} html
  164. * @param {String} optimizedPath
  165. * @param {Array} sourceFileList
  166. * @param {Object} attrs
  167. */
  168. wiring.appendScripts = function appendScripts(html, optimizedPath, sourceFileList, attrs) {
  169. return this.appendFiles(html, 'js', optimizedPath, sourceFileList, attrs);
  170. };
  171. /**
  172. * Simple script removal.
  173. *
  174. * @param {String} html
  175. * @param {String} scriptPath
  176. */
  177. wiring.removeScript = function removeScript(html, scriptPath) {
  178. return this.domUpdate(html, 'script[src$="' + scriptPath + '"]', '', 'd');
  179. };
  180. /**
  181. * Style alias to `appendFiles`.
  182. *
  183. * @param {String} html
  184. * @param {String} optimizedPath
  185. * @param {Array} sourceFileList
  186. * @param {Object} attrs
  187. */
  188. wiring.appendStyles = function appendStyles(html, optimizedPath, sourceFileList, attrs) {
  189. return this.appendFiles(html, 'css', optimizedPath, sourceFileList, attrs);
  190. };
  191. /**
  192. * Simple style removal.
  193. *
  194. * @param {String} html
  195. * @param {String} path
  196. */
  197. wiring.removeStyle = function removeStyle(html, path) {
  198. return this.domUpdate(html, 'link[href$="' + path + '"]', '', 'd');
  199. };
  200. /**
  201. * Append a directory of scripts.
  202. *
  203. * @param {String} html
  204. * @param {String} optimizedPath
  205. * @param {String} sourceScriptDir
  206. * @param {Object} attrs
  207. */
  208. wiring.appendScriptsDir = function appendScriptsDir(html, optimizedPath, sourceScriptDir, attrs) {
  209. var sourceScriptList = fs.readdirSync(path.resolve(sourceScriptDir));
  210. return this.appendFiles(html, 'js', optimizedPath, sourceScriptList, attrs);
  211. };
  212. /**
  213. * Append a directory of stylesheets.
  214. *
  215. * @param {String} html
  216. * @param {String} optimizedPath
  217. * @param {String} sourceStyleDir
  218. * @param {Object} attrs
  219. */
  220. wiring.appendStylesDir = function appendStylesDir(html, optimizedPath, sourceStyleDir, attrs) {
  221. var sourceStyleList = fs.readdirSync(path.resolve(sourceStyleDir));
  222. return this.appendFiles(html, 'css', optimizedPath, sourceStyleList, attrs);
  223. };
  224. /**
  225. * Read in the contents of a resolved file path as a string.
  226. *
  227. * @param {String} filePath
  228. */
  229. wiring.readFileAsString = function readFileAsString(filePath) {
  230. return fs.readFileSync(path.resolve(filePath), 'utf8');
  231. };
  232. /**
  233. * Write the content of a string to a resolved file path.
  234. *
  235. * @param {String} html
  236. * @param {String} filePath
  237. */
  238. wiring.writeFileFromString = function writeFileFromString(html, filePath) {
  239. fs.writeFileSync(path.resolve(filePath), html, 'utf8');
  240. };