This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch feat-local-config
in repository https://gitbox.apache.org/repos/asf/echarts-doc.git

commit 32998345699f8d59551794b700b829a7b71c0a28
Author: 100pah <sushuang0...@gmail.com>
AuthorDate: Wed Jul 30 21:26:54 2025 +0800

    feat: support local config, not tracked by git.
---
 .gitignore                     |   2 +
 README.md                      |  12 +-
 build.js                       | 343 +----------------------------------------
 build.js => build/build-doc.js |  23 +--
 build/helper.js                |  29 ++++
 build/webpack.config.js        | 118 ++++++++------
 config/env.dev.js              |   4 +-
 package.json                   |   6 +-
 src/components/LiveExample.vue |   4 +-
 9 files changed, 128 insertions(+), 413 deletions(-)

diff --git a/.gitignore b/.gitignore
index e3f8c759..1213b7f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -178,6 +178,8 @@ slides/webgl
 /public/css
 /public/zh/documents
 /public/en/documents
+/public/zh/coding-standard-content.html
+/public/en/coding-standard-content.html
 /public/*.html
 
 
diff --git a/README.md b/README.md
index 0aa1c614..165cdca7 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,17 @@ It will:
 
 ### Local Config
 
-To customize the links of `echarts-examples` and other configurations, you can 
create a local config file `echarts-doc/config/env.dev-local.js`, which is not 
tracked by git. The content can be copied from `echarts-doc/config/env.dev.js`, 
and then modify it as needed. `npm run dev` will use this local config file, if 
it exists, to replace `echarts-doc/config/env.dev.js`.
+To customize the links of `echarts-examples` and other configurations, you can 
create a local config file `echarts-doc/config/env.dev-local.js`, which is not 
tracked by git, and its top-level properties will be used to override the 
corresponding properties of `echarts-doc/config/env.dev.js` when `npm run dev`.
+
+For example, create a `echarts-doc/config/env.dev-local.js`:
+```js
+module.exports = {
+    // These props will override the same props in 
`echarts-doc/config/env.dev-local.js`
+    galleryViewPath: 'http://127.0.0.1:3002/en/view.html?local=1&c=',
+    galleryEditorPath: 'http://127.0.0.1:3002/en/editor.html?local=1&c=',
+    EMBEDDED_ECHARTS_SCRIPT_URL: 
'http://localhost:8001/echarts/echarts/dist/echarts.js',
+};
+```
 
 
 ## Tips About Writing Doc
diff --git a/build.js b/build.js
index 22e6e729..6430894c 100644
--- a/build.js
+++ b/build.js
@@ -1,344 +1,5 @@
 /**
- * ------------------------------------------------------------------------
- * Usage:
- *
- * ```shell
- * node build.js --env asf # build all for asf
- * node build.js --env echartsjs # build all for echartsjs.
- * node build.js --env localsite # build all for localsite.
- * node build.js --env dev # the same as "debug", dev the content of docs.
- * # Check `./config` to see the available env
- * ```
- * ------------------------------------------------------------------------
+ * @file For compatibiliy in case that some CI call this file. May not 
necessary.
  */
 
