build.js 8.6 KB

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