tasks.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. 'use strict';
  2. let _ = require('lodash');
  3. let del = require('del');
  4. let gear = require('gear');
  5. let path = require('path');
  6. let utility = require('./utility');
  7. let parseHeader = utility.parseHeader;
  8. let tasks = require('gear-lib');
  9. tasks.clean = function(directories, blobs, done) {
  10. directories = _.isString(directories) ? [directories] : directories;
  11. return del(directories).then(() => done(null, blobs));
  12. };
  13. tasks.clean.type = 'collect';
  14. // Depending on the languages required for the current language being
  15. // processed, this task reorders it's dependencies first then include the
  16. // language.
  17. tasks.reorderDeps = function(options, blobs, done) {
  18. let buffer = {},
  19. newBlobOrder = [];
  20. _.each(blobs, function(blob) {
  21. let basename = path.basename(blob.name),
  22. fileInfo = parseHeader(blob.result),
  23. extra = { blob: blob, processed: false };
  24. buffer[basename] = _.merge(extra, fileInfo || {});
  25. });
  26. function pushInBlob(object) {
  27. if(!object.processed) {
  28. object.processed = true;
  29. newBlobOrder.push(object.blob);
  30. }
  31. }
  32. _.each(buffer, function(buf) {
  33. let object;
  34. if(buf.Requires) {
  35. _.each(buf.Requires, function(language) {
  36. object = buffer[language];
  37. pushInBlob(object);
  38. });
  39. }
  40. pushInBlob(buf);
  41. });
  42. done(null, newBlobOrder);
  43. };
  44. tasks.reorderDeps.type = 'collect';
  45. tasks.template = function(template, blob, done) {
  46. template = template || '';
  47. let filename = path.basename(blob.name),
  48. basename = path.basename(filename, '.js'),
  49. content = _.template(template)({
  50. name: basename,
  51. filename: filename,
  52. content: blob.result.trim()
  53. });
  54. return done(null, new gear.Blob(content, blob));
  55. };
  56. tasks.templateAll = function(options, blobs, done) {
  57. return options.callback(blobs)
  58. .then(function(data) {
  59. let template = options.template || data.template,
  60. content = _.template(template)(data);
  61. return done(null, [new gear.Blob(content)]);
  62. })
  63. .catch(done);
  64. };
  65. tasks.templateAll.type = 'collect';
  66. tasks.rename = function(options, blob, done) {
  67. options = options || {};
  68. let name = blob.name,
  69. ext = new RegExp(path.extname(name) + '$');
  70. name = name.replace(ext, options.extname);
  71. return done(null, new gear.Blob(blob.result, { name: name }));
  72. };
  73. // Adds the contributors from `AUTHORS.en.txt` onto the `package.json` file
  74. // and moves the result into the `build` directory.
  75. tasks.buildPackage = function(json, blob, done) {
  76. let result,
  77. lines = blob.result.split(/\r?\n/),
  78. regex = /^- (.*) <(.*)>$/;
  79. json.contributors = _.transform(lines, function(result, line) {
  80. let matches = line.match(regex);
  81. if(matches) {
  82. result.push({
  83. name: matches[1],
  84. email: matches[2]
  85. });
  86. }
  87. }, []);
  88. result = JSON.stringify(json, null, ' ');
  89. return done(null, new gear.Blob(result, blob));
  90. };
  91. // Mainly for replacing the keys of `utility.REPLACES` for it's values while
  92. // skipping over strings, regular expressions, or comments. However, this is
  93. // pretty generic so long as you use the `utility.replace` function, you can
  94. // replace a regular expression with a string.
  95. tasks.replaceSkippingStrings = function(params, blob, done) {
  96. let content = blob.result,
  97. length = content.length,
  98. offset = 0,
  99. replace = params.replace || '',
  100. regex = params.regex,
  101. starts = /\/\/|['"\/]/,
  102. result = [],
  103. chunk, end, match, start, terminator;
  104. while(offset < length) {
  105. chunk = content.slice(offset);
  106. match = chunk.match(starts);
  107. end = match ? match.index : length;
  108. chunk = content.slice(offset, end + offset);
  109. result.push(chunk.replace(regex, replace));
  110. offset += end;
  111. if(match) {
  112. // We found a starter sequence: either a `//` or a "quote"
  113. // In the case of `//` our terminator is the end of line.
  114. // Otherwise it's either a matching quote or an escape symbol.
  115. terminator = match[0] !== '//' ? new RegExp(`[${match[0]}\\\\]`)
  116. : /$/m;
  117. start = offset;
  118. offset += 1;
  119. while(true) {
  120. chunk = content.slice(offset);
  121. match = chunk.match(terminator);
  122. if(!match) {
  123. return done('Unmatched quote');
  124. }
  125. if(match[0] === '\\') {
  126. offset += match.index + 2;
  127. } else {
  128. offset += match.index + 1;
  129. result.push(content.slice(start, offset));
  130. break;
  131. }
  132. }
  133. }
  134. }
  135. return done(null, new gear.Blob(result.join(''), blob));
  136. };
  137. tasks.filter = function(callback, blobs, done) {
  138. let filteredBlobs = _.filter(blobs, callback);
  139. // Re-add in blobs required from header definition
  140. _.each(filteredBlobs, function(blob) {
  141. let dirname = path.dirname(blob.name),
  142. content = blob.result,
  143. fileInfo = parseHeader(content);
  144. if(fileInfo && fileInfo.Requires) {
  145. _.each(fileInfo.Requires, function(language) {
  146. let filename = `${dirname}/${language}`,
  147. fileFound = _.find(filteredBlobs, { name: filename });
  148. if(!fileFound) {
  149. filteredBlobs.push(
  150. _.find(blobs, { name: filename }));
  151. }
  152. });
  153. }
  154. });
  155. return done(null, filteredBlobs);
  156. };
  157. tasks.filter.type = 'collect';
  158. tasks.readSnippet = function(options, blob, done) {
  159. let name = path.basename(blob.name, '.js'),
  160. fileInfo = parseHeader(blob.result),
  161. snippetName = path.join('test', 'detect', name, 'default.txt');
  162. function onRead(error, blob) {
  163. if(error) return done(error); // ignore missing snippets
  164. let meta = { name: `${name}.js`, fileInfo: fileInfo };
  165. return done(null, new gear.Blob(blob.result, meta));
  166. }
  167. gear.Blob.readFile(snippetName, 'utf8', onRead, false);
  168. };
  169. tasks.insertLicenseTag = function(options, blob, done) {
  170. let hljsVersion = require('../package').version,
  171. licenseTag = `/*! highlight.js v${hljsVersion} | ` +
  172. `BSD3 License | git.io/hljslicense */\n`;
  173. return done(null, new gear.Blob(licenseTag + blob.result, blob));
  174. };
  175. // Packages up included languages into the core `highlight.js` and moves the
  176. // result into the `build` directory.
  177. tasks.packageFiles = function(options, blobs, done) {
  178. let content,
  179. coreFile = _.head(blobs),
  180. languages = _.tail(blobs),
  181. lines = coreFile.result
  182. .replace(utility.regex.header, '')
  183. .split('\n\n'),
  184. lastLine = _.last(lines),
  185. langStr = _.reduce(languages, (str, language) =>
  186. `${str + language.result}\n`, '');
  187. lines[lines.length - 1] = langStr.trim();
  188. lines = lines.concat(lastLine);
  189. content = lines.join('\n\n');
  190. return done(null, [new gear.Blob(content)]);
  191. };
  192. tasks.packageFiles.type = 'collect';
  193. module.exports = new gear.Registry({ tasks: tasks });