/* * 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 path = require('path'); const fse = require('fs-extra'); const https = require('https'); const fs = require('fs'); const rollup = require('rollup'); const {nodeResolve} = require('@rollup/plugin-node-resolve'); const commonjs = require('@rollup/plugin-commonjs'); const config = require('./config'); function modifyEChartsCode(code) { return code.replace(/Math\.random/g, '__random__inner__') // https://github.com/apache/echarts/blob/737e23c0054e6b501ecc6f562920cffae953b5c6/src/core/echarts.ts#L537 // This code will cause infinite loop if we reduce the precision of Date in the visual regression test. // TODO: This is a very dirty HACK. .replace('remainTime > 0', 'false'); } module.exports.testNameFromFile = function(fileName) { return path.basename(fileName, '.html'); }; module.exports.fileNameFromTest = function (testName) { return testName + '.html'; }; function getVersionDir(source, version) { version = version || 'local'; if (source === 'PR') { // For PR preview artifacts const prNumber = version.replace(/^#/, ''); return `tmp/__version__/pr-${prNumber}`; } return `tmp/__version__/${version}`; } module.exports.getVersionDir = getVersionDir; module.exports.getActionsFullPath = function (testName) { return path.join(__dirname, 'actions', testName + '.json'); }; module.exports.getEChartsTestFileName = function () { return `echarts.test-${config.testVersion}.js`; }; // Clean PR directories at the start of initing because PR code may change module.exports.cleanPRDirectories = function () { const baseDir = path.join(__dirname, 'tmp/__version__'); if (fs.existsSync(baseDir)) { const dirs = fs.readdirSync(baseDir); dirs.forEach(dir => { if (dir.startsWith('pr-')) { fse.removeSync(path.join(baseDir, dir)); } }); } } module.exports.prepareEChartsLib = function (source, version, useCNMirror) { console.log(`Preparing ECharts lib: ${source} ${version}`); const versionFolder = path.join(__dirname, getVersionDir(source, version)); const ecDownloadPath = `${versionFolder}/echarts.js`; const testLibPath = `${versionFolder}/${module.exports.getEChartsTestFileName()}`; fse.ensureDirSync(versionFolder); if (!version || version === 'local') { // Developing version, make sure it's new build fse.copySync(path.join(__dirname, '../../dist/echarts.js'), ecDownloadPath); let code = modifyEChartsCode(fs.readFileSync(ecDownloadPath, 'utf-8')); fs.writeFileSync(testLibPath, code, 'utf-8'); return Promise.resolve(); } else if (fs.existsSync(ecDownloadPath) && fs.existsSync(testLibPath) && fs.statSync(ecDownloadPath).size > 0 && fs.statSync(testLibPath).size > 0) { return Promise.resolve(); } return new Promise((resolve, reject) => { let url; if (source === 'PR') { const prNumber = version.replace(/^#/, ''); if (!/^\d+$/.test(prNumber)) { reject('Invalid PR number format. Should be #123'); return; } url = `https://echarts-pr-${prNumber}.surge.sh/dist/echarts.js`; } else { const isNightly = source === 'nightly'; const packageName = isNightly ? 'echarts-nightly' : 'echarts'; url = useCNMirror ? `https://registry.npmmirror.com/${packageName}/${version}/files/dist/echarts.js` : `https://unpkg.com/${packageName}@${version}/dist/echarts.js`; } console.log(`Downloading ECharts from ${url}`); https.get(url, response => { if (response.statusCode === 404) { reject(`PR artifact doesn't exist at ${url}. Make sure the PR build is complete.`); return; } let data = ''; response.on('data', chunk => { data += chunk; }); response.on('end', () => { if (!data) { reject(`Downloaded file is empty from ${url}`); return; } fs.writeFileSync(ecDownloadPath, data, 'utf-8'); const code = modifyEChartsCode(data); fs.writeFileSync(testLibPath, code, 'utf-8'); resolve(); }); }).on('error', (e) => { reject(`Failed to download from ${url}: ${e}`); }); }); }; module.exports.fetchVersions = function (isNightly, useCNMirror) { return new Promise((resolve, reject) => { const npmRegistry = useCNMirror ? 'https://registry.npmmirror.com' : 'https://registry.npmjs.org'; const endpoint = `${npmRegistry}/echarts${isNightly ? '-nightly' : ''}`; https.get(endpoint, res => { if (res.statusCode !== 200) { res.destroy(); reject('status code: ' + res.statusCode); return; } var buffers = []; res.on('data', buffers.push.bind(buffers)); res.on('end', function () { try { var data = Buffer.concat(buffers); resolve(Object.keys(JSON.parse(data).versions).reverse()); } catch (e) { reject(e); } }); }) .on('error', reject); }); }; module.exports.buildRuntimeCode = async function () { const bundle = await rollup.rollup({ input: path.join(__dirname, 'runtime/main.js'), plugins: [ { // https://rollupjs.org/guide/en/#a-simple-example resolveId(source, importer) { return source === 'crypto' ? source : null; }, load(id) { // seedrandom use crypto as external module return id === 'crypto' ? 'export default null;' : null; } }, nodeResolve(), commonjs() ] }); const { output } = await bundle.generate({ format: 'iife', name: 'autorun' }); return output[0].code; }; module.exports.waitTime = function (time) { return new Promise(resolve => { setTimeout(() => { resolve(); }, time); }); };