-const md2json = require('./tool/md2json');
-const {extractDesc} = require('./tool/schemaHelper');
-const fs = require('fs');
-const fse = require('fs-extra');
-const marked = require('marked');
-const copydir = require('copy-dir');
-const chalk = require('chalk');
-// const MarkDownTOCRenderer = require('./tool/MarkDownTOCRenderer');
-const argv = require('yargs').argv;
-const path = require('path');
-const assert = require('assert');
-const chokidar = require('chokidar');
-const {debounce} = require('lodash');
-const {getDocJSONPVarNname} = require('./src/shared');
-
-const projectDir = __dirname;
-
-function initEnv() {
-    let envType = argv.env;
-    let isDev = argv.dev != null || argv.debug != null || argv.env === 'dev';
-
-    if (isDev) {
-        console.warn('=============================');
-        console.warn('!!! THIS IS IN DEV MODE !!!');
-        console.warn('=============================');
-        envType = 'dev';
-    }
-
-    if (!envType) {
-        throw new Error('--env MUST be specified');
-    }
-
-    let config = require('./config/env.' + envType);
-
-    const localOverridePath = './config/env.' + envType + '-local.js';
-    if (fs.existsSync(path.join(__dirname, localOverridePath))) {
-        config = require(localOverridePath);
-    }
-
-    assert(path.isAbsolute(config.releaseDestDir) && 
path.isAbsolute(config.ecWWWGeneratedDir));
-
-    config.envType = envType;
-
-    return config;
-}
-
-const config = initEnv();
-
-const languages = ['zh', 'en'];
-
-config.gl = config.gl || {};
-for (let key in config) {
-    if (key !== 'gl' && !config.gl.hasOwnProperty(key)) {
-        config.gl[key] = config[key];
-    }
-}
-
-async function md2jsonAsync(opt) {
-
-    var newOpt = Object.assign({
-        path: path.join(opt.language, opt.entry, '**/*.md'),
-        tplEnv: Object.assign({}, config, {
-            galleryViewPath: config.galleryViewPath.replace('${lang}', 
opt.language),
-            galleryEditorPath: config.galleryEditorPath.replace('${lang}', 
opt.language),
-            handbookPath: config.handbookPath.replace('${lang}', opt.language)
-        }),
-        imageRoot: config.imagePath
-    }, opt);
-
-    function run(cb) {
-        md2json(newOpt).then(schema => {
-            writeSingleSchema(schema, opt.language, opt.entry, false);
-            writeSingleSchemaPartioned(schema, opt.language, opt.entry, false);
-            console.log(chalk.green('generated: ' + opt.language + '/' + 
opt.entry));
-            cb && cb();
-        }).catch(e => {
-            console.log(e);
-        });
-    }
-
-    var runDebounced = debounce(run, 500, {
-        leading: false,
-        trailing: true
-    });
-    return await new Promise((resolve, reject) => {
-        run(resolve);
-
-        if (argv.watch) {
-            chokidar.watch(path.resolve(__dirname, opt.language, opt.entry), {
-                ignoreInitial: true
-            }).on('all', (event, path) => {
-                console.log(path, event);
-                runDebounced();
-            });
-        }
-    });
-}
-
-function copyAsset() {
-
-    const assetSrcDir = path.resolve(projectDir, 'asset');
-
-    function doCopy() {
-        for (let lang of languages) {
-            const assetDestDir = path.resolve(config.releaseDestDir, 
`${lang}/documents/asset`);
-            copydir.sync(assetSrcDir, assetDestDir);
-        }
-    }
-    var doCopyDebounced = debounce(doCopy, 500, {
-        leading: false,
-        trailing: true
-    });
-
-    doCopy();
-
-    if (argv.watch) {
-        chokidar.watch(assetSrcDir, {
-            ignoreInitial: true
-        }).on('all', (event, path) => {
-            console.log(path, event);
-            doCopyDebounced();
-        });
-    }
-    console.log('Copy asset done.');
-}
-
-async function run() {
-
-    for (let language of languages) {
-        await md2jsonAsync({
-            sectionsAnyOf: ['visualMap', 'dataZoom', 'series', 
'graphic.elements', 'dataset.transform'],
-            entry: 'option',
-            language
-        });
-
-        await md2jsonAsync({
-            entry: 'tutorial',
-            maxDepth: 1,
-            language
-        });
-
-        await md2jsonAsync({
-            entry: 'api',
-            language
-        });
-
-        await md2jsonAsync({
-            sectionsAnyOf: ['series'],
-            entry: 'option-gl',
-            // Overwrite
-            tplEnv: config.gl,
-            imageRoot: config.gl.imagePath,
-            language
-        });
-    }
-
-    console.log('Build doc done.');
-
-    copyAsset();
-
-    if (!argv.watch) {  // Not in watch dev mode
-        try {
-            // TODO Do we need to debug changelog in the doc folder?
-            buildChangelog();
-            buildCodeStandard();
-
-            copySite();
-        }
-        catch (e) {
-            console.log('Error happens when copying to dest folders.');
-            console.log(e);
-        }
-    }
-
-    console.log('All done.');
-}
-
-function buildChangelog() {
-    for (let lang of languages) {
-        const srcPath = path.resolve(projectDir, `${lang}/changelog.md`);
-        const destPath = path.resolve(config.ecWWWGeneratedDir, 
`${lang}/documents/changelog-content.html`);
-        fse.outputFileSync(
-            destPath,
-            marked(fs.readFileSync(srcPath, 'utf-8')),
-            'utf-8'
-        );
-        console.log(chalk.green('generated: ' + destPath));
-    }
-    console.log('Build changelog done.');
-}
-
-function buildCodeStandard() {
-    // support for Chinese character
-    const customRenderer = new marked.Renderer();
-    customRenderer.heading = function(text, level, raw) {
-        const id = raw.toLowerCase().replace(/[^\w\u4e00-\u9fa5]+/g, '-');
-        return `<h${level} id="${id}">${text}</h${level}>\n`;
-    };
-
-    for (let lang of languages) {
-        const codeStandardDestPath = path.resolve(config.ecWWWGeneratedDir, 
`${lang}/coding-standard-content.html`);
-        fse.ensureDirSync(path.dirname(codeStandardDestPath));
-        fse.outputFileSync(
-            codeStandardDestPath,
-            marked(fs.readFileSync(`${lang}/coding-standard.md`, 'utf-8'), { 
renderer: customRenderer }),
-            'utf-8'
-        );
-        console.log(chalk.green('generated: ' + codeStandardDestPath));
-    }
-
-    console.log('Build code standard done.');
-}
-
-function copySite() {
-    const jsSrcPath = path.resolve(projectDir, 'public/js/doc-bundle.js');
-    const cssSrcDir = path.resolve(projectDir, 'public/css');
-
-    // Copy js and css of doc site.
-    for (let lang of languages) {
-        const jsDestPath = path.resolve(config.releaseDestDir, 
`${lang}/js/doc-bundle.js`);
-        fse.copySync(jsSrcPath, jsDestPath);
-        console.log(chalk.green(`js copied to: ${jsDestPath}`));
-
-        const cssDestDir = path.resolve(config.releaseDestDir, `${lang}/css`);
-        fse.copySync(cssSrcDir, cssDestDir);
-        console.log(chalk.green(`css copied to: ${cssDestDir}`));
-    }
-
-    console.log('Copy site done.');
-}
-
-function writeSingleSchema(schema, language, docName, format) {
-    const destPath = path.resolve(config.releaseDestDir, 
`${language}/documents/${docName}.json`);
-    fse.ensureDirSync(path.dirname(destPath));
-    fse.outputFileSync(
-        destPath,
-        format ? JSON.stringify(schema, null, 2) : JSON.stringify(schema),
-        'utf-8'
-    );
-    // console.log(chalk.green('generated: ' + destPath));
-}
-
-function writeSingleSchemaPartioned(schema, language, docName, format) {
-    const {outline, descriptions} = extractDesc(schema, docName);
-
-    function convertToJS(basename, filePath) {
-        const content = fs.readFileSync(filePath, 'utf-8');
-        const varName = getDocJSONPVarNname(basename);
-        const code = `window.${varName} = ${content}`;
-        fs.writeFileSync(filePath.replace(/\.json$/, '.js'), code, 'utf-8');
-    }
-
-    const outlineBasename = `${docName}-outline.json`;
-    const outlineDestPath = path.resolve(config.releaseDestDir, 
`${language}/documents/${docName}-parts/${outlineBasename}`);
-    fse.ensureDirSync(path.dirname(outlineDestPath));
-    fse.outputFileSync(
-        outlineDestPath,
-        format ? JSON.stringify(outline, null, 2) : JSON.stringify(outline),
-        'utf-8'
-    );
-    convertToJS(outlineBasename, outlineDestPath);
-
-    function copyUIControlConfigs(source, target) {
-        for (let key in source) {
-            if (target[key]) {
-                if (source[key].uiControl && !target[key].uiControl) {
-                    target[key].uiControl = source[key].uiControl;
-                }
-                if (source[key].exampleBaseOptions && 
!target[key].exampleBaseOptions) {
-                    target[key].exampleBaseOptions = 
source[key].exampleBaseOptions;
-                }
-            }
-            else {
-                // console.error(`Unmatched option path ${key}`);
-            }
-        }
-    }
-
-    function readOptionDesc(language, partKey) {
-        const descDestPath = path.resolve(config.releaseDestDir, 
`${language}/documents/${docName}-parts/${partKey}.json`);
-        try {
-            const text = fs.readFileSync(descDestPath, 'utf-8');
-            return JSON.parse(text);
-        }
-        catch(e) {
-            return;
-        }
-    }
-
-    function writeOptionDesc(language, partKey, json) {
-        const descBasename = `${partKey}.json`;
-        const descDestPath = path.resolve(config.releaseDestDir, 
`${language}/documents/${docName}-parts/${descBasename}`);
-        fse.ensureDirSync(path.dirname(descDestPath));
-        fse.outputFileSync(
-            descDestPath,
-            format ? JSON.stringify(json, null, 2) : JSON.stringify(json),
-            'utf-8'
-        );
-        convertToJS(descBasename, descDestPath);
-    }
-
-    for (let partKey in descriptions) {
-        let partDescriptions = descriptions[partKey];
-
-        // Copy ui control config from zh to english.
-        if (language === 'zh') {
-            languages.forEach(function (otherLang) {
-                if (otherLang === 'zh') {
-                    return;
-                }
-                const json = readOptionDesc(otherLang, partKey);
-                if (json) {
-                    copyUIControlConfigs(partDescriptions, json);
-                    writeOptionDesc(otherLang, partKey, json);
-                }
-            });
-        }
-        else {
-            const json = readOptionDesc('zh', partKey);
-            if (json) {
-                copyUIControlConfigs(json, partDescriptions);
-            }
-        }
-
-        writeOptionDesc(language, partKey, partDescriptions);
-        // console.log(chalk.green('generated: ' + descDestPath));
-    }
-};
-
-run();
+require('./build/build-doc.js');
diff --git a/build.js b/build/build-doc.js
similarity index 93%
copy from build.js
copy to build/build-doc.js
index 22e6e729..ac0d8e70 100644
--- a/build.js
+++ b/build/build-doc.js
@@ -12,8 +12,8 @@
  * ------------------------------------------------------------------------
  */
 
