#!/usr/bin/env node /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ const fsExtra = require('fs-extra'); const fs = require('fs'); const {resolve} = require('path'); const config = require('./config.js'); const commander = require('commander'); const {build, watch, color} = require('zrender/build/helper'); const ecLangPlugin = require('./rollup-plugin-ec-lang'); const prePublish = require('./pre-publish'); const recheckDEV = require('zrender/build/babel-plugin-transform-remove-dev').recheckDEV; function run() { /** * Tips for `commander`: * (1) If arg xxx not specified, `commander.xxx` is undefined. * Otherwise: * If '-x, --xxx', `commander.xxx` can only be true/false, even if '--xxx yyy' input. * If '-x, --xxx ', the 'some' string is required, or otherwise error will be thrown. * If '-x, --xxx [some]', the 'some' string is optional, that is, `commander.xxx` can be boolean or string. * (2) `node ./build/build.js --help` will print helper info and exit. */ let descIndent = ' '; let egIndent = ' '; commander .usage('[options]') .description([ 'Build echarts and generate result files in directory `echarts/dist`.', '', ' For example:', '', egIndent + 'node build/build.js --release' + '\n' + descIndent + '# Build all to `dist` folder.', egIndent + 'node build/build.js --prepublish' + '\n' + descIndent + '# Only prepublish.', egIndent + 'node build/build.js --removedev' + '\n' + descIndent + '# Remove __DEV__ code. If --min, __DEV__ always be removed.', egIndent + 'node build/build.js --type ""' + '\n' + descIndent + '# Only generate `dist/echarts.js`.', egIndent + 'node build/build.js --type common --min' + '\n' + descIndent + '# Only generate `dist/echarts.common.min.js`.', egIndent + 'node build/build.js --type simple --min --lang en' + '\n' + descIndent + '# Only generate `dist/echarts-en.simple.min.js`.', egIndent + 'node build/build.js --lang "my/lang.js" -i "my/index.js" -o "my/bundle.js"' + '\n' + descIndent + '# Take `/my/index.js` as input and generate `/my/bundle.js`,' + '\n' + descIndent + 'where `/my/lang.js` is used as language file.', ].join('\n')) .option( '-w, --watch', [ 'Watch modifications of files and auto-compile to dist file. For example,', descIndent + '`echarts/dist/echarts.js`.' ].join('\n')) .option( '--lang ', [ 'Use the specified file instead of `echarts/src/lang.js`. For example:', descIndent + '`--lang en` will use `echarts/src/langEN.js`.', descIndent + '`--lang my/langDE.js` will use `/my/langDE.js`. -o must be specified in this case.', descIndent + '`--lang /my/indexSW.js` will use `/my/indexSW.js`. -o must be specified in this case.' ].join('\n')) .option( '--release', 'Build all for release' ) .option( '--prepublish', 'Build all for release' ) .option( '--removedev', 'Remove __DEV__ code. If --min, __DEV__ always be removed.' ) .option( '--min', 'Whether to compress the output file, and remove error-log-print code.' ) .option( '--type ', [ 'Can be "simple" or "common" or "" (default). For example,', descIndent + '`--type ""` or `--type "common"`.' ].join('\n')) .option( '--sourcemap', 'Whether output sourcemap.' ) .option( '--format ', 'The format of output bundle. Can be "umd", "amd", "iife", "cjs", "es".' ) .option( '-i, --input ', 'If input file path is specified, output file path must be specified too.' ) .option( '-o, --output ', 'If output file path is specified, input file path must be specified too.' ) .parse(process.argv); let isWatch = !!commander.watch; let isRelease = !!commander.release; let isPrePublish = !!commander.prepublish; let opt = { lang: commander.lang, min: commander.min, type: commander.type || '', input: commander.input, output: commander.output, format: commander.format, sourcemap: commander.sourcemap, removeDev: commander.removedev, addBundleVersion: isWatch }; validateIO(opt.input, opt.output); validateLang(opt.lang, opt.output); normalizeParams(opt); // Clear `echarts/dist` if (isRelease) { fsExtra.removeSync(getPath('./dist')); } if (isWatch) { watch(config.createECharts(opt)); } else if (isPrePublish) { prePublish(); } else if (isRelease) { let configs = []; let configForCheck; [ {min: false}, {min: true}, {min: false, lang: 'en'}, {min: true, lang: 'en'} ].forEach(function (opt) { ['', 'simple', 'common'].forEach(function (type) { let singleOpt = Object.assign({type}, opt); normalizeParams(singleOpt); let singleConfig = config.createECharts(singleOpt); configs.push(singleConfig); if (singleOpt.min && singleOpt.type === '') { configForCheck = singleConfig; } }); }); configs.push( config.createBMap(false), config.createBMap(true), config.createDataTool(false), config.createDataTool(true) ); build(configs) .then(function () { checkCode(configForCheck); prePublish(); }).catch(handleBuildError); } else { let cfg = config.createECharts(opt); build([cfg]) .then(function () { if (opt.removeDev) { checkCode(cfg); } }) .catch(handleBuildError); } } function normalizeParams(opt) { if (opt.sourcemap == null) { opt.sourcemap = !(opt.min || opt.type); } if (opt.removeDev == null) { opt.removeDev = !!opt.min; } } function handleBuildError(err) { console.log(err); } function checkCode(singleConfig) { // Make sure __DEV__ is eliminated. let code = fs.readFileSync(singleConfig.output.file, {encoding: 'utf-8'}); if (!code) { throw new Error(`${singleConfig.output.file} is empty`); } recheckDEV(code); console.log(color('fgGreen', 'dim')('Check code: correct.')); } function validateIO(input, output) { if ((input != null && output == null) || (input == null && output != null) ) { throw new Error('`input` and `output` must be both set.'); } } function validateLang(lang, output) { if (!lang) { return; } let langInfo = ecLangPlugin.getLangFileInfo(lang); if (langInfo.isOuter && !output) { throw new Error('`-o` or `--output` must be specified if using a file path in `--lang`.'); } if (!langInfo.absolutePath || !fs.statSync(langInfo.absolutePath).isFile()) { throw new Error(`File ${langInfo.absolutePath} does not exist yet. Contribution is welcome!`); } } /** * @param {string} relativePath Based on echarts directory. * @return {string} Absolute path. */ function getPath(relativePath) { return resolve(__dirname, '../', relativePath); } run();