123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /*
- * 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.
- */
- /**
- * [Create CommonJS files]:
- * Compatible with prevoius folder structure: `echarts/lib` exists in `node_modules`
- * (1) Build all files to CommonJS to `echarts/lib`.
- * (2) Remove __DEV__.
- * (3) Mount `echarts/src/export.js` to `echarts/lib/echarts.js`.
- *
- * [Create ESModule files]:
- * Build all files to CommonJS to `echarts/esm`.
- */
- const nodePath = require('path');
- const assert = require('assert');
- const fs = require('fs');
- const fsExtra = require('fs-extra');
- const chalk = require('chalk');
- const ts = require('typescript');
- const globby = require('globby');
- const transformDEVUtil = require('./transform-dev');
- const preamble = require('./preamble');
- const dts = require('@lang/rollup-plugin-dts').default;
- const rollup = require('rollup');
- const { transformImport } = require('zrender/build/transformImport');
- const ecDir = nodePath.resolve(__dirname, '..');
- const tmpDir = nodePath.resolve(ecDir, 'pre-publish-tmp');
- const tsConfig = readTSConfig();
- const autoGeneratedFileAlert = `
- /**
- * AUTO-GENERATED FILE. DO NOT MODIFY.
- */
- `;
- const mainSrcGlobby = {
- patterns: [
- 'src/**/*.ts'
- ],
- cwd: ecDir
- };
- const extensionSrcGlobby = {
- patterns: [
- 'extension-src/**/*.ts'
- ],
- cwd: ecDir
- };
- const extensionSrcDir = nodePath.resolve(ecDir, 'extension-src');
- const extensionESMDir = nodePath.resolve(ecDir, 'extension');
- const typesDir = nodePath.resolve(ecDir, 'types');
- const esmDir = 'lib';
- const compileWorkList = [
- {
- logLabel: 'main ts -> js-esm',
- compilerOptionsOverride: {
- module: 'ES2015',
- rootDir: ecDir,
- outDir: tmpDir,
- // Generate types when buidling esm
- declaration: true,
- declarationDir: typesDir
- },
- srcGlobby: mainSrcGlobby,
- transformOptions: {
- filesGlobby: {patterns: ['**/*.js'], cwd: tmpDir},
- preamble: preamble.js,
- transformDEV: true
- },
- before: async function () {
- fsExtra.removeSync(tmpDir);
- fsExtra.removeSync(nodePath.resolve(ecDir, 'types'));
- fsExtra.removeSync(nodePath.resolve(ecDir, esmDir));
- fsExtra.removeSync(nodePath.resolve(ecDir, 'index.js'));
- fsExtra.removeSync(nodePath.resolve(ecDir, 'index.blank.js'));
- fsExtra.removeSync(nodePath.resolve(ecDir, 'index.common.js'));
- fsExtra.removeSync(nodePath.resolve(ecDir, 'index.simple.js'));
- },
- after: async function () {
- fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.all.js'), nodePath.resolve(ecDir, 'index.js'));
- fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.blank.js'), nodePath.resolve(ecDir, 'index.blank.js'));
- fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.common.js'), nodePath.resolve(ecDir, 'index.common.js'));
- fs.renameSync(nodePath.resolve(tmpDir, 'src/echarts.simple.js'), nodePath.resolve(ecDir, 'index.simple.js'));
- fs.renameSync(nodePath.resolve(tmpDir, 'src'), nodePath.resolve(ecDir, esmDir));
- transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.js'), esmDir);
- transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.blank.js'), esmDir);
- transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.common.js'), esmDir);
- transformRootFolderInEntry(nodePath.resolve(ecDir, 'index.simple.js'), esmDir);
- await transformLibFiles(nodePath.resolve(ecDir, esmDir), esmDir);
- await transformLibFiles(nodePath.resolve(ecDir, 'types'), esmDir);
- fsExtra.removeSync(tmpDir);
- }
- },
- {
- logLabel: 'extension ts -> js-esm',
- compilerOptionsOverride: {
- module: 'ES2015',
- declaration: false,
- rootDir: extensionSrcDir,
- outDir: extensionESMDir
- },
- srcGlobby: extensionSrcGlobby,
- transformOptions: {
- filesGlobby: {patterns: ['**/*.js'], cwd: extensionESMDir},
- preamble: preamble.js,
- transformDEV: true
- },
- before: async function () {
- fsExtra.removeSync(extensionESMDir);
- },
- after: async function () {
- await transformLibFiles(extensionESMDir, 'lib');
- }
- }
- ];
- /**
- * @public
- */
- module.exports = async function () {
- for (let {
- logLabel, compilerOptionsOverride, srcGlobby,
- transformOptions, before, after
- } of compileWorkList) {
- process.stdout.write(chalk.green.dim(`[${logLabel}]: compiling ...`));
- before && await before();
- let srcPathList = await readFilePaths(srcGlobby);
- await tsCompile(compilerOptionsOverride, srcPathList);
- process.stdout.write(chalk.green.dim(` done \n`));
- process.stdout.write(chalk.green.dim(`[${logLabel}]: transforming ...`));
- await transformCode(transformOptions);
- after && await after();
- process.stdout.write(chalk.green.dim(` done \n`));
- }
- process.stdout.write(chalk.green.dim(`Generating entries ...`));
- generateEntries();
- process.stdout.write(chalk.green.dim(`Bundling DTS ...`));
- await bundleDTS();
- console.log(chalk.green.dim('All done.'));
- };
- async function runTsCompile(localTs, compilerOptions, srcPathList) {
- // Must do it. becuase the value in tsconfig.json might be different from the inner representation.
- // For example: moduleResolution: "NODE" => moduleResolution: 2
- const {options, errors} = localTs.convertCompilerOptionsFromJson(compilerOptions, ecDir);
- if (errors.length) {
- let errMsg = 'tsconfig parse failed: '
- + errors.map(error => error.messageText).join('. ')
- + '\n compilerOptions: \n' + JSON.stringify(compilerOptions, null, 4);
- assert(false, errMsg);
- }
- // See: https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API
- let program = localTs.createProgram(srcPathList, options);
- let emitResult = program.emit();
- let allDiagnostics = localTs
- .getPreEmitDiagnostics(program)
- .concat(emitResult.diagnostics);
- allDiagnostics.forEach(diagnostic => {
- if (diagnostic.file) {
- let {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
- let message = localTs.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
- console.log(chalk.red(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`));
- }
- else {
- console.log(chalk.red(localTs.flattenDiagnosticMessageText(diagnostic.messageText, '\n')));
- }
- });
- if (allDiagnostics.length > 0) {
- throw new Error('TypeScript Compile Failed')
- }
- }
- module.exports.runTsCompile = runTsCompile;
- async function tsCompile(compilerOptionsOverride, srcPathList) {
- assert(
- compilerOptionsOverride
- && compilerOptionsOverride.module
- && compilerOptionsOverride.rootDir
- && compilerOptionsOverride.outDir
- );
- let compilerOptions = {
- ...tsConfig.compilerOptions,
- ...compilerOptionsOverride,
- sourceMap: false
- };
- runTsCompile(ts, compilerOptions, srcPathList);
- }
- /**
- * Transform import/require path in the entry file to `esm` or `lib`.
- */
- function transformRootFolderInEntry(entryFile, replacement) {
- let code = fs.readFileSync(entryFile, 'utf-8');
- // Simple regex replacement
- // TODO More robust way?
- assert(
- !/(import\s+|from\s+|require\(\s*)["']\.\/echarts\./.test(code)
- && !/(import\s+|from\s+|require\(\s*)["']echarts\./.test(code),
- 'Import echarts.xxx.ts is not supported.'
- );
- code = code.replace(/((import\s+|from\s+|require\(\s*)["'])\.\//g, `$1./${replacement}/`);
- fs.writeFileSync(
- entryFile,
- // Also transform zrender.
- singleTransformImport(code, replacement),
- 'utf-8'
- );
- }
- /**
- * Transform `zrender/src` to `zrender/lib` in all files
- */
- async function transformLibFiles(rooltFolder, replacement) {
- const files = await readFilePaths({
- patterns: ['**/*.js', '**/*.d.ts'],
- cwd: rooltFolder
- });
- // Simple regex replacement
- // TODO More robust way?
- for (let fileName of files) {
- let code = fs.readFileSync(fileName, 'utf-8');
- code = singleTransformImport(code, replacement);
- // For lower ts version, not use import type
- // TODO Use https://github.com/sandersn/downlevel-dts ?
- // if (fileName.endsWith('.d.ts')) {
- // code = singleTransformImportType(code);
- // }
- fs.writeFileSync(fileName, code, 'utf-8');
- }
- }
- /**
- * 1. Transform zrender/src to zrender/lib
- * 2. Add .js extensions
- */
- function singleTransformImport(code, replacement) {
- return transformImport(
- code.replace(/([\"\'])zrender\/src\//g, `$1zrender/${replacement}/`),
- (moduleName) => {
- // Ignore 'tslib' and 'echarts' in the extensions.
- if (moduleName === 'tslib' || moduleName === 'echarts') {
- return moduleName;
- }
- else if (moduleName === 'zrender/lib/export') {
- throw new Error('Should not import the whole zrender library.');
- }
- else if (moduleName.endsWith('.ts')) {
- // Replace ts with js
- return moduleName.replace(/\.ts$/, '.js');
- }
- else if (moduleName.endsWith('.js')) {
- return moduleName;
- }
- else {
- return moduleName + '.js'
- }
- }
- );
- }
- // function singleTransformImportType(code) {
- // return code.replace(/import\s+type\s+/g, 'import ');
- // }
- /**
- * @param {Object} transformOptions
- * @param {Object} transformOptions.filesGlobby {patterns: string[], cwd: string}
- * @param {string} [transformOptions.preamble] See './preamble.js'
- * @param {boolean} [transformOptions.transformDEV]
- */
- async function transformCode({filesGlobby, preamble, transformDEV}) {
- let filePaths = await readFilePaths(filesGlobby);
- filePaths.map(filePath => {
- let code = fs.readFileSync(filePath, 'utf8');
- if (transformDEV) {
- let result = transformDEVUtil.transform(code, false);
- code = result.code;
- }
- code = autoGeneratedFileAlert + code;
- if (preamble) {
- code = preamble + code;
- }
- fs.writeFileSync(filePath, code, 'utf8');
- });
- }
- async function readFilePaths({patterns, cwd}) {
- assert(patterns && cwd);
- return (
- await globby(patterns, {cwd})
- ).map(
- srcPath => nodePath.resolve(cwd, srcPath)
- );
- }
- async function bundleDTS() {
- const outDir = nodePath.resolve(__dirname, '../types/dist');
- const commonConfig = {
- onwarn(warning, rollupWarn) {
- // Not warn circular dependency
- if (warning.code !== 'CIRCULAR_DEPENDENCY') {
- rollupWarn(warning);
- }
- },
- plugins: [
- dts({
- respectExternal: true
- })
- // {
- // generateBundle(options, bundle) {
- // for (let chunk of Object.values(bundle)) {
- // chunk.code = `
- // type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
- // ${chunk.code}`
- // }
- // }
- // }
- ]
- };
- // Bundle chunks.
- const parts = [
- 'core', 'charts', 'components', 'renderers', 'option', 'features'
- ];
- const inputs = {};
- parts.forEach(partName => {
- inputs[partName] = nodePath.resolve(__dirname, `../types/src/export/${partName}.d.ts`)
- });
- const bundle = await rollup.rollup({
- input: inputs,
- ...commonConfig
- });
- let idx = 1;
- await bundle.write({
- dir: outDir,
- minifyInternalExports: false,
- manualChunks: (id) => {
- // Only create one chunk.
- return 'shared';
- },
- chunkFileNames: 'shared.d.ts'
- });
- // Bundle all in one
- const bundleAllInOne = await rollup.rollup({
- input: nodePath.resolve(__dirname, `../types/src/export/all.d.ts`),
- ...commonConfig
- });
- await bundleAllInOne.write({
- file: nodePath.resolve(outDir, 'echarts.d.ts')
- });
- }
- function readTSConfig() {
- // tsconfig.json may have comment string, which is invalid if
- // using `require('tsconfig.json'). So we use a loose parser.
- let filePath = nodePath.resolve(ecDir, 'tsconfig.json');
- const tsConfigText = fs.readFileSync(filePath, {encoding: 'utf8'});
- return (new Function(`return ( ${tsConfigText} )`))();
- }
- function generateEntries() {
- ['charts', 'components', 'renderers', 'core', 'features'].forEach(entryName => {
- if (entryName !== 'option') {
- const jsCode = fs.readFileSync(nodePath.join(__dirname, `template/${entryName}.js`), 'utf-8');
- fs.writeFileSync(nodePath.join(__dirname, `../${entryName}.js`), jsCode, 'utf-8');
- }
- const dtsCode = fs.readFileSync(nodePath.join(__dirname, `/template/${entryName}.d.ts`), 'utf-8');
- fs.writeFileSync(nodePath.join(__dirname, `../${entryName}.d.ts`), dtsCode, 'utf-8');
- });
- }
- module.exports.readTSConfig = readTSConfig;
|