plugin.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. (function() {
  2. var NpmAdapter, async, chalk, clip, commonOptions, displayListing, extendOptions, fetchListing, fileExists, fs, https, loadEnv, logger, lpad, main, max, mkdirp, normalizePluginName, npm, options, path, readJSON, ref, ref1, usage, waterfall,
  3. indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
  4. async = require('async');
  5. chalk = require('chalk');
  6. fs = require('fs');
  7. path = require('path');
  8. npm = require('npm');
  9. mkdirp = require('mkdirp');
  10. https = require('https');
  11. ref = require('./common'), NpmAdapter = ref.NpmAdapter, loadEnv = ref.loadEnv, commonOptions = ref.commonOptions, extendOptions = ref.extendOptions;
  12. ref1 = require('./../core/utils'), fileExists = ref1.fileExists, readJSON = ref1.readJSON;
  13. logger = require('./../core/logger').logger;
  14. usage = "\nusage: wintersmith plugin [options] <command>\n\ncommands:\n\n " + (chalk.bold('list')) + " - list available plugins\n " + (chalk.bold('install')) + " <plugin> - install plugin\n\noptions:\n\n -C, --chdir [path] change the working directory\n -c, --config [path] path to config\n";
  15. options = {};
  16. extendOptions(options, commonOptions);
  17. max = function(array, get) {
  18. var item, j, len, rv, v;
  19. if (get == null) {
  20. get = function(item) {
  21. return item;
  22. };
  23. }
  24. rv = null;
  25. for (j = 0, len = array.length; j < len; j++) {
  26. item = array[j];
  27. v = get(item);
  28. if (v > rv) {
  29. rv = v;
  30. }
  31. }
  32. return rv;
  33. };
  34. lpad = function(string, amount, char) {
  35. var i, j, p, ref2;
  36. if (char == null) {
  37. char = ' ';
  38. }
  39. p = '';
  40. for (i = j = 0, ref2 = amount - string.length; 0 <= ref2 ? j < ref2 : j > ref2; i = 0 <= ref2 ? ++j : --j) {
  41. p += char;
  42. }
  43. return p + string;
  44. };
  45. clip = function(string, maxlen) {
  46. if (string.length <= maxlen) {
  47. return string;
  48. }
  49. return string.slice(0, maxlen - 2).trim() + "..";
  50. };
  51. fetchListing = function(callback) {
  52. var request;
  53. return request = https.get('https://api.npms.io/v2/search?q=keywords:wintersmith-plugin&size=200', function(response) {
  54. var data, error;
  55. if (response.statusCode !== 200) {
  56. error = new Error("Unexpected response when searching registry, HTTP " + response.statusCode);
  57. }
  58. if (!/^application\/json/.test(response.headers['content-type'])) {
  59. error = new Error("Invalid content-type: " + response.headers['content-type']);
  60. }
  61. if (error != null) {
  62. response.resume();
  63. callback(error);
  64. return;
  65. }
  66. data = [];
  67. response.on('data', function(chunk) {
  68. return data.push(chunk);
  69. });
  70. return response.on('end', function() {
  71. var listing, parsed;
  72. try {
  73. parsed = JSON.parse(Buffer.concat(data));
  74. } catch (error1) {
  75. error = error1;
  76. callback(error);
  77. return;
  78. }
  79. listing = parsed.results.map(function(result) {
  80. return result["package"];
  81. });
  82. listing.sort(function(a, b) {
  83. if (a.name > b.name) {
  84. return 1;
  85. }
  86. if (a.name < b.name) {
  87. return -1;
  88. }
  89. return 0;
  90. });
  91. return callback(null, listing);
  92. });
  93. });
  94. };
  95. displayListing = function(list, callback) {
  96. var display, j, k, left, len, line, margin, maxw, pad, plugin, results;
  97. display = list.map(function(plugin) {
  98. var description, homepage, maintainers, name, ref2, ref3, ref4;
  99. name = normalizePluginName(plugin.name);
  100. description = plugin.description;
  101. maintainers = plugin.maintainers.map(function(v) {
  102. return v.username;
  103. }).join(' ');
  104. homepage = (ref2 = (ref3 = plugin.links) != null ? ref3.homepage : void 0) != null ? ref2 : (ref4 = plugin.links) != null ? ref4.npm : void 0;
  105. return {
  106. name: name,
  107. description: description,
  108. maintainers: maintainers,
  109. homepage: homepage
  110. };
  111. });
  112. pad = max(display, function(item) {
  113. return item.name.length;
  114. });
  115. maxw = process.stdout.getWindowSize()[0] - 2;
  116. margin = ((function() {
  117. results = [];
  118. for (var j = 0; 0 <= pad ? j < pad : j > pad; 0 <= pad ? j++ : j--){ results.push(j); }
  119. return results;
  120. }).apply(this).map(function() {
  121. return ' ';
  122. })).join('');
  123. for (k = 0, len = display.length; k < len; k++) {
  124. plugin = display[k];
  125. line = (lpad(plugin.name, pad)) + " " + (clip(plugin.description, maxw - pad - 2));
  126. left = maxw - line.length;
  127. if (left > plugin.maintainers.length) {
  128. line += chalk.grey(lpad(plugin.maintainers, left));
  129. }
  130. logger.info(line.replace(/^\s*(\S+) /, function(m) {
  131. return chalk.bold(m);
  132. }));
  133. if ((plugin.homepage != null) && plugin.homepage.length < maxw - pad - 2) {
  134. logger.info(margin + " " + (chalk.gray(plugin.homepage)));
  135. }
  136. logger.info('');
  137. }
  138. return callback(null, list);
  139. };
  140. waterfall = function(flow, callback) {
  141. /* async.waterfall that allows for parallel tasks */
  142. var item, j, len, resolved;
  143. resolved = [];
  144. for (j = 0, len = flow.length; j < len; j++) {
  145. item = flow[j];
  146. switch (typeof item) {
  147. case 'function':
  148. resolved.push(item);
  149. break;
  150. case 'object':
  151. case 'array':
  152. resolved.push(async.apply(async.parallel, item));
  153. break;
  154. default:
  155. return callback(new Error("Invalid item '" + item + "' in flow"));
  156. }
  157. }
  158. return async.waterfall(resolved, callback);
  159. };
  160. normalizePluginName = function(name) {
  161. return name.replace(/^wintersmith\-/, '');
  162. };
  163. main = function(argv) {
  164. var action, cmd, installPlugin, loadCurrentEnv;
  165. action = argv._[3];
  166. if (action == null) {
  167. console.log(usage);
  168. process.exit(0);
  169. }
  170. loadCurrentEnv = function(callback) {
  171. return loadEnv(argv, callback);
  172. };
  173. installPlugin = function(res, callback) {
  174. var configFile, createPackageJson, env, install, j, len, list, name, p, packageFile, plugin, readConfig, saveConfig, updateConfig;
  175. env = res[0], list = res[1];
  176. name = argv._[4];
  177. plugin = null;
  178. for (j = 0, len = list.length; j < len; j++) {
  179. p = list[j];
  180. if (normalizePluginName(p.name) === normalizePluginName(name)) {
  181. plugin = p;
  182. break;
  183. }
  184. }
  185. if (!plugin) {
  186. return callback(new Error("Unknown plugin: " + name));
  187. }
  188. configFile = env.config.__filename;
  189. packageFile = env.resolvePath('package.json');
  190. createPackageJson = function(callback) {
  191. return fileExists(packageFile, function(exists) {
  192. if (exists) {
  193. return callback();
  194. } else {
  195. logger.warn("package.json missing, creating minimal package");
  196. return fs.writeFile(packageFile, '{\n "dependencies": {},\n "private": true\n}\n', callback);
  197. }
  198. });
  199. };
  200. readConfig = function(callback) {
  201. return readJSON(configFile, callback);
  202. };
  203. updateConfig = function(config, callback) {
  204. var ref2;
  205. if (config.plugins == null) {
  206. config.plugins = [];
  207. }
  208. if (ref2 = plugin.name, indexOf.call(config.plugins, ref2) < 0) {
  209. config.plugins.push(plugin.name);
  210. }
  211. return callback(null, config);
  212. };
  213. saveConfig = function(config, callback) {
  214. var json;
  215. logger.verbose("saving config file: " + configFile);
  216. json = JSON.stringify(config, null, 2);
  217. return fs.writeFile(configFile, json + '\n', callback);
  218. };
  219. install = function(callback) {
  220. logger.verbose("installing " + plugin.name);
  221. process.chdir(env.workDir);
  222. return async.series([
  223. createPackageJson, function(callback) {
  224. return npm.load({
  225. logstream: new NpmAdapter(logger),
  226. save: true
  227. }, callback);
  228. }, function(callback) {
  229. return npm.install(plugin.name, callback);
  230. }
  231. ], function(error) {
  232. return callback(error);
  233. });
  234. };
  235. return async.waterfall([install, readConfig, updateConfig, saveConfig], callback);
  236. };
  237. switch (action) {
  238. case 'list':
  239. cmd = [fetchListing, displayListing];
  240. break;
  241. case 'install':
  242. cmd = [[loadCurrentEnv, fetchListing], installPlugin];
  243. break;
  244. default:
  245. cmd = [
  246. function(callback) {
  247. return callback(new Error("Unknown plugin action: " + action));
  248. }
  249. ];
  250. }
  251. return waterfall(cmd, function(error) {
  252. if (error != null) {
  253. logger.error(error.message, error);
  254. return process.exit(1);
  255. } else {
  256. return process.exit(0);
  257. }
  258. });
  259. };
  260. module.exports = main;
  261. module.exports.usage = usage;
  262. module.exports.options = options;
  263. }).call(this);