-const md2json = require('./tool/md2json');
-const {extractDesc} = require('./tool/schemaHelper');
+const md2json = require('../tool/md2json');
+const {extractDesc} = require('../tool/schemaHelper');
 const fs = require('fs');
 const fse = require('fs-extra');
 const marked = require('marked');
@@ -22,12 +22,12 @@ const chalk = require('chalk');
 // const MarkDownTOCRenderer = require('./tool/MarkDownTOCRenderer');
 const argv = require('yargs').argv;
 const path = require('path');
-const assert = require('assert');
 const chokidar = require('chokidar');
 const {debounce} = require('lodash');
-const {getDocJSONPVarNname} = require('./src/shared');
+const {getDocJSONPVarNname} = require('../src/shared');
+const {readConfigEnvFile} = require('./helper');
 
-const projectDir = __dirname;
+const projectDir = path.resolve(__dirname, '..');
 
 function initEnv() {
     let envType = argv.env;
@@ -44,14 +44,7 @@ function initEnv() {
         throw new Error('--env MUST be specified');
     }
 
-    let config = require('./config/env.' + envType);
-
-    const localOverridePath = './config/env.' + envType + '-local.js';
-    if (fs.existsSync(path.join(__dirname, localOverridePath))) {
-        config = require(localOverridePath);
-    }
-
-    assert(path.isAbsolute(config.releaseDestDir) && 
path.isAbsolute(config.ecWWWGeneratedDir));
+    const config = readConfigEnvFile(envType);
 
     config.envType = envType;
 
@@ -100,7 +93,7 @@ async function md2jsonAsync(opt) {
         run(resolve);
 
         if (argv.watch) {
-            chokidar.watch(path.resolve(__dirname, opt.language, opt.entry), {
+            chokidar.watch(path.resolve(projectDir, opt.language, opt.entry), {
                 ignoreInitial: true
             }).on('all', (event, path) => {
                 console.log(path, event);
@@ -216,7 +209,7 @@ function buildCodeStandard() {
         fse.ensureDirSync(path.dirname(codeStandardDestPath));
         fse.outputFileSync(
             codeStandardDestPath,
-            marked(fs.readFileSync(`${lang}/coding-standard.md`, 'utf-8'), { 
renderer: customRenderer }),
+            marked(fs.readFileSync(path.resolve(projectDir, 
`${lang}/coding-standard.md`), 'utf-8'), { renderer: customRenderer }),
             'utf-8'
         );
         console.log(chalk.green('generated: ' + codeStandardDestPath));
diff --git a/build/helper.js b/build/helper.js
new file mode 100644
index 00000000..6116dd5f
--- /dev/null
+++ b/build/helper.js
@@ -0,0 +1,29 @@
+const fs = require('fs');
+const path = require('path');
+const assert = require('assert');
+
+const ENV_TYPE = ['dev', 'localsite', 'asf'];
+
+/**
+ * @param envType {string} 'dev' | 'localsite' | 'asf'
+ */
+exports.readConfigEnvFile = function (envType) {
+    assert(ENV_TYPE.indexOf(envType) >= 0, 'envType must be one of ' + 
ENV_TYPE.join(', '));
+
+    const configEnvRelativePath = '../config/env.' + envType + '.js';
+    const configEnvLocalRelativePath = '../config/env.' + envType + 
'-local.js';
+
+    const config = require(configEnvRelativePath);
+    if (fs.existsSync(path.resolve(__dirname, configEnvLocalRelativePath))) {
+        const configLocal = require(configEnvLocalRelativePath);
+        assert(
+            typeof configLocal === 'object' && !Array.isArray(configLocal),
+            configEnvLocalRelativePath + ' must be an object.'
+        );
+        Object.assign(config, configLocal);
+    }
+
+    assert(path.isAbsolute(config.releaseDestDir) && 
path.isAbsolute(config.ecWWWGeneratedDir));
+
+    return config;
+};
diff --git a/build/webpack.config.js b/build/webpack.config.js
index 09cbc1ff..e76ee15a 100644
--- a/build/webpack.config.js
+++ b/build/webpack.config.js
@@ -2,57 +2,73 @@ const webpack = require('webpack');
 const VueLoaderPlugin = require('vue-loader/lib/plugin');
 const path = require('path');
 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const {readConfigEnvFile} = require('./helper');
 
-/**
- * @type {import('webpack').Configuration}
- */
-module.exports = {
-    entry: path.resolve(__dirname, '../src/main.js'),
-    output: {
-        filename: 'doc-bundle.js',
-        path: path.resolve(__dirname, '../public/js'),
-        library: 'echartsDoc',
-        libraryTarget: 'umd'
-    },
-    stats: 'minimal',
-    module: {
-        rules: [{
-            test: /\.vue$/,
-            use: ['vue-loader']
-        }, {
-            test: /\.js$/,
-            use: ['babel-loader'],
-            exclude: /node_modules/
-        }, {
-            test: /\.css$/,
-            use: [MiniCssExtractPlugin.loader, 'css-loader']
-        }, {
-            test: /\.scss$/,
-            use: [MiniCssExtractPlugin.loader, 'css-loader', 'sassjs-loader']
-        }, {
-            test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
-            use: [{
-                loader: 'file-loader',
-                options: {
-                    limit: 10000,
-                    outputPath: '../css',
-                    name: '[name].[ext]'
-                }
+
+module.exports = (env, argv) => {
+
+    const isDev = argv.mode === 'development';
+
+    let configEnv = {};
+    if (isDev) {
+        configEnv = readConfigEnvFile('dev');
+    }
+
+    return {
+        entry: path.resolve(__dirname, '../src/main.js'),
+        output: {
+            filename: 'doc-bundle.js',
+            path: path.resolve(__dirname, '../public/js'),
+            library: 'echartsDoc',
+            libraryTarget: 'umd'
+        },
+        stats: 'minimal',
+        module: {
+            rules: [{
+                test: /\.vue$/,
+                use: ['vue-loader']
+            }, {
+                test: /\.js$/,
+                use: ['babel-loader'],
+                exclude: /node_modules/
+            }, {
+                test: /\.css$/,
+                use: [MiniCssExtractPlugin.loader, 'css-loader']
+            }, {
+                test: /\.scss$/,
+                use: [MiniCssExtractPlugin.loader, 'css-loader', 
'sassjs-loader']
+            }, {
+                test: 
/\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/,
+                use: [{
+                    loader: 'file-loader',
+                    options: {
+                        limit: 10000,
+                        outputPath: '../css',
+                        name: '[name].[ext]'
+                    }
+                }]
             }]
-        }]
-    },
-    externals: {
-        vue: 'Vue',
-        codemirror: 'CodeMirror',
-        'js-beautify': 'beautifier'
-    },
-    plugins: [
-        new webpack.IgnorePlugin({
-            resourceRegExp: /^fs$/
-        }),
-        new VueLoaderPlugin(),
-        new MiniCssExtractPlugin({
-            filename: '../css/doc-bundle.css'
-        })
-    ]
+        },
+        externals: {
+            vue: 'Vue',
+            codemirror: 'CodeMirror',
+            'js-beautify': 'beautifier'
+        },
+        plugins: [
+            new webpack.DefinePlugin({
+                // It can be used in the code directly.
+                INJECTED_CONFIG: JSON.stringify({
+                    EMBEDDED_ECHARTS_SCRIPT_URL: 
configEnv.EMBEDDED_ECHARTS_SCRIPT_URL,
+                })
+            }),
+            new webpack.IgnorePlugin({
+                resourceRegExp: /^fs$/
+            }),
+            new VueLoaderPlugin(),
+            new MiniCssExtractPlugin({
+                filename: '../css/doc-bundle.css'
+            })
+        ]
+    };
 };
+
diff --git a/config/env.dev.js b/config/env.dev.js
index 14cba3a5..127270d4 100644
--- a/config/env.dev.js
+++ b/config/env.dev.js
@@ -13,5 +13,7 @@ module.exports = {
     },
 
     releaseDestDir: path.resolve(__dirname, '../public'),
-    ecWWWGeneratedDir: path.resolve(__dirname, '../../echarts-www/_generated')
+    ecWWWGeneratedDir: path.resolve(__dirname, '../../echarts-www/_generated'),
+
+    EMBEDDED_ECHARTS_SCRIPT_URL: null,
 };
\ No newline at end of file
diff --git a/package.json b/package.json
index 9634f9f8..7b21cbcd 100644
--- a/package.json
+++ b/package.json
@@ -7,9 +7,9 @@
     "url": "https://github.com/apache/echarts-doc";
   },
   "scripts": {
-    "build": "node build.js --env asf",
-    "watch": "node build.js --env dev --watch",
-    "localsite": "node build.js --env localsite",
+    "build": "node build/build-doc.js --env asf",
+    "watch": "node build/build-doc.js --env dev --watch",
+    "localsite": "node build/build-doc.js --env localsite",
     "build:site": "webpack --config build/webpack.config.js --mode production",
     "watch:site": "webpack --config build/webpack.config.js --mode development 
--devtool inline-source-map --watch",
     "server": "node server.js",
diff --git a/src/components/LiveExample.vue b/src/components/LiveExample.vue
index 03a64684..090ce47d 100644
--- a/src/components/LiveExample.vue
+++ b/src/components/LiveExample.vue
@@ -86,7 +86,9 @@ let echartsLoadPromise;
 function fetchECharts() {
     return echartsLoadPromise || (echartsLoadPromise = new Promise(function 
(resolve, reject) {
         const script = document.createElement('script');
-        script.src = (window.ECHARTS_WWW_VENDORS_CDN_ROOT || 
'https://fastly.jsdelivr.net/npm/') + 'echarts/dist/echarts.min.js';
+        script.src = INJECTED_CONFIG.EMBEDDED_ECHARTS_SCRIPT_URL
+            ? INJECTED_CONFIG.EMBEDDED_ECHARTS_SCRIPT_URL
+            : (window.ECHARTS_WWW_VENDORS_CDN_ROOT || 
'https://fastly.jsdelivr.net/npm/') + 'echarts/dist/echarts.min.js';
         script.async = true;
         script.onload = function () {
             echartsLoadPromise = null;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org
For additional commands, e-mail: commits-h...@echarts.apache.org

Reply via email to