build.js 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #!/usr/bin/env node
  2. const fsExtra = require('fs-extra');
  3. const fs = require('fs');
  4. const {resolve} = require('path');
  5. const config = require('./config.js');
  6. const commander = require('commander');
  7. const {build, watch, color} = require('zrender/build/helper');
  8. const ecLangPlugin = require('./rollup-plugin-ec-lang');
  9. const prePublish = require('./pre-publish');
  10. const recheckDEV = require('zrender/build/babel-plugin-transform-remove-dev').recheckDEV;
  11. function run() {
  12. /**
  13. * Tips for `commander`:
  14. * (1) If arg xxx not specified, `commander.xxx` is undefined.
  15. * Otherwise:
  16. * If '-x, --xxx', `commander.xxx` can only be true/false, even if '--xxx yyy' input.
  17. * If '-x, --xxx <some>', the 'some' string is required, or otherwise error will be thrown.
  18. * If '-x, --xxx [some]', the 'some' string is optional, that is, `commander.xxx` can be boolean or string.
  19. * (2) `node ./build/build.js --help` will print helper info and exit.
  20. */
  21. let descIndent = ' ';
  22. let egIndent = ' ';
  23. commander
  24. .usage('[options]')
  25. .description([
  26. 'Build echarts and generate result files in directory `echarts/dist`.',
  27. '',
  28. ' For example:',
  29. '',
  30. egIndent + 'node build/build.js --release'
  31. + '\n' + descIndent + '# Build all to `dist` folder.',
  32. egIndent + 'node build/build.js --prepublish'
  33. + '\n' + descIndent + '# Only prepublish.',
  34. egIndent + 'node build/build.js --removedev'
  35. + '\n' + descIndent + '# Remove __DEV__ code. If --min, __DEV__ always be removed.',
  36. egIndent + 'node build/build.js --type ""'
  37. + '\n' + descIndent + '# Only generate `dist/echarts.js`.',
  38. egIndent + 'node build/build.js --type common --min'
  39. + '\n' + descIndent + '# Only generate `dist/echarts.common.min.js`.',
  40. egIndent + 'node build/build.js --type simple --min --lang en'
  41. + '\n' + descIndent + '# Only generate `dist/echarts-en.simple.min.js`.',
  42. egIndent + 'node build/build.js --lang "my/lang.js" -i "my/index.js" -o "my/bundle.js"'
  43. + '\n' + descIndent + '# Take `<cwd>/my/index.js` as input and generate `<cwd>/my/bundle.js`,'
  44. + '\n' + descIndent + 'where `<cwd>/my/lang.js` is used as language file.',
  45. ].join('\n'))
  46. .option(
  47. '-w, --watch', [
  48. 'Watch modifications of files and auto-compile to dist file. For example,',
  49. descIndent + '`echarts/dist/echarts.js`.'
  50. ].join('\n'))
  51. .option(
  52. '--lang <language file path or shortcut>', [
  53. 'Use the specified file instead of `echarts/src/lang.js`. For example:',
  54. descIndent + '`--lang en` will use `echarts/src/langEN.js`.',
  55. descIndent + '`--lang my/langDE.js` will use `<cwd>/my/langDE.js`. -o must be specified in this case.',
  56. descIndent + '`--lang /my/indexSW.js` will use `/my/indexSW.js`. -o must be specified in this case.'
  57. ].join('\n'))
  58. .option(
  59. '--release',
  60. 'Build all for release'
  61. )
  62. .option(
  63. '--prepublish',
  64. 'Build all for release'
  65. )
  66. .option(
  67. '--removedev',
  68. 'Remove __DEV__ code. If --min, __DEV__ always be removed.'
  69. )
  70. .option(
  71. '--min',
  72. 'Whether to compress the output file, and remove error-log-print code.'
  73. )
  74. .option(
  75. '--type <type name>', [
  76. 'Can be "simple" or "common" or "" (default). For example,',
  77. descIndent + '`--type ""` or `--type "common"`.'
  78. ].join('\n'))
  79. .option(
  80. '--sourcemap',
  81. 'Whether output sourcemap.'
  82. )
  83. .option(
  84. '--format <format>',
  85. 'The format of output bundle. Can be "umd", "amd", "iife", "cjs", "es".'
  86. )
  87. .option(
  88. '-i, --input <input file path>',
  89. 'If input file path is specified, output file path must be specified too.'
  90. )
  91. .option(
  92. '-o, --output <output file path>',
  93. 'If output file path is specified, input file path must be specified too.'
  94. )
  95. .parse(process.argv);
  96. let isWatch = !!commander.watch;
  97. let isRelease = !!commander.release;
  98. let isPrePublish = !!commander.prepublish;
  99. let opt = {
  100. lang: commander.lang,
  101. min: commander.min,
  102. type: commander.type || '',
  103. input: commander.input,
  104. output: commander.output,
  105. format: commander.format,
  106. sourcemap: commander.sourcemap,
  107. removeDev: commander.removedev,
  108. addBundleVersion: isWatch
  109. };
  110. validateIO(opt.input, opt.output);
  111. validateLang(opt.lang, opt.output);
  112. normalizeParams(opt);
  113. // Clear `echarts/dist`
  114. if (isRelease) {
  115. fsExtra.removeSync(getPath('./dist'));
  116. }
  117. if (isWatch) {
  118. watch(config.createECharts(opt));
  119. }
  120. else if (isPrePublish) {
  121. prePublish();
  122. }
  123. else if (isRelease) {
  124. let configs = [];
  125. let configForCheck;
  126. [
  127. {min: false},
  128. {min: true},
  129. {min: false, lang: 'en'},
  130. {min: true, lang: 'en'}
  131. ].forEach(function (opt) {
  132. ['', 'simple', 'common'].forEach(function (type) {
  133. let singleOpt = Object.assign({type}, opt);
  134. normalizeParams(singleOpt);
  135. let singleConfig = config.createECharts(singleOpt);
  136. configs.push(singleConfig);
  137. if (singleOpt.min && singleOpt.type === '') {
  138. configForCheck = singleConfig;
  139. }
  140. });
  141. });
  142. configs.push(
  143. config.createBMap(false),
  144. config.createBMap(true),
  145. config.createDataTool(false),
  146. config.createDataTool(true)
  147. );
  148. build(configs)
  149. .then(function () {
  150. checkCode(configForCheck);
  151. prePublish();
  152. }).catch(handleBuildError);
  153. }
  154. else {
  155. let cfg = config.createECharts(opt);
  156. build([cfg])
  157. .then(function () {
  158. if (opt.removeDev) {
  159. checkCode(cfg);
  160. }
  161. })
  162. .catch(handleBuildError);
  163. }
  164. }
  165. function normalizeParams(opt) {
  166. if (opt.sourcemap == null) {
  167. opt.sourcemap = !(opt.min || opt.type);
  168. }
  169. if (opt.removeDev == null) {
  170. opt.removeDev = !!opt.min;
  171. }
  172. }
  173. function handleBuildError(err) {
  174. console.log(err);
  175. }
  176. function checkCode(singleConfig) {
  177. // Make sure __DEV__ is eliminated.
  178. let code = fs.readFileSync(singleConfig.output.file, {encoding: 'utf-8'});
  179. if (!code) {
  180. throw new Error(`${singleConfig.output.file} is empty`);
  181. }
  182. recheckDEV(code);
  183. console.log(color('fgGreen', 'dim')('Check code: correct.'));
  184. }
  185. function validateIO(input, output) {
  186. if ((input != null && output == null)
  187. || (input == null && output != null)
  188. ) {
  189. throw new Error('`input` and `output` must be both set.');
  190. }
  191. }
  192. function validateLang(lang, output) {
  193. if (!lang) {
  194. return;
  195. }
  196. let langInfo = ecLangPlugin.getLangFileInfo(lang);
  197. if (langInfo.isOuter && !output) {
  198. throw new Error('`-o` or `--output` must be specified if using a file path in `--lang`.');
  199. }
  200. if (!langInfo.absolutePath || !fs.statSync(langInfo.absolutePath).isFile()) {
  201. throw new Error(`File ${langInfo.absolutePath} does not exist yet. Contribution is welcome!`);
  202. }
  203. }
  204. /**
  205. * @param {string} relativePath Based on echarts directory.
  206. * @return {string} Absolute path.
  207. */
  208. function getPath(relativePath) {
  209. return resolve(__dirname, '../', relativePath);
  210. }
  211. run();