helper.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /**
  2. * Both used by zrender and echarts.
  3. */
  4. const assert = require('assert');
  5. const rollup = require('rollup');
  6. const path = require('path');
  7. const fs = require('fs');
  8. const fsExtra = require('fs-extra');
  9. const babel = require('@babel/core');
  10. const esm2cjsPlugin = require('./babel-plugin-transform-modules-commonjs-ec');
  11. const removeDEVPlugin = require('./babel-plugin-transform-remove-dev');
  12. /**
  13. * @param {Array.<Object>} configs A list of rollup configs:
  14. * See: <https://rollupjs.org/#big-list-of-options>
  15. * For example:
  16. * [
  17. * {
  18. * ...inputOptions,
  19. * output: [outputOptions],
  20. * watch: {chokidar, include, exclude}
  21. * },
  22. * ...
  23. * ]
  24. * @return {Promise}
  25. */
  26. exports.build = function (configs) {
  27. return new Promise(function (promiseResolve, promiseReject) {
  28. let index = 0;
  29. buildSingle();
  30. function buildSingle() {
  31. let singleConfig = configs[index++];
  32. if (!singleConfig) {
  33. promiseResolve();
  34. return;
  35. }
  36. console.log(
  37. color('fgCyan', 'dim')('\nBundles '),
  38. color('fgCyan')(singleConfig.input),
  39. color('fgCyan', 'dim')('=>'),
  40. color('fgCyan')(singleConfig.output.file),
  41. color('fgCyan', 'dim')(' ...')
  42. );
  43. rollup
  44. .rollup(singleConfig)
  45. .then(function (bundle) {
  46. return bundle.write(singleConfig.output);
  47. })
  48. .then(function () {
  49. console.log(
  50. color('fgGreen', 'dim')('Created '),
  51. color('fgGreen')(singleConfig.output.file),
  52. color('fgGreen', 'dim')(' successfully.')
  53. );
  54. buildSingle();
  55. })
  56. .catch(function (err) {
  57. console.log(color('fgRed')(err));
  58. promiseReject();
  59. });
  60. }
  61. });
  62. };
  63. /**
  64. * @param {Object} singleConfig A single rollup config:
  65. * See: <https://rollupjs.org/#big-list-of-options>
  66. * For example:
  67. * {
  68. * ...inputOptions,
  69. * output: [outputOptions],
  70. * watch: {chokidar, include, exclude}
  71. * }
  72. */
  73. exports.watch = function (singleConfig) {
  74. let watcher = rollup.watch(singleConfig);
  75. watcher.on('event', function (event) {
  76. // event.code can be one of:
  77. // START — the watcher is (re)starting
  78. // BUNDLE_START — building an individual bundle
  79. // BUNDLE_END — finished building a bundle
  80. // END — finished building all bundles
  81. // ERROR — encountered an error while bundling
  82. // FATAL — encountered an unrecoverable error
  83. if (event.code !== 'START' && event.code !== 'END') {
  84. console.log(
  85. color('fgBlue')('[' + getTimeString() + ']'),
  86. color('dim')('build'),
  87. event.code.replace(/_/g, ' ').toLowerCase()
  88. );
  89. }
  90. if (event.code === 'ERROR' || event.code === 'FATAL') {
  91. printCodeError(event.error);
  92. }
  93. if (event.code === 'BUNDLE_END') {
  94. printWatchResult(event);
  95. }
  96. });
  97. };
  98. /**
  99. * @param {string} srcDir Absolute directory path.
  100. * @param {Function} [cb] Params: {
  101. * fileName: like 'some.js', without dir path.
  102. * relativePath: relative to srcDir.
  103. * absolutePath
  104. * }
  105. */
  106. exports.travelSrcDir = function (srcDir, cb) {
  107. assert(fs.statSync(srcDir).isDirectory());
  108. const regDir = /^[^.].*$/;
  109. const regSrc = /^[^.].*[.]js$/;
  110. doTravelSrcDir('.');
  111. function doTravelSrcDir(relativePath) {
  112. const absolutePath = path.resolve(srcDir, relativePath);
  113. fs.readdirSync(absolutePath).forEach(fileName => {
  114. const childAbsolutePath = path.resolve(absolutePath, fileName);
  115. const stat = fs.statSync(childAbsolutePath);
  116. if (stat.isDirectory()) {
  117. if (regDir.test(fileName)) {
  118. doTravelSrcDir(path.join(relativePath, fileName));
  119. }
  120. }
  121. else if (stat.isFile()) {
  122. if (regSrc.test(fileName)) {
  123. cb({fileName, relativePath, absolutePath: childAbsolutePath});
  124. }
  125. }
  126. });
  127. }
  128. };
  129. /**
  130. * @param {string} [opt]
  131. * @param {string} [opt.inputPath] Absolute input path.
  132. * @param {string} [opt.outputPath] Absolute output path.
  133. * @param {string} [opt.preamble]
  134. * @param {Function} [opt.transform]
  135. * @param {Function} [opt.reserveDEV]
  136. */
  137. exports.prePulishSrc = function ({inputPath, outputPath, preamble, transform, reserveDEV}) {
  138. assert(inputPath && outputPath);
  139. console.log(
  140. color('fgGreen', 'dim')('[transform] '),
  141. color('fgGreen')(inputPath),
  142. color('fgGreen', 'dim')('...')
  143. );
  144. let plugins = [];
  145. !reserveDEV && plugins.push(removeDEVPlugin);
  146. plugins.push(esm2cjsPlugin);
  147. let {code} = babel.transformFileSync(inputPath, {
  148. plugins: plugins
  149. });
  150. !reserveDEV && removeDEVPlugin.recheckDEV(code);
  151. if (transform) {
  152. code = transform({code, inputPath, outputPath});
  153. }
  154. if (preamble) {
  155. code = preamble + code;
  156. }
  157. fsExtra.ensureFileSync(outputPath);
  158. fs.writeFileSync(outputPath, code, {encoding:'utf-8'});
  159. };
  160. function printWatchResult(event) {
  161. console.log(
  162. color('fgGreen', 'dim')('Created'),
  163. color('fgGreen')(event.output.join(', ')),
  164. color('fgGreen', 'dim')('in'),
  165. color('fgGreen')(event.duration),
  166. color('fgGreen', 'dim')('ms.')
  167. );
  168. }
  169. function printCodeError(error) {
  170. console.log('\n' + color()(error.code));
  171. if (error.code === 'PARSE_ERROR') {
  172. console.log(
  173. color()('line'),
  174. color('fgCyan')(error.loc.line),
  175. color()('column'),
  176. color('fgCyan')(error.loc.column),
  177. color()('in'),
  178. color('fgCyan')(error.loc.file)
  179. );
  180. }
  181. if (error.frame) {
  182. console.log('\n' + color('fgRed')(error.frame));
  183. }
  184. console.log(color('dim')('\n' + error.stack));
  185. }
  186. function getTimeString() {
  187. return (new Date()).toLocaleString();
  188. }
  189. const COLOR_RESET = '\x1b[0m';
  190. const COLOR_MAP = {
  191. bright: '\x1b[1m',
  192. dim: '\x1b[2m',
  193. underscore: '\x1b[4m',
  194. blink: '\x1b[5m',
  195. reverse: '\x1b[7m',
  196. hidden: '\x1b[8m',
  197. fgBlack: '\x1b[30m',
  198. fgRed: '\x1b[31m',
  199. fgGreen: '\x1b[32m',
  200. fgYellow: '\x1b[33m',
  201. fgBlue: '\x1b[34m',
  202. fgMagenta: '\x1b[35m',
  203. fgCyan: '\x1b[36m',
  204. fgWhite: '\x1b[37m',
  205. bgBlack: '\x1b[40m',
  206. bgRed: '\x1b[41m',
  207. bgGreen: '\x1b[42m',
  208. bgYellow: '\x1b[43m',
  209. bgBlue: '\x1b[44m',
  210. bgMagenta: '\x1b[45m',
  211. bgCyan: '\x1b[46m',
  212. bgWhite: '\x1b[47m'
  213. };
  214. /**
  215. * Print colored text with `console.log`.
  216. *
  217. * Usage:
  218. * let color = require('colorConsole');
  219. * color('fgCyan')('some text'); // cyan text.
  220. * color('fgCyan', 'bright')('some text'); // bright cyan text.
  221. * color('fgCyan', 'bgRed')('some text') // cyan text and red background.
  222. */
  223. let color = exports.color = function () {
  224. let prefix = [];
  225. for (let i = 0; i < arguments.length; i++) {
  226. let color = COLOR_MAP[arguments[i]];
  227. color && prefix.push(color);
  228. }
  229. prefix = prefix.join('');
  230. return function (text) {
  231. return prefix + text + COLOR_RESET;
  232. };
  233. };