en/builder/build.js (289 lines of code) (raw):

/* global BUILD_CONFIG, UglifyJS */ define(function (require) { var saveAs = require('./lib/FileSaver'); var rollup = require('rollup'); var transformDev = require('transformDev'); var TOP_MODULE_NAME = 'topModuleInRequireES'; var RETRY_MAX = 2; var TIMEOUT = 10000; var RETRY_DELAY = 2000; var $log = document.getElementById('log'); var $tip = document.getElementById('tip'); var version = BUILD_CONFIG.version || 'latest'; var isVersion5 = version === 'latest' || +version[0] >= 5; var jsDelivrBase = 'https://echarts.apache.org/en/js/vendors'; var urlArgs = '__v__=' + (+new Date()); var topCode = [`export * from "echarts/src/echarts";`]; var srcFolder = version.startsWith('5.0.0') ? 'esm' // Only 5.0.0 has esm folder : isVersion5 ? 'lib' : 'src'; if (BUILD_CONFIG.api && (version.startsWith('5.0.0') || !isVersion5)) { topCode.push(`export * from "echarts/src/export";`); } // Including charts var charts = (BUILD_CONFIG.charts || '').split(','); charts.forEach(function (chart) { chart && topCode.push(`import "echarts/src/chart/${chart}";`); }); if (topCode.indexOf(`echarts/src/chart/scatter`) >= 0) { topCode.push(`import "echarts/src/chart/effectScatter"`); } // Including components var components = (BUILD_CONFIG.components || '').split(','); // PENDING: always import dataset & transform component as they have no entry for now components.push('dataset'); isVersion5 && components.push('transform'); components.forEach(function (component) { component && topCode.push(`import "echarts/src/component/${component}";`); }); if (BUILD_CONFIG.vml && !isVersion5) { topCode.push(`import "zrender/${srcFolder}/vml/vml";`); } if (BUILD_CONFIG.svg) { topCode.push(`import "zrender/${srcFolder}/svg/svg";`); } // Always require log and time axis topCode.push( `import "echarts/src/scale/Time";`, `import "echarts/src/scale/Log";` ); var versionId = !version || version === 'latest' ? '' : '@' + version; var npmEntries = {}; var pathsConfig = { 'echarts/src': `/echarts${versionId}/${srcFolder}` }; function resolveNpmDependencies(pkg, version) { var versionId = !version || version === 'latest' ? '' : '@' + version; return fetch(`${jsDelivrBase}/${pkg}${versionId}/package.json`, { mode: 'cors' }) .then(response => response.json()) .then(pkgCfg => { var entry = pkgCfg.module || pkgCfg.main || 'index.js'; if (!entry.endsWith('.js')) { entry = entry + '.js'; } npmEntries[pkg] = `/${pkg}${versionId}/${entry}`; var promises = []; for (let pkgName in pkgCfg.dependencies) { var depVersion = pkgCfg.dependencies[pkgName]; var depVersionId = !depVersion || depVersion === 'latest' ? '' : '@' + depVersion; pathsConfig[pkgName] = `/${pkgName}${depVersionId}`; promises.push(resolveNpmDependencies(pkgName, depVersion)); } return Promise.all(promises); }); } resolveNpmDependencies('echarts', version) .then(startRollup) // Loading scripts and build function startRollup() { var moduleLoadErr; var moduleLoadTimers = []; rollup.rollup({ input: TOP_MODULE_NAME, // legacy: true, plugins: [{ resolveId: function (importee, importor) { if (importee === TOP_MODULE_NAME) { return importee; } // console.log('resolveid', importee, importor); return getAbsolutePath( importee, importor !== TOP_MODULE_NAME ? importor : null ); }, transform: function (code) { return { code: code.replace(/process.env.NODE_ENV/g, JSON.stringify( !BUILD_CONFIG.source ? 'production' : 'development' )) }; }, load: function (path) { if (moduleLoadErr) { return; } if (path === TOP_MODULE_NAME) { return topCode.join('\n'); } var retryCount = 0; return new Promise(function (resolve, reject) { var err; function retryableLoad() { // When using apache CDN, might fail to loading source. if (retryCount >= RETRY_MAX) { var msg = 'Failed to load module: "' + path + '"!' + ' Please reload the page to retry.'; err.customMessage = msg; moduleLoadErr = err; moduleLoadTimers.forEach(clearTimeout); moduleLoadTimers.length = 0; return reject(err); } console.log(path) fetchModuleContent(`${jsDelivrBase}${path}`) .then(function (content) { builderLog('Loaded module: "' + path + '"'); err = null; resolve(content); }) .catch(function (e) { retryCount++; err = e; moduleLoadTimers.push(setTimeout(retryableLoad, RETRY_DELAY)); }); } retryableLoad(); }); } }] }).then(function (bundle) { builderLog('Bundling code...'); return new Promise((resolve, reject) => { setTimeout(() => { bundle.generate({ name: 'echarts', format: 'umd', // legacy: true }) .then((result) => { var code = result.output[0].code; code = transformDev.transform(code, false, !BUILD_CONFIG.source ? 'false' : 'true').code; builderLog('Bundle code: Done.', 'success'); resolve(code); }) .catch(reject); }, 0); }); }).then(function (code) { if (BUILD_CONFIG.source) { return onDone(); } builderLog('Compressing code...'); setTimeout(() => { try { code = jsCompress(code); setTimeout(() => { builderLog('Compress code: Done.', 'success'); onDone(); }, 0); } catch (e) { e.customMessage = 'Failed to compress code: ' + e.message; throw e; } }, 0); function onDone() { download(code); setTimeout(() => { builderLog('✔️ All Done!', 'success'); $tip.innerHTML = 'OK'; }, 0); } }).catch(function (err) { builderLog(err.customMessage || ('Failed to build code: ' + err.message), 'error'); $tip.innerHTML = 'Error'; console.error(err); }); } function download(code) { const contentType = 'text/javascript;charset=utf-8'; try { var blob = new Blob([code], { type: contentType }); var fileName = ['echarts']; if (!BUILD_CONFIG.source) { fileName.push('min'); } fileName.push('js'); saveAs(blob, fileName.join('.')); } catch (e) { console.error(e); window.open('data:' + contentType +',' + encodeURIComponent(code)); } } function builderLog(msg, type) { requestAnimationFrame(() => { var logLine = document.createElement('div'); logLine.innerHTML = '[' + new Date().toLocaleTimeString() + '] ' + (type ? ('<strong style="color:' + (type === 'success' ? 'green' : 'red') + '">' + msg + '</strong>') : msg); $log.appendChild(logLine); $log.scrollTop = $log.scrollHeight; }); } function jsCompress(source) { var result = UglifyJS.minify(source); if (result.error) { throw new Error(result.error) } return result.code; } // Get absolute path. `basePath` can be omitted if moduleId is absolute. function getAbsolutePath(moduleId, basePath) { if (npmEntries[moduleId]) { return npmEntries[moduleId]; } moduleId = addExt(moduleId); for (var path in pathsConfig) { if (pathsConfig.hasOwnProperty(path)) { if (moduleId.indexOf(path) === 0) { return moduleId.replace(path, pathsConfig[path]); } } } if (basePath) { moduleId = resolve(dir(basePath), moduleId); } if (moduleId.charAt(0) !== '/') { throw new Error('"' + moduleId + '" can not be found.'); } return moduleId; } function addExt(moduleId) { if (moduleId.split('/').pop().indexOf('.') < 0) { moduleId += '.js'; } return moduleId; } function fetchModuleContent(moduleUrl) { moduleUrl += '?' + urlArgs; var controller = new AbortController(); var timeout = setTimeout(() => controller.abort(), TIMEOUT); return fetch(moduleUrl, { mode: 'cors', signal: controller.signal }) .then(response => response.text()) .finally(() => clearTimeout(timeout)); } // Nodejs `path.resolve`. function resolve() { var resolvedPath = ''; var resolvedAbsolute; for (var i = arguments.length - 1; i >= 0 && !resolvedAbsolute; i--) { var path = arguments[i]; if (path) { resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path[0] === '/'; } } if (!resolvedAbsolute) { throw new Error('At least one absolute path should be input.'); } // Normalize the path resolvedPath = normalizePathArray(resolvedPath.split('/'), false).join('/'); return '/' + resolvedPath; } // resolves . and .. elements in a path array with directory names there // must be no slashes or device names (c:\) in the array // (so also no leading and trailing slashes - it does not distinguish // relative and absolute paths) function normalizePathArray(parts, allowAboveRoot) { var res = []; for (var i = 0; i < parts.length; i++) { var p = parts[i]; // ignore empty parts if (!p || p === '.') { continue; } if (p === '..') { if (res.length && res[res.length - 1] !== '..') { res.pop(); } else if (allowAboveRoot) { res.push('..'); } } else { res.push(p); } } return res; } function dir(path) { if (path) { return path.charAt(path.length - 1) === '/' ? path : resolve(path, '..'); } } });