<|start_filename|>packages/fes-preset-built-in/src/utils/constants.js<|end_filename|>
import { winPath } from '@fesjs/utils';
import { dirname } from 'path';

export const runtimePath = winPath(
    dirname(require.resolve('@fesjs/runtime/package.json'))
);


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/info/index.js<|end_filename|>

export default function (api) {
    api.registerCommand({
        command: 'info',
        description: 'print debugging information about your environment',
        async fn() {
            return require('envinfo').run(
                {
                    System: ['OS', 'CPU'],
                    Binaries: ['Node', 'Yarn', 'npm'],
                    Browsers: ['Chrome', 'Edge', 'Firefox', 'Safari'],
                    npmPackages: ['@fesjs/fes', 'vue', 'vue-router'],
                    npmGlobalPackages: ['@fesjs/fes']
                },
                {
                    showNotFound: true,
                    duplicates: true,
                    fullTree: true
                }
            )
                .then(console.log);
        }
    });
}


<|start_filename|>jest.config.js<|end_filename|>

module.exports = {
    testPathIgnorePatterns: [
        '/node_modules/',
        'fes-template',
        'fes-template-h5'
    ]
};


<|start_filename|>packages/fes-compiler/src/index.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/tree/master/packages/core
 */


import Config from './config';
import Logger from './logger';
import Service from './service';
import PluginAPI from './service/pluginAPI';
import { PluginType } from './service/enums';
import { isPluginOrPreset } from './service/utils/pluginUtils';

export {
    Config,
    Service,
    PluginAPI,
    isPluginOrPreset,
    PluginType,
    Logger
};


<|start_filename|>packages/fes-plugin-access/src/runtime/createComponent.js<|end_filename|>
export default function createComponent(useAccess) {
    return (props, { slots }) => {
        const access = useAccess(props.id);
        if (!access.value || !slots.default) return null;
        return slots.default();
    };
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/build/build.js<|end_filename|>
import webpack from 'webpack';

export async function build({
    bundleConfig
}) {
    return new Promise((resolve, reject) => {
        const compiler = webpack(bundleConfig);
        compiler.run((err, stats) => {
            if (err || stats.hasErrors()) {
                try {
                    console.log(stats.toString('errors-only'));
                } catch (e) {}
                console.error(err);
                return reject(new Error('build failed'));
            }
            resolve({ stats });
        });
    });
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/copy.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'copy',
        config: {
            schema(joi) {
                return joi.array().items(
                    joi.alternatives(
                        joi.object({
                            from: joi.string(),
                            to: joi.string()
                        }),
                        joi.string()
                    )
                );
            }
        }
    });
};


<|start_filename|>packages/fes-plugin-qiankun/examples/app1/.fes.js<|end_filename|>
// .fes.js 只负责管理编译时配置，只能使用plain Object


export default {
    qiankun: {
        micro: {}
    },
    plugins: [
        require.resolve('../../../fes-plugin-model/lib'),
        require.resolve('../../../fes-plugin-qiankun/lib'),
    ]
};


<|start_filename|>packages/fes-plugin-qiankun/examples/app1/src/app.js<|end_filename|>
import PageLoading from '@/components/PageLoading';

export const beforeRender = {
    loading: <PageLoading />,
    action() {
        return new Promise((resolve) => {
            setTimeout(() => {
                resolve();
            }, 200);
        });
    }
};

export const qiankun = {
    // 应用加载之前
    async bootstrap(props) {
        console.log('app1 bootstrap', props);
    },
    // 应用 render 之前触发
    async mount(props) {
        console.log('app1 mount', props);
    },
    // 当 props 更新时触发
    async update(props) {
        console.log('app1 update', props);
    },
    // 应用卸载之后触发
    async unmount(props) {
        console.log('app1 unmount', props);
    }
};


<|start_filename|>packages/fes-plugin-model/src/utils/getModels.js<|end_filename|>
import { glob } from '@fesjs/utils';
import { getValidFiles } from '.';

export function getModels(cwd, pattern) {
    const files = glob
        .sync(pattern || '**/*.{js,jsx}', {
            cwd
        })
        .filter(
            file => !file.endsWith('.d.ts')
        && !file.endsWith('.test.js')
        && !file.endsWith('.test.jsx')
        );

    return getValidFiles(files, cwd);
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/cssLoader.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'cssLoader',
        config: {
            default: {},
            schema(joi) {
                return joi
                    .object({
                        url: joi.alternatives(joi.boolean(), joi.function()),
                        import: joi.alternatives(joi.boolean(), joi.function()),
                        modules: joi.alternatives(
                            joi.boolean(),
                            joi.string(),
                            joi.object()
                        ),
                        sourceMap: joi.boolean(),
                        importLoaders: joi.number(),
                        onlyLocals: joi.boolean(),
                        esModule: joi.boolean(),
                        localsConvention: joi
                            .string()
                            .valid(
                                'asIs',
                                'camelCase',
                                'camelCaseOnly',
                                'dashes',
                                'dashesOnly'
                            )
                    })
                    .description(
                        'more css-loader options see https://webpack.js.org/loaders/css-loader/#options'
                    );
            }
        }
    });
};


<|start_filename|>packages/fes-plugin-locale/src/runtime/langUConfigMap.js<|end_filename|>
export default {
    'ar-EG': {
        lang: 'ar-EG',
        label: 'العربية',
        icon: '🇪🇬',
        title: 'لغة'
    },
    'az-AZ': {
        lang: 'az-AZ',
        label: 'Az<NAME>',
        icon: '🇦🇿',
        title: 'Dil'
    },
    'bg-BG': {
        lang: 'bg-BG',
        label: 'Български език',
        icon: '🇧🇬',
        title: 'език'
    },
    'ca-ES': {
        lang: 'ca-ES',
        label: 'Catalá',
        icon: '🇨🇦',
        title: 'llengua'
    },
    'cs-CZ': {
        lang: 'cs-CZ',
        label: 'Čeština',
        icon: '🇨🇿',
        title: 'Jazyk'
    },
    'da-DK': {
        lang: 'da-DK',
        label: 'Dansk',
        icon: '🇩🇰',
        title: 'Sprog'
    },
    'de-DE': {
        lang: 'de-DE',
        label: 'Deutsch',
        icon: '🇩🇪',
        title: 'Sprache'
    },
    'el-GR': {
        lang: 'el-GR',
        label: 'Ελληνικά',
        icon: '🇬🇷',
        title: 'Γλώσσα'
    },
    'en-GB': {
        lang: 'en-GB',
        label: 'English',
        icon: '🇬🇧',
        title: 'Language'
    },
    'en-US': {
        lang: 'en-US',
        label: 'English',
        icon: '🇺🇸',
        title: 'Language'
    },
    'es-ES': {
        lang: 'es-ES',
        label: 'Español',
        icon: '🇪🇸',
        title: 'Idioma'
    },
    'et-EE': {
        lang: 'et-EE',
        label: 'Eesti',
        icon: '🇪🇪',
        title: 'Keel'
    },
    'fa-IR': {
        lang: 'fa-IR',
        label: 'فارسی',
        icon: '🇮🇷',
        title: 'زبان'
    },
    'fi-FI': {
        lang: 'fi-FI',
        label: 'Suomi',
        icon: '🇫🇮',
        title: 'Kieli'
    },
    'fr-BE': {
        lang: 'fr-BE',
        label: 'Français',
        icon: '🇧🇪',
        title: 'Langue'
    },
    'fr-FR': {
        lang: 'fr-FR',
        label: 'Français',
        icon: '🇫🇷',
        title: 'Langue'
    },
    'ga-IE': {
        lang: 'ga-IE',
        label: 'Gaeilge',
        icon: '🇮🇪',
        title: 'Teanga'
    },
    'he-IL': {
        lang: 'he-IL',
        label: 'עברית',
        icon: '🇮🇱',
        title: 'שפה'
    },
    'hi-IN': {
        lang: 'hi-IN',
        label: 'हिन्दी, हिंदी',
        icon: '🇮🇳',
        title: 'भाषा: हिन्दी'
    },
    'hr-HR': {
        lang: 'hr-HR',
        label: 'Hrvatski jezik',
        icon: '🇭🇷',
        title: 'Jezik'
    },
    'hu-HU': {
        lang: 'hu-HU',
        label: 'Magyar',
        icon: '🇭🇺',
        title: 'Nyelv'
    },
    'hy-AM': {
        lang: 'hu-HU',
        label: 'Հայերեն',
        icon: '🇦🇲',
        title: 'Լեզու'
    },
    'id-ID': {
        lang: 'id-ID',
        label: 'Bahasa Indonesia',
        icon: '🇮🇩',
        title: 'Bahasa'
    },
    'it-IT': {
        lang: 'it-IT',
        label: 'Italiano',
        icon: '🇮🇹',
        title: 'Linguaggio'
    },
    'is-IS': {
        lang: 'is-IS',
        label: 'Íslenska',
        icon: '🇮🇸',
        title: 'Tungumál'
    },
    'ja-JP': {
        lang: 'ja-JP',
        label: '日本語',
        icon: '🇯🇵',
        title: '言語'
    },
    'ku-IQ': {
        lang: 'ku-IQ',
        label: 'کوردی',
        icon: '🇮🇶',
        title: 'Ziman'
    },
    'kn-IN': {
        lang: 'zh-TW',
        label: 'ಕನ್ನಡ',
        icon: '🇮🇳',
        title: 'ಭಾಷೆ'
    },
    'ko-KR': {
        lang: 'ko-KR',
        label: '한국어',
        icon: '🇰🇷',
        title: '언어'
    },
    'lv-LV': {
        lang: 'lv-LV',
        label: 'Latviešu valoda',
        icon: '🇱🇮',
        title: 'Kalba'
    },
    'mk-MK': {
        lang: 'mk-MK',
        label: 'македонски јазик',
        icon: '🇲🇰',
        title: 'Јазик'
    },
    'mn-MN': {
        lang: 'mn-MN',
        label: 'Монгол хэл',
        icon: '🇲🇳',
        title: 'Хэл'
    },
    'ms-MY': {
        lang: 'ms-MY',
        label: 'بهاس ملايو‎',
        icon: '🇲🇾',
        title: 'Bahasa'
    },
    'nb-NO': {
        lang: 'nb-NO',
        label: 'Norsk',
        icon: '🇳🇴',
        title: 'Språk'
    },
    'ne-NP': {
        lang: 'ne-NP',
        label: 'नेपाली',
        icon: '🇳🇵',
        title: 'भाषा'
    },
    'nl-BE': {
        lang: 'nl-BE',
        label: 'Vlaams',
        icon: '🇧🇪',
        title: 'Taal'
    },
    'nl-NL': {
        lang: 'nl-NL',
        label: 'Vlaams',
        icon: '🇳🇱',
        title: 'Taal'
    },
    'pt-BR': {
        lang: 'pt-BR',
        label: 'Português',
        icon: '🇧🇷',
        title: 'Idiomas'
    },
    'pt-PT': {
        lang: 'pt-PT',
        label: 'Português',
        icon: '🇵🇹',
        title: 'Idiomas'
    },
    'ro-RO': {
        lang: 'ro-RO',
        label: 'Română',
        icon: '🇷🇴',
        title: 'Limba'
    },
    'ru-RU': {
        lang: 'ru-RU',
        label: 'русский',
        icon: '🇷🇺',
        title: 'язык'
    },
    'sk-SK': {
        lang: 'sk-SK',
        label: 'Slovenčina',
        icon: '🇸🇰',
        title: 'Jazyk'
    },
    'sr-RS': {
        lang: 'sr-RS',
        label: 'српски језик',
        icon: '🇸🇷',
        title: 'Језик'
    },
    'sl-SI': {
        lang: 'sl-SI',
        label: 'Slovenščina',
        icon: '🇸🇱',
        title: 'Jezik'
    },
    'sv-SE': {
        lang: 'sv-SE',
        label: 'Svenska',
        icon: '🇸🇪',
        title: 'Språk'
    },
    'ta-IN': {
        lang: 'ta-IN',
        label: 'தமிழ்',
        icon: '🇮🇳',
        title: 'மொழி'
    },
    'th-TH': {
        lang: 'th-TH',
        label: 'ไทย',
        icon: '🇹🇭',
        title: 'ภาษา'
    },
    'tr-TR': {
        lang: 'tr-TR',
        label: 'Türkçe',
        icon: '🇹🇷',
        title: 'Dil'
    },
    'uk-UA': {
        lang: 'uk-UA',
        label: 'Українська',
        icon: '🇺🇰',
        title: 'Мова'
    },
    'vi-VN': {
        lang: 'vi-VN',
        label: 'Tiếng Việt',
        icon: '🇻🇳',
        title: 'Ngôn ngữ'
    },
    'zh-CN': {
        lang: 'zh-CN',
        label: '简体中文',
        icon: '🇨🇳',
        title: '语言'
    },
    'zh-TW': {
        lang: 'zh-TW',
        label: '繁体中文',
        icon: '🇭🇰',
        title: '語言'
    }
};


<|start_filename|>packages/fes-runtime/build.config.js<|end_filename|>

module.exports = {
    target: 'browser'
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/dev/index.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/commands/dev/dev.ts
 */

const assert = require('assert');

export default (api) => {
    const {
        env,
        paths,
        utils: { chalk, portfinder }
    } = api;

    const unwatchs = [];
    let port;
    let hostname;
    let server;

    function destroy() {
        for (const unwatch of unwatchs) {
            unwatch();
        }
        server?.close();
    }

    api.registerCommand({
        command: 'dev',
        description: 'start a local http service for development',
        options: [{
            name: '--port',
            description: 'http service port, like 8080'
        }, {
            name: '--https',
            description: 'whether to turn on the https service'
        }],
        async fn({ args = {} }) {
            const {
                cleanTmpPathExceptCache,
                getBundleAndConfigs
            } = require('../buildDevUtils');
            const { delay } = require('@fesjs/utils');
            const createRouteMiddleware = require('./createRouteMiddleware').default;
            const generateFiles = require('../../../utils/generateFiles').default;
            const { watchPkg } = require('./watchPkg');

            const defaultPort = process.env.PORT || args.port || api.config.devServer?.port;
            port = await portfinder.getPortPromise({
                port: defaultPort ? parseInt(String(defaultPort), 10) : 8000
            });
            hostname = process.env.HOST || api.config.devServer?.host || 'localhost';

            process.send({
                type: 'UPDATE_PORT',
                port
            });

            // enable https
            const isHTTPS = process.env.HTTPS || args.https;

            console.log(chalk.cyan(`Starting the development server ${isHTTPS ? 'https' : 'http'}://${hostname}:${port} ...`));

            cleanTmpPathExceptCache({
                absTmpPath: paths.absTmpPath
            });
            const watch = process.env.WATCH !== 'none';

            // generate files
            const unwatchGenerateFiles = await generateFiles({
                api,
                watch
            });
            if (unwatchGenerateFiles) unwatchs.push(unwatchGenerateFiles);

            if (watch) {
                // watch pkg changes
                const unwatchPkg = watchPkg({
                    cwd: api.cwd,
                    onChange() {
                        console.log();
                        api.logger.info('Plugins in package.json changed.');
                        api.restartServer();
                    }
                });
                unwatchs.push(unwatchPkg);

                // watch config change
                const unwatchConfig = api.service.configInstance.watch({
                    userConfig: api.service.userConfig,
                    onChange: async ({ pluginChanged, valueChanged }) => {
                        if (pluginChanged.length) {
                            console.log();
                            api.logger.info(
                                `Plugins of ${pluginChanged
                                    .map(p => p.key)
                                    .join(', ')} changed.`
                            );
                            api.restartServer();
                        }
                        if (valueChanged.length) {
                            let reload = false;
                            let regenerateTmpFiles = false;
                            const fns = [];
                            const reloadConfigs = [];
                            valueChanged.forEach(({ key, pluginId }) => {
                                const { onChange } = api.service.plugins[pluginId].config || {};
                                if (
                                    onChange
                                    === api.ConfigChangeType.regenerateTmpFiles
                                ) {
                                    regenerateTmpFiles = true;
                                }
                                if (
                                    !onChange
                                    || onChange === api.ConfigChangeType.reload
                                ) {
                                    reload = true;
                                    reloadConfigs.push(key);
                                }
                                if (typeof onChange === 'function') {
                                    fns.push(onChange);
                                }
                            });

                            if (reload) {
                                console.log();
                                api.logger.info(
                                    `Config ${reloadConfigs.join(
                                        ', '
                                    )} changed.`
                                );
                                api.restartServer();
                            } else {
                                api.service.userConfig = api.service.configInstance.getUserConfig();

                                await api.setConfig();

                                if (regenerateTmpFiles) {
                                    await generateFiles({
                                        api
                                    });
                                } else {
                                    fns.forEach(fn => fn());
                                }
                            }
                        }
                    }
                });
                unwatchs.push(unwatchConfig);
            }

            // delay dev server 启动，避免重复 compile
            // https://github.com/webpack/watchpack/issues/25
            // https://github.com/yessky/webpack-mild-compile
            await delay(500);

            // dev
            const { bundleConfig } = await getBundleAndConfigs({ api });

            const beforeMiddlewares = await api.applyPlugins({
                key: 'addBeforeMiddlewares',
                type: api.ApplyPluginsType.add,
                initialValue: [],
                args: {}
            });
            const middlewares = await api.applyPlugins({
                key: 'addMiddlewares',
                type: api.ApplyPluginsType.add,
                initialValue: [],
                args: {}
            });
            const { startDevServer } = require('./devServer');
            server = startDevServer({
                webpackConfig: bundleConfig,
                host: hostname,
                port,
                proxy: api.config.proxy,
                https: isHTTPS,
                beforeMiddlewares: [...beforeMiddlewares, createRouteMiddleware(api)],
                afterMiddlewares: [...middlewares],
                customerDevServerConfig: api.config.devServer
            });
            return {
                destroy
            };
        }
    });

    api.registerMethod({
        name: 'getPort',
        fn() {
            assert(
                env === 'development',
                'api.getPort() is only valid in development.'
            );
            return port;
        }
    });

    api.registerMethod({
        name: 'getHostname',
        fn() {
            assert(
                env === 'development',
                'api.getHostname() is only valid in development.'
            );
            return hostname;
        }
    });

    api.registerMethod({
        name: 'getServer',
        fn() {
            assert(
                env === 'development',
                'api.getServer() is only valid in development.'
            );
            return server;
        }
    });

    api.registerMethod({
        name: 'restartServer',
        fn() {
            console.log(chalk.gray('Try to restart dev server...'));
            destroy();
            process.send({
                type: 'RESTART'
            });
        }
    });
};


<|start_filename|>packages/create-fes-app/templates/app/pc/.fes.prod.js<|end_filename|>
// .fes.js 只负责管理编译时配置，只能使用plain Object


export default {
    publicPath: ''
};


<|start_filename|>packages/fes-plugin-request/src/template/helpers.js<|end_filename|>
/**
 *判断类型
 * @param {*} obj 需要判断的对象
 */
export function typeOf(obj) {
    const map = {
        '[object Boolean]': 'boolean',
        '[object Number]': 'number',
        '[object String]': 'string',
        '[object Function]': 'function',
        '[object Array]': 'array',
        '[object Date]': 'date',
        '[object RegExp]': 'regExp',
        '[object Undefined]': 'undefined',
        '[object Null]': 'null',
        '[object Object]': 'object',
        '[object URLSearchParams]': 'URLSearchParams'
    };
    return map[Object.prototype.toString.call(obj)];
}

export function isFunction(obj) {
    return typeOf(obj) === 'function';
}

export function isDate(obj) {
    return typeOf(obj) === 'date';
}

export function isString(obj) {
    return typeOf(obj) === 'string';
}

export function isArray(obj) {
    return typeOf(obj) === 'array';
}

export function isObject(obj) {
    return typeOf(obj) === 'object';
}

export function isURLSearchParams(obj) {
    return typeOf(obj) === 'URLSearchParams';
}

// eslint-disable-next-line
export const isUndefined = val => val === undefined;

export const isDefined = val => val != null;


export function checkHttpRequestHasBody(method) {
    method = method.toUpperCase();
    const HTTP_METHOD = {
        GET: {
            request_body: false
        },
        POST: {
            request_body: true
        },
        PUT: {
            request_body: true
        },
        DELETE: {
            request_body: true
        },
        HEAD: {
            request_body: false
        },
        OPTIONS: {
            request_body: false
        },
        PATCH: {
            request_body: true
        }
    };
    return HTTP_METHOD[method].request_body;
}

export function trimObj(obj) {
    if (isObject(obj)) {
        Object.entries(obj).forEach(([key, value]) => {
            if (isString(value)) {
                obj[key] = value.trim();
            } else if (isObject(value)) {
                trimObj(value);
            }
        });
    }
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/babelPluginImport.js<|end_filename|>
export default (api) => {
    api.describe({
        key: 'babelPluginImport',
        config: {
            schema(joi) {
                return joi.array();
            }
        }
    });

    api.modifyBabelOpts((babelOpts) => {
        if (api.config.babelPluginImport) {
            api.config.babelPluginImport.forEach((config) => {
                babelOpts.plugins.push(['import', config]);
            });
        }

        return babelOpts;
    });
};


<|start_filename|>scripts/compiler.js<|end_filename|>
// 关闭 import 规则
/* eslint import/no-extraneous-dependencies: 0 */

const babel = require('@babel/core');


function transform(code, options) {
    const result = babel.transformSync(code, options);
    return result.code;
}

function transformNodeCode(code) {
    return transform(code, {
        presets: [
            ['@babel/preset-env', {
                modules: 'cjs',
                targets: { node: '12' }
            }]
        ]
    });
}

function transformBrowserCode(code) {
    // 因为 fes.js 在生产打包的时候，会处理所有的 node_modules 下的文件，确保不会丢失必要 polyfill
    // 因此这里不对 polyfill 进行处理，避免全局污染
    return transform(code, {
        presets: [
            ['@babel/preset-env', {
                modules: false,
                useBuiltIns: false,
                targets: { chrome: '51' }
            }]
        ]
    });
}

function compiler(code, config) {
    if (!config.target || config.target === 'node') {
        return transformNodeCode(code);
    }
    if (config.target === 'browser') {
        return transformBrowserCode(code);
    }
    throw new Error(`config target error: ${config.target}, only can use 'node' and 'browser'`);
}


module.exports = compiler;


<|start_filename|>packages/fes-template/src/locales/id-ID.js<|end_filename|>

export default {
    'navbar.lang': 'Bahasa',
    'layout.user.link.help': 'Bantuan',
    'layout.user.link.privacy': 'Privasi',
    'layout.user.link.terms': 'Ketentuan',
    'app.preview.down.block': 'Unduh halaman ini dalam projek lokal anda',
    'app.welcome.link.fetch-blocks': 'Dapatkan semua blok',
    'app.welcome.link.block-list':
    'Buat standar dengan cepat, halaman-halaman berdasarkan pengembangan `block`'
};


<|start_filename|>packages/fes-template-h5/src/common/utils.js<|end_filename|>
// TODO
// 时间格式化
// js 数字精度计算
// 手机号、身份证号 等的校验
// 数字分割


export function resetContainerHeight(dom) {
    const originalHeight = document.body.clientHeight || document.documentElement.clientHeight;

    window.onresize = function () {
        const resizeHeight = document.documentElement.clientHeight || document.body.clientHeight;
        if (resizeHeight < originalHeight) {
            // 恢复内容区域高度
            const container = document.querySelector(dom);
            container.style.height = originalHeight;
        }
    };
}


export function resetInputBlur() {
    const isWechat = window.navigator.userAgent.match(/MicroMessenger\/([\d.]+)/i);
    if (!isWechat) return;
    const wechatVersion = isWechat[1];
    const version = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);

    // 如果设备类型为iOS 12+ 和wechat 6.7.4+，恢复成原来的视口
    if (+wechatVersion.replace(/\./g, '') >= 674 && +version[1] >= 12) {
        window.scrollTo(0, Math.max(document.body.clientHeight, document.documentElement.clientHeight));
    }
}

export function getQueryString(name) {
    const reg = new RegExp(`(^|&)${name}=([^&]*)(&|$)`, 'i');
    const r = window.location.search.substr(1).match(reg);
    if (r != null) {
        return decodeURIComponent(r[2]);
    }
    return null;
}

export function simpleRequest(options) {
    const xhr = new XMLHttpRequest();
    xhr.timeout = 3000;
    if (options.type === 'GET') {
        xhr.open(options.type, options.url, options.async || true);
        xhr.send(null);
    } else if (options.type === 'POST') {
        xhr.open(options.type, options.url, options.async || true);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(JSON.stringify(options.data || {}));
    }
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status >= 200 && xhr.status < 300) {
                options.successed(xhr.responseText);
            } else {
                options.failed && options.failed(xhr);
            }
        }
    };
    xhr.ontimeout = function () {
        options.failed && options.failed(xhr);
    };
}


<|start_filename|>packages/fes-utils/src/compatESModuleRequire.js<|end_filename|>
export default function compatESModuleRequire(m) {
    return m.__esModule ? m.default : m;
}


<|start_filename|>packages/fes-plugin-qiankun/examples/main/src/app.js<|end_filename|>
import { access as accessApi } from '@fesjs/fes';
import PageLoading from '@/components/PageLoading';
import Antdv from 'ant-design-vue';
import 'ant-design-vue/dist/antd.css';

export const beforeRender = {
    loading: <PageLoading />,
    action() {
        const { setRole } = accessApi;
        return new Promise((resolve) => {
            setTimeout(() => {
                setRole('admin');
                resolve();
            }, 1000);
        });
    }
};

export const onAppCreated = ({ app }) => {
    app.use(Antdv);
};


<|start_filename|>packages/fes-plugin-access/src/runtime/createDirective.js<|end_filename|>
import { watch } from 'vue';

const cache = new WeakMap();
const setDispaly = (el, access) => {
    if (access.value) {
        el.style.display = el._display;
    } else {
        el.style.display = 'none';
    }
};
export default function createDirective(useAccess) {
    return {
        beforeMount(el) {
            const ctx = {};
            ctx.watch = (path) => {
                el._display = el._display || el.style.display;
                const access = useAccess(path);
                setDispaly(el, access);
                return watch(access, () => {
                    setDispaly(el, access);
                });
            };
            cache.set(el, ctx);
        },
        mounted(el, binding) {
            const ctx = cache.get(el);
            if (ctx.unwatch) {
                ctx.unwatch();
            }
            ctx.unwatch = ctx.watch(binding.value);
        },
        updated(el, binding) {
            const ctx = cache.get(el);
            if (ctx.unwatch) {
                ctx.unwatch();
            }
            ctx.unwatch = ctx.watch(binding.value);
        },
        beforeUnmount(el) {
            const ctx = cache.get(el);
            if (ctx.unwatch) {
                ctx.unwatch();
            }
        }
    };
}


<|start_filename|>packages/fes-plugin-jest/src/createDefaultConfig.js<|end_filename|>
import { existsSync } from 'fs';
import { join } from 'path';

export default (cwd, args) => {
    const testMatchTypes = ['spec', 'test'];
    if (args.e2e) {
        testMatchTypes.push('e2e');
    }
    const hasSrc = existsSync(join(cwd, 'src'));
    return {
        collectCoverageFrom: [
            'index.{js,jsx,vue}',
            hasSrc && 'src/**/*.{js,jsx,vue}',
            '!**/.fes/**',
            '!**/typings/**',
            '!**/types/**',
            '!**/fixtures/**',
            '!**/examples/**',
            '!**/*.d.ts'
        ].filter(Boolean),
        moduleFileExtensions: [
            'js',
            'jsx',
            'json',
            // tell Jest to handle *.vue files
            'vue'
        ],
        transform: {
            // process *.vue files with vue-jest
            '^.+\\.vue$': require.resolve('vue3-jest'),
            '.+\\.(css|styl|less|sass|scss|jpg|jpeg|png|svg|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
                require.resolve('jest-transform-stub'),
            '^.+\\.jsx?$': require.resolve(
                '../helpers/transformers/javascript'
            )
        },
        transformIgnorePatterns: ['/node_modules/'],
        // support the same @ -> src alias mapping in source code
        moduleNameMapper: {
            '^@/(.*)$': '<rootDir>/src/$1'
        },
        testMatch: [
            `**/tests/**/*.(${testMatchTypes.join('|')}).[jt]s?(x)`,
            '**/__tests__/**/*.[jt]s?(x)'
        ],
        // https://github.com/facebook/jest/issues/6766
        testURL: 'http://localhost/',
        watchPlugins: [
            require.resolve('jest-watch-typeahead/filename'),
            require.resolve('jest-watch-typeahead/testname')
        ],
        verbose: true
    };
};


<|start_filename|>packages/fes-template/src/stores/user.js<|end_filename|>
export default {
    namespaced: true,
    state: () => ({
        name: 'aring',
        age: 20,
        count: 0
    }),
    mutations: {
        increment(state) {
            state.count++;
        }
    },
    getters: {
        doubleCount(state) {
            return state.count * 2;
        }
    },
    actions: {
        asyncIncrement({ commit }) {
            setTimeout(() => {
                commit('increment');
            }, 2000);
        },
        login() {
            return new Promise((reslove) => {
                setTimeout(() => {
                    console.log('login');
                    reslove('OK');
                }, 1000);
            });
        }
    },
    modules: {
        address: {
            state: () => ({
                province: '广东省',
                city: '深圳市',
                zone: '南山区'
            }),
            getters: {
                address(state) {
                    return state.province + state.city + state.zone;
                }
            }
        },
        posts: {
            namespaced: true,
            state: () => ({}),
            mutations: {
                doSomething() {}
            }
        }
    }
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/base.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'base',
        config: {
            default: '',
            schema(joi) {
                return joi.string().allow('');
            }
        }
    });
};


<|start_filename|>packages/fes-plugin-request/build.config.js<|end_filename|>

module.exports = {
    copy: ['template']
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/webpackConfig/vue.js<|end_filename|>
// import webpack from 'webpack';

export default function createVueWebpackConfig({
    config,
    webpackConfig
}) {
    webpackConfig.module
        .rule('vue')
        .test(/\.vue$/)
        .use('vue-loader')
        .loader(require.resolve('vue-loader'))
        .options({
            babelParserPlugins: ['jsx', 'classProperties', 'decorators-legacy'],
            ...(config.vueLoader || {})
        })
        .end();

    webpackConfig
        .plugin('vue-loader')
        .use(require('vue-loader').VueLoaderPlugin);
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/nodeModulesTransform.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'nodeModulesTransform',
        config: {
            default: {
                exclude: []
            },
            schema(joi) {
                return joi.object({
                    exclude: joi.array().items(joi.string())
                });
            }
        }
    });
};


<|start_filename|>packages/fes/build.config.js<|end_filename|>

module.exports = {
    copy: ['index.js']
};


<|start_filename|>packages/fes-plugin-layout/src/runtime/helpers/pluginLocale.js<|end_filename|>
import { unref, computed } from 'vue';
import { plugin } from '@@/core/coreExports';


const transTitle = (name) => {
    const sharedLocale = plugin.getShared('locale');
    if (sharedLocale) {
        const { t } = sharedLocale.useI18n();
        return t(name);
    }
    return name;
};


const _transform = (arr) => {
    if (Array.isArray(arr)) {
        arr.forEach((item) => {
            if (item.title) {
                item._title = item.title;
                item.title = computed(() => transTitle(item._title));
            }
            if (item.children && item.children.length > 0) {
                _transform(item.children);
            }
        });
    }
};

export const transform = (menus) => {
    const originData = unref(menus);
    _transform(originData);
    return originData;
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/help/index.js<|end_filename|>


export default function (api) {
    api.registerCommand({
        command: 'help',
        description: 'show command helps',
        async fn({ program }) {
            program.outputHelp();
        }
    });
}


<|start_filename|>packages/fes-plugin-jest/helpers/transformers/javascript.js<|end_filename|>
const babelJest = require('babel-jest').default;

module.exports = babelJest.createTransformer({
    presets: [
        ['@babel/preset-env', { targets: { node: 'current' } }]
    ],
    plugins: ['@vue/babel-plugin-jsx'],
    babelrc: false,
    configFile: false
});


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/autoprefixer.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'autoprefixer',
        config: {
            default: {
                flexbox: 'no-2009'
            },
            schema(joi) {
                return joi
                    .object()
                    .description('postcss autoprefixer, default flexbox: no-2009');
            }
        }
    });
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/devServer.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'devServer',
        config: {
            default: {},
            schema(joi) {
                return joi
                    .object({
                        port: joi.number().description('devServer port, default 8000'),
                        host: joi.string(),
                        https: joi.alternatives(
                            joi
                                .object({
                                    key: joi.string(),
                                    cert: joi.string()
                                })
                                .unknown(),
                            joi.boolean()
                        ),
                        headers: joi.object(),
                        writeToDisk: joi.alternatives(joi.boolean(), joi.function())
                    })
                    .description('devServer configs')
                    .unknown(true);
            }
        }
    });
};


<|start_filename|>packages/fes-plugin-access/src/runtime/runtime.js<|end_filename|>
import { plugin, ApplyPluginsType } from '@@/core/coreExports';
import { access, install } from './core';

export function onRouterCreated({ router }) {
    router.beforeEach(async (to, from, next) => {
        const runtimeConfig = plugin.applyPlugins({
            key: 'access',
            type: ApplyPluginsType.modify,
            initialValue: {}
        });
        if (to.matched.length === 0) {
            if (runtimeConfig.noFoundHandler && typeof runtimeConfig.noFoundHandler === 'function') {
                return runtimeConfig.noFoundHandler({
                    router, to, from, next
                });
            }
        }
        let path;
        if (to.matched.length === 1) {
            path = to.matched[0].path;
        } else {
            path = to.path;
        }
        const canRoute = await access.hasAccess(path);
        if (canRoute) {
            return next();
        }
        if (runtimeConfig.unAccessHandler && typeof runtimeConfig.unAccessHandler === 'function') {
            return runtimeConfig.unAccessHandler({
                router, to, from, next
            });
        }
        next(false);
    });
}

export function onAppCreated({ app }) {
    install(app);
}


<|start_filename|>packages/fes-template/.fes.prod.js<|end_filename|>
// .fes.js 只负责管理编译时配置，只能使用plain Object


export default {
    publicPath: 'https://gw.alipayobjects.com/'
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/generateFiles/core/exports/coreExports.js<|end_filename|>
import { readFileSync } from 'fs';
import { join } from 'path';
import generateExports from '../../../../utils/generateExports';

export default function (api) {
    api.onGenerateFiles(async () => {
        const coreExports = await api.applyPlugins({
            key: 'addCoreExports',
            type: api.ApplyPluginsType.add,
            initialValue: []
        });

        const fesExportsHook = {}; // repeated definition
        const absoluteFilePath = 'core/coreExports.js';
        const content = `${coreExports
            .map(item => generateExports(absoluteFilePath, {
                item,
                fesExportsHook
            }))
            .join('\n')}\n`;
        const tpl = readFileSync(join(__dirname, './coreExports.tpl'), 'utf-8');
        api.writeTmpFile({
            path: absoluteFilePath,
            content: tpl.replace('CORE_EXPORTS', content)
        });
    });
}


<|start_filename|>packages/fes-template/src/common/service.js<|end_filename|>
import { requestWrap } from '@fesjs/fes';

// TODO
// 响应体控制
// formData 控制
// 错误控制
// 跳错误页面 || 或者重新登录
// 段时间内不能重复发送的请求

// or
export default requestWrap({
    login: {
        url: '',
        throttle: 300,
        options: {
            method: 'get'
        }
    }
});


<|start_filename|>packages/create-fes-app/src/index.js<|end_filename|>
import path from 'path';
import { chalk } from '@fesjs/utils';
import validateProjectName from 'validate-npm-package-name';
import fs from 'fs-extra';
import inquirer from 'inquirer';

import { clearConsole } from './utils';
import AppGenerator from './generator/App';

export default async ({ cwd, args }) => {
    if (args.proxy) {
        process.env.HTTP_PROXY = args.proxy;
    }
    const projectName = args._[0];
    const inCurrent = projectName === '.';
    const name = inCurrent ? path.relative('../', cwd) : projectName;
    const targetDir = path.resolve(cwd, projectName || '.');

    const result = validateProjectName(name);
    if (!result.validForNewPackages) {
        console.error(chalk.red(`Invalid project name: "${name}"`));
        result.errors && result.errors.forEach((err) => {
            console.error(chalk.red.dim(`Error: ${err}`));
        });
        result.warnings && result.warnings.forEach((warn) => {
            console.error(chalk.red.dim(`Warning: ${warn}`));
        });
        throw new Error('Process exited');
    }
    if (fs.pathExistsSync(targetDir) && !args.merge) {
        if (args.force) {
            await fs.remove(targetDir);
        } else if (inCurrent) {
            clearConsole();
            const { ok } = await inquirer.prompt([
                {
                    name: 'ok',
                    type: 'confirm',
                    message: 'Generate project in current directory?'
                }
            ]);
            if (!ok) {
                return null;
            }
        } else {
            clearConsole();
            const { action } = await inquirer.prompt([
                {
                    name: 'action',
                    type: 'list',
                    message: `Target directory ${chalk.cyan(targetDir)} already exists. Pick an action:`,
                    choices: [
                        { name: 'Overwrite', value: 'overwrite' },
                        { name: 'Merge', value: 'merge' },
                        { name: 'Cancel', value: false }
                    ]
                }
            ]);
            if (!action) {
                return null;
            }
            if (action === 'overwrite') {
                console.log(`\nRemoving ${chalk.cyan(targetDir)}...`);
                await fs.remove(targetDir);
            }
        }
    }

    clearConsole();
    const { template } = await inquirer.prompt([
        {
            name: 'template',
            type: 'list',
            message: 'Pick an template:',
            choices: [
                { name: 'PC, suitable for management desk front-end applications', value: 'pc' },
                { name: 'H5, suitable for mobile applications', value: 'h5' },
                { name: 'Cancel', value: false }
            ]
        }
    ]);

    if (template) {
        const generator = new AppGenerator({
            cwd,
            args,
            targetDir,
            path: path.join(__dirname, `../templates/app/${template}`)
        });
        await generator.run();
        console.log();
        console.log(chalk.green(`project ${projectName} created successfully, please execute the following command to use:`));
        console.log(`$ cd ${projectName}`);
        console.log('$ yarn');
        console.log('$ yarn dev');
        console.log();
    }
};


<|start_filename|>packages/fes-utils/src/cleanRequireCache.js<|end_filename|>
export const isWindows = typeof process !== 'undefined' && process.platform === 'win32';

// 暂时无法使用 jest 进行单元测试，原因可参见
// https://github.com/facebook/jest/issues/5741

export default function (cacheKey) {
    // windows 下 require.cache 中路径 key 为类似 ‘c:\demo\.umirc.ts’
    const cachePath = isWindows ? cacheKey.replace(/\//g, '\\') : cacheKey;
    if (require.cache[cachePath]) {
        const cacheParent = (require.cache[cachePath]).parent;
        let i = cacheParent?.children.length || 0;
        // 清理 require cache 中 parents 的引用
        while (i--) {
            if (cacheParent.children[i].id === cachePath) {
                cacheParent.children.splice(i, 1);
            }
        }

        delete require.cache[cachePath];
    }
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/generateFiles/fes/index.js<|end_filename|>
import { readFileSync } from 'fs';
import { join } from 'path';
import { winPath } from '@fesjs/utils';
import { runtimePath } from '../../../utils/constants';

export function importsToStr(imports) {
    return imports.map((imp) => {
        const { source, specifier } = imp;
        if (specifier) {
            return `import ${specifier} from '${winPath(source)}';`;
        }
        return `import '${winPath(source)}';`;
    });
}

export default function (api) {
    const {
        utils: { Mustache }
    } = api;

    api.onGenerateFiles(async () => {
        const fesTpl = readFileSync(join(__dirname, 'fes.tpl'), 'utf-8');
        api.writeTmpFile({
            path: 'fes.js',
            content: Mustache.render(fesTpl, {
                enableTitle: api.config.title !== false,
                defaultTitle: api.config.title || '',
                runtimePath,
                rootElement: `#${api.config.mountElementId || 'app'}`,
                entryCode: (
                    await api.applyPlugins({
                        key: 'addEntryCode',
                        type: api.ApplyPluginsType.add,
                        initialValue: []
                    })
                ).join('\r\n'),
                entryCodeAhead: (
                    await api.applyPlugins({
                        key: 'addEntryCodeAhead',
                        type: api.ApplyPluginsType.add,
                        initialValue: []
                    })
                ).join('\r\n'),
                importsAhead: importsToStr(
                    await api.applyPlugins({
                        key: 'addEntryImportsAhead',
                        type: api.ApplyPluginsType.add,
                        initialValue: []
                    })
                ).join('\r\n'),
                imports: importsToStr(
                    await api.applyPlugins({
                        key: 'addEntryImports',
                        type: api.ApplyPluginsType.add,
                        initialValue: []
                    })
                ).join('\r\n')
            })
        });
    });
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/externals.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'externals',
        config: {
            schema(joi) {
                // https://webpack.js.org/configuration/externals/#externals
                return joi.alternatives(joi.object(), joi.string(), joi.function());
            }
        }
    });
};


<|start_filename|>.github/actions/generate-changelog/main.js<|end_filename|>

const cp = require("child_process");
cp.execSync(`cd ${__dirname}; npm ci`);

const path = require("path");
const core = require("@actions/core");
const lernaChangelog = path.resolve(__dirname, "node_modules/.bin/conventional-changelog");

const exec = cmd => cp.execSync(cmd).toString().trim();

const changelog = exec(`node ${lernaChangelog}  -p cmyr-config -r 2`);
console.log(changelog);

core.setOutput("changelog", JSON.stringify(changelog.split('\n').slice(4).join('\n')));

<|start_filename|>packages/fes-preset-built-in/src/plugins/features/mock.js<|end_filename|>
import { existsSync, readFileSync } from 'fs';
import { resolve } from 'path';
import { chokidar, lodash, parseRequireDeps } from '@fesjs/utils';
import bodyParser from 'body-parser';
import cookieParser from 'cookie-parser';
import mockjs from 'mockjs';

export default (api) => {
    let mockFlag = false; // mock 开关flag
    let mockPrefix = '/'; // mock 过滤前缀
    let mockFile = ''; // mock 文件
    let loadMock = ''; // mock 对象

    const registerBabel = (paths) => {
        // support
        // clear require cache and set babel register
        const requireDeps = paths.reduce((memo, file) => {
            memo = memo.concat(parseRequireDeps(file));
            return memo;
        }, []);
        requireDeps.forEach((f) => {
            if (require.cache[f]) {
                delete require.cache[f];
            }
        });
        api.babelRegister.setOnlyMap({
            key: 'mock',
            value: [...paths, ...requireDeps]
        });
    };

    api.describe({
        key: 'mock',
        config: {
            schema(joi) {
                return joi.alternatives(joi.boolean(), joi.object());
            }
        },
        enableBy: () => process.env.NODE_ENV === 'development'
    });

    // 对 array、object 遍历处理
    function traversalHandler(val, callback) {
        if (lodash.isArray(val)) {
            val.forEach(callback);
        }
        if (lodash.isPlainObject(val)) {
            Object.keys(val).forEach((key) => {
                callback(val[key], key);
            });
        }
    }

    // 根据参数个数获取配置
    function getOption(arg) {
        const len = arg.length;
        // 默认配置
        const option = {
            headers: {
                'Cache-Control': 'no-cache'
            },
            statusCode: 200,
            cookies: [],
            timeout: 0
        };
        if (len === 0) return option;
        if (len === 1) {
            const newOption = arg[0];
            if (lodash.isPlainObject(newOption)) {
                traversalHandler(newOption, (value, key) => {
                    if (key === 'headers') {
                        traversalHandler(
                            newOption.headers,
                            (headervalue, headerkey) => {
                                option.headers[headerkey] = newOption.headers[headerkey];
                            }
                        );
                    } else {
                        option[key] = newOption[key];
                    }
                });
            }
        } else {
            option.url = arg[0];
            option.result = arg[1];
        }
        return option;
    }

    // 把基于 cgiMockfile 的相对绝对转成绝对路径
    function parsePath(value) {
        return resolve(api.cwd, value);
    }

    const createMock = () => {
        const requestList = [];
        const cgiMock = (...arg) => {
            const option = getOption(arg);
            if (!option.url || !option.result) return;
            requestList.push(option);
        };

        const utils = {};
        utils.file = function (file) {
            return readFileSync(parsePath(file));
        };

        // mock打开情况下，配置的过滤前缀
        const mockPrefixTemp = api.config.mock.prefix || mockPrefix;
        mockPrefix = mockPrefixTemp === mockPrefix
            ? mockPrefixTemp
            : `${mockPrefixTemp}/`;
        // mock文件处理
        mockFile = parsePath('./mock.js');
        if (!existsSync(mockFile)) {
            api.logger.info('mock.js File does not exist, please check');
            return;
        }
        // 清除require的缓存，保证 mock 文件修改后拿到最新的 mock.js
        if (require.cache[mockFile]) {
            delete require.cache[mockFile];
        }
        // require最新的 mock.js 文件
        try {
            // register babel
            registerBabel([mockFile]);
            const _initFunction = require(mockFile);
            const initFunction = _initFunction.default || _initFunction;
            if (!lodash.isFunction(initFunction)) {
                api.logger.info('mock.js should export Function');
                return;
            }
            initFunction({ cgiMock, mockjs, utils });
        } catch (err) {
            api.logger.error('mock.js run fail!');
        }

        return (req, res, next) => {
            // 如果请求不是以 cgiMock.prefix 开头，直接 next
            if (!req.path.startsWith(mockPrefix)) {
                return next();
            }
            // 请求以 cgiMock.prefix 开头，匹配处理
            const matchRequet = requestList.find(
                item => req.path.search(item.url) !== -1
            );
            if (!matchRequet) {
                return next();
            }

            const sendData = () => {
                // set header
                res.set(matchRequet.headers);
                // set Content-Type
                matchRequet.type && res.type(matchRequet.type);
                // set status code
                res.status(matchRequet.statusCode);
                // set cookie
                traversalHandler(matchRequet.cookies, (item) => {
                    const name = item.name;
                    const value = item.value;
                    delete item.name;
                    delete item.value;
                    res.cookie(name, value, item);
                });

                // do result
                if (lodash.isFunction(matchRequet.result)) {
                    matchRequet.result(req, res);
                } else if (
                    lodash.isArray(matchRequet.result)
                    || lodash.isPlainObject(matchRequet.result)
                ) {
                    !matchRequet.type && res.type('json');
                    res.json(matchRequet.result);
                } else {
                    !matchRequet.type && res.type('text');
                    res.send(matchRequet.result.toString());
                }
            };

            bodyParser.json({ strict: false })(req, res, () => {
                bodyParser.urlencoded({ extended: true })(req, res, () => {
                    cookieParser()(req, res, () => {
                        sendData();
                    });
                });
            });
        };
    };

    api.onStart(() => {
        // 获取mock配置: 是否打开
        mockFlag = lodash.isPlainObject(api.config.mock)
            ? true
            : api.config.mock;
        if (!mockFlag) return;

        loadMock = createMock();
        return chokidar
            .watch(mockFile, {
                ignoreInitial: true
            })
            .on('change', () => {
                api.logger.info('mock.js changed，reload');
                loadMock = createMock();
            });
    });

    api.addBeforeMiddlewares(() => (req, res, next) => {
        if (!mockFlag) return next();
        loadMock(req, res, next);
    });
};


<|start_filename|>packages/fes-runtime/src/index.js<|end_filename|>
// TODO 其他 API
export {
    useRoute,
    useRouter,
    onBeforeRouteUpdate,
    onBeforeRouteLeave,
    RouterLink,
    RouterView,
    useLink,
    createWebHashHistory,
    createWebHistory,
    createMemoryHistory,
    createRouter
} from 'vue-router';

export { default as Plugin, ApplyPluginsType } from './plugin';


<|start_filename|>packages/fes-compiler/src/service/enums.js<|end_filename|>
export const PluginType = {
    preset: 'preset',
    plugin: 'plugin'
};

export const ServiceStage = {
    uninitialized: 0,
    constructor: 1,
    init: 2,
    initPlugins: 3,
    initHooks: 4,
    pluginReady: 5,
    getConfig: 6,
    getPaths: 7,
    run: 8
};

export const ConfigChangeType = {
    reload: 'reload',
    regenerateTmpFiles: 'regenerateTmpFiles'
};

export const ApplyPluginsType = {
    add: 'add',
    modify: 'modify',
    event: 'event'
};

export const EnableBy = {
    register: 'register',
    config: 'config'
};


<|start_filename|>packages/fes-compiler/src/service/babelRegister.js<|end_filename|>
import {
    lodash,
    winPath
} from '@fesjs/utils';


export default class BabelRegister {
    only = {};

    setOnlyMap({
        key,
        value
    }) {
        this.only[key] = value;
        this.register();
    }

    register() {
        const only = lodash.uniq(
            Object.keys(this.only)
                .reduce((memo, key) => memo.concat(this.only[key]), [])
                .map(winPath)
        );
        require('@babel/register')({
            presets: [
                [
                    require.resolve('@babel/preset-env'),
                    {
                        targets: {
                            node: 'current'
                        },
                        modules: 'commonjs'
                    }
                ]
            ],
            ignore: [/node_modules/],
            only,
            extensions: ['.jsx', '.js', '.ts', '.tsx'],
            babelrc: false,
            cache: false
        });
    }
}


<|start_filename|>packages/fes-plugin-request/src/template/throttle.js<|end_filename|>

const throttleMap = new Map();

export default async (ctx, next) => {
    if (ctx.config.throttle) {
        if (throttleMap.get(ctx.key) >= Date.now()) {
            ctx.error = {
                type: 'FREQUENTLY',
                msg: '请求过于频繁'
            };
            return;
        }
    }
    await next();
    throttleMap.set(ctx.key, Date.now() + ctx.config.throttle);
};


<|start_filename|>packages/fes-plugin-icon/src/index.js<|end_filename|>
import { readFileSync } from 'fs';
import { join, basename } from 'path';
import optimizeSvg from './optimizeSvg';

export default (api) => {
    api.addRuntimePluginKey(() => '');
    // 配置
    api.describe({
        key: 'icon',
        config: {
            schema(joi) {
                return joi.object();
            }
        }
    });

    const namespace = 'plugin-icon';
    const absRuntimeFilePath = join(namespace, 'runtime.js');

    // 监听 icons 文件变更，重新生成文件
    api.addTmpGenerateWatcherPaths(() => join(api.paths.absSrcPath, 'icons'));

    let generatedOnce = false;
    api.onGenerateFiles(async () => {
        const base = join(api.paths.absSrcPath, 'icons');
        const iconFiles = api.utils.glob.sync('**/*', {
            cwd: join(api.paths.absSrcPath, 'icons')
        });
        const svgDatas = await optimizeSvg(iconFiles.map(item => join(base, item)));
        const iconNames = [];
        const SVG_COMPONENT_TMPLATE = 'export default () => (SVG)';
        for (const { fileName, data } of svgDatas) {
            iconNames.push(basename(fileName, '.svg'));
            api.writeTmpFile({
                path: `${namespace}/icons/${basename(fileName, '.svg')}.js`,
                content: SVG_COMPONENT_TMPLATE
                    .replace('SVG', data)
            });
        }

        api.writeTmpFile({
            path: `${namespace}/icons.js`,
            content: api.utils.Mustache.render(
                readFileSync(join(__dirname, 'runtime/icons.tpl'), 'utf-8'),
                {
                    ICON_NAMES: iconNames
                }
            )
        });

        api.writeTmpFile({
            path: absRuntimeFilePath,
            content: api.utils.Mustache.render(readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8'), {
            })
        });

        if (!generatedOnce) {
            generatedOnce = true;
            api.copyTmpFiles({
                namespace,
                path: join(__dirname, 'runtime'),
                ignore: ['.tpl']
            });
        }
    });

    api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
};


<|start_filename|>packages/fes-plugin-jest/src/index.js<|end_filename|>

import assert from 'assert';
import { join } from 'path';
import { existsSync } from 'fs';
import { Logger } from '@fesjs/compiler';
// jest-cli 不在暴露 options，维护一份本地的 options
import { options as CliOptions } from './jestArgs';
import createDefaultConfig from './createDefaultConfig';

const logger = new Logger('fes:plugin-unit-jest');

function getCommandOptiton() {
    const opts = [];
    Object.keys(CliOptions).forEach((key) => {
        const option = CliOptions[key];
        const opt = {};
        if (key !== 'version') {
            if (option.alias) {
                opt.name = `-${option.alias} --${key}`;
            } else {
                opt.name = `--${key}`;
            }
            opt.description = option.description;
            opts.push(opt);
        }
    });
    return opts;
}

export default function (api) {
    const { utils: { mergeConfig }, cwd } = api;

    api.registerCommand({
        command: 'test',
        description: 'run unit tests with jest',
        options: getCommandOptiton(),
        async fn({ args }) {
            process.env.NODE_ENV = 'test';

            if (args._[0] === 'test') {
                args._.shift();
            }

            args.debug && logger.log(`args: ${JSON.stringify(args)}`);

            // Read config from cwd/jest.config.js
            const userJestConfigFile = join(cwd, 'jest.config.js');
            const userJestConfig = existsSync(userJestConfigFile) && require(userJestConfigFile);
            args.debug && logger.log(`config from jest.config.js: ${JSON.stringify(userJestConfig)}`);

            // Read jest config from package.json
            const packageJSONPath = join(cwd, 'package.json');
            const packageJestConfig = existsSync(packageJSONPath) && require(packageJSONPath).jest;
            args.debug && logger.log(`jest config from package.json: ${JSON.stringify(packageJestConfig)}`);

            // Merge configs
            // user config and args config could have value function for modification
            const config = mergeConfig(
                createDefaultConfig(cwd, args),
                packageJestConfig,
                userJestConfig
            );
            args.debug && logger.log(`final config: ${JSON.stringify(config)}`);

            // Generate jest options
            const argsConfig = Object.keys(CliOptions).reduce((prev, name) => {
                if (args[name]) prev[name] = args[name];

                // Convert alias args into real one
                const { alias } = CliOptions[name];
                if (alias && args[alias]) prev[name] = args[alias];
                return prev;
            }, {});
            args.debug && logger.log(`config from args: ${JSON.stringify(argsConfig)}`);

            // 比较大的库建议使用require，使用时才加载，提升fes命令的效率
            const { runCLI } = require('jest');
            // Run jest
            const result = await runCLI(
                {
                    // @ts-ignore
                    _: args._ || [],
                    // @ts-ignore
                    $0: args.$0 || '',
                    // 必须是单独的 config 配置，值为 string，否则不生效
                    // @ts-ignore
                    config: JSON.stringify(config),
                    ...argsConfig
                },
                [cwd]
            );
            args.debug && logger.log(result);

            // Throw error when run failed
            assert(result.results.success, 'Test with jest failed');
        }
    });
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/alias.js<|end_filename|>
export default (api) => {
    const { paths } = api;

    api.describe({
        key: 'alias',
        config: {
            schema(joi) {
                return joi.object();
            },
            default: {
            }
        }
    });

    api.chainWebpack(async (memo) => {
        // 选择在 chainWebpack 中进行以上 alias 的初始化，是为了支持用户使用 modifyPaths API 对 paths 进行改写
        memo.resolve.alias.set('@', paths.absSrcPath);
        memo.resolve.alias.set('@@', paths.absTmpPath);

        return memo;
    });
};


<|start_filename|>packages/fes-template/src/locales/zh-CN.js<|end_filename|>

export default {
    test: '测试',
    home: '首页',
    'navBar.lang': '语言',
    'layout.user.link.help': '帮助',
    'layout.user.link.privacy': '隐私',
    'layout.user.link.terms': '条款',
    'app.preview.down.block': '下载此页面到本地项目',
    'app.welcome.link.fetch-blocks': '获取全部区块',
    'app.welcome.link.block-list': '基于 block 开发，快速构建标准页面'
};


<|start_filename|>packages/fes-runtime/src/utils/assert.js<|end_filename|>
export default function assert(value, message) {
    if (!value) throw new Error(message);
}


<|start_filename|>scripts/randomColor.js<|end_filename|>
/* eslint import/no-extraneous-dependencies: 0 */
const chalk = require('chalk');

const colors = [
    'red',
    'green',
    'yellow',
    'blue',
    'magenta',
    'cyan',
    'gray',
    'redBright',
    'greenBright',
    'yellowBright',
    'blueBright',
    'magentaBright',
    'cyanBright'
];

let index = 0;
const cache = {};

module.exports = function (pkg) {
    if (!cache[pkg]) {
        const color = colors[index];
        const str = chalk[color].bold(pkg);
        cache[pkg] = str;
        if (index === colors.length - 1) {
            index = 0;
        } else {
            index += 1;
        }
    }
    return cache[pkg];
};


<|start_filename|>packages/fes-template/src/models/user.js<|end_filename|>
import { ref } from 'vue';

export default function user() {
    const count = ref(1);
    return {
        count
    };
}


<|start_filename|>scripts/build.js<|end_filename|>
// 关闭 import 规则
/* eslint import/no-extraneous-dependencies: 0 */

const fs = require('fs');
const fse = require('fs-extra');
const path = require('path');
const merge = require('deepmerge');
const chokidar = require('chokidar');
const chalk = require('chalk');
const argv = require('yargs-parser')(process.argv.slice(2));

const compiler = require('./compiler');
const randomColor = require('./randomColor');


const ESM_OUTPUT_DIR = 'es';
const NODE_CJS_OUTPUT_DIR = 'lib';
const SOURCE_DIR = 'src';
const CONFIG_FILE_NAME = 'build.config.js';
const GLOBAL_CONFIG_PATH = path.join(process.cwd(), CONFIG_FILE_NAME);
const DEFAULT_CONFIG = {
    target: 'node',
    pkgs: [],
    copy: []
};

const PACKAGE_PATH = path.join(process.cwd(), './packages');

function genLog(pkgName) {
    return (msg) => {
        console.log(`${randomColor(pkgName)}: ${msg}`);
    };
}

function getPkgPath(pkgName) {
    return path.join(PACKAGE_PATH, pkgName);
}

function genShortPath(filePath) {
    const codePath = filePath.split(`/${SOURCE_DIR}/`)[1];
    return `${SOURCE_DIR}/${codePath}`;
}

function getPkgSourcePath(pkgName) {
    return path.join(getPkgPath(pkgName), SOURCE_DIR);
}

function getOutputPath(config, pkgName) {
    if (config.target === 'browser') {
        return path.join(getPkgPath(pkgName), ESM_OUTPUT_DIR);
    }

    return path.join(getPkgPath(pkgName), NODE_CJS_OUTPUT_DIR);
}

function getGlobalConfig() {
    if (fs.existsSync(GLOBAL_CONFIG_PATH)) {
        const userConfig = require(GLOBAL_CONFIG_PATH);
        return merge(DEFAULT_CONFIG, userConfig);
    }
    return DEFAULT_CONFIG;
}

function getPkgConfig(config, pkgName) {
    const pkgConfigPath = path.join(getPkgPath(pkgName), CONFIG_FILE_NAME);
    if (fs.existsSync(pkgConfigPath)) {
        return merge(config, require(pkgConfigPath));
    }

    return config;
}

function getNeedCompilerPkg(config) {
    // 用户通过 cli 指定的包，优先级最高
    if (argv.pkg) {
        return Array.isArray(argv.pkg) ? argv.pkg : argv.pkg;
    }
    // 默认编译所有 packages
    if (!config.pkgs?.length) {
        const pkgs = fs.readdirSync(PACKAGE_PATH);
        return pkgs;
    }

    return config.pkgs;
}

function cleanBeforeCompilerResult(pkgName, log) {
    const esmOutputDir = path.join(getPkgPath(pkgName), ESM_OUTPUT_DIR);
    const cjsOutputDir = path.join(getPkgPath(pkgName), NODE_CJS_OUTPUT_DIR);
    if (fs.existsSync(esmOutputDir)) {
        log(chalk.gray(`Clean ${ESM_OUTPUT_DIR} directory`));
        fse.removeSync(esmOutputDir);
    }
    if (fs.existsSync(cjsOutputDir)) {
        log(chalk.gray(`Clean ${NODE_CJS_OUTPUT_DIR} directory`));
        fse.removeSync(cjsOutputDir);
    }
}

function transformFile(filePath, outputPath, config, log) {
    if (/\.[jt]sx?$/.test(path.extname(filePath))) {
        const code = fs.readFileSync(filePath, 'utf-8');
        const shortFilePath = genShortPath(filePath);
        const transformedCode = compiler(code, config);

        const type = config.target === 'browser' ? ESM_OUTPUT_DIR : NODE_CJS_OUTPUT_DIR;
        log(`Transform to ${type} for ${config.target === 'browser' ? chalk.yellow(shortFilePath) : chalk.blue(shortFilePath)}`);
        fse.outputFileSync(outputPath, transformedCode);
    } else {
        fse.copySync(filePath, outputPath);
    }
}

function compilerPkg(codeDir, outputDir, config, log) {
    const files = fs.readdirSync(codeDir);
    files.forEach((file) => {
        const filePath = path.join(codeDir, file);
        const outputFilePath = path.join(outputDir, file);
        const fileStats = fs.lstatSync(filePath);
        if (config.copy.includes(file)) {
            fse.copySync(filePath, outputFilePath);
        } else if (fileStats.isDirectory(filePath) && !/__tests__/.test(file)) {
            fse.ensureDirSync(outputFilePath);
            compilerPkg(filePath, outputFilePath, config, log);
        } else if (fileStats.isFile(filePath)) {
            transformFile(filePath, outputFilePath, config, log);
        }
    });
}

function watchFile(dir, outputDir, config, log) {
    chokidar.watch(dir, {
        ignoreInitial: true
    }).on('all', (event, changeFile) => {
        // 修改的可能是一个目录，一个文件，一个需要 copy 的文件 or 目录
        const baseName = path.basename(changeFile);
        const shortChangeFile = genShortPath(changeFile);
        const outputPath = changeFile.replace(dir, outputDir);
        const stat = fs.lstatSync(changeFile);
        log(`[${event}] ${shortChangeFile}`);
        if (config.copy.includes(baseName)) {
            fse.copySync(changeFile, outputPath);
        } else if (stat.isFile()) {
            transformFile(changeFile, outputPath, config, log);
        } else if (stat.isDirectory()) {
            compilerPkg(changeFile, outputPath, config);
        }
    });
}

function compilerPkgs(pkgs, globalConfig) {
    pkgs.forEach((pkgName) => {
        const sourceCodeDir = getPkgSourcePath(pkgName);
        if (fs.existsSync(sourceCodeDir)) {
            const log = genLog(pkgName);
            const config = getPkgConfig(globalConfig, pkgName);
            const outputDir = getOutputPath(config, pkgName);

            cleanBeforeCompilerResult(pkgName, log);
            const type = config.target === 'browser' ? ESM_OUTPUT_DIR : NODE_CJS_OUTPUT_DIR;
            log(chalk.white(`Build ${type} with babel`));
            compilerPkg(sourceCodeDir, outputDir, config, log);
            if (argv.watch) {
                log(chalk.magenta(`Start watch ${SOURCE_DIR} directory...`));
                watchFile(sourceCodeDir, outputDir, config, log);
            }
        }
    });
}

function main() {
    const globalConfig = getGlobalConfig();
    const pkgs = getNeedCompilerPkg(globalConfig);

    compilerPkgs(pkgs, globalConfig);
}

main();


<|start_filename|>packages/fes-utils/src/Generator.js<|end_filename|>
import {
    copyFileSync, readFileSync, statSync, writeFileSync
} from 'fs';
import { dirname, relative, join } from 'path';

import chalk from 'chalk';
import mkdirp from 'mkdirp';
import glob from 'glob';
import Mustache from 'mustache';

class Generator {
  cwd;

  args;

  constructor({ cwd, args }) {
      this.cwd = cwd;
      this.args = args;
  }

  async run() {
      await this.writing();
  }

  // eslint-disable-next-line
  async writing() {}

  copyTpl(opts) {
      const tpl = readFileSync(opts.templatePath, 'utf-8');
      const content = Mustache.render(tpl, opts.context);
      mkdirp.sync(dirname(opts.target));
      console.log(`${chalk.green('Write:')} ${relative(this.cwd, opts.target)}`);
      writeFileSync(opts.target, content, 'utf-8');
  }

  copyDirectory(opts) {
      const files = glob.sync('**/*', {
          cwd: opts.path,
          dot: true,
          ignore: ['**/node_modules/**']
      });
      files.forEach((file) => {
          const absFile = join(opts.path, file);
          if (statSync(absFile).isDirectory()) return;
          if (file.endsWith('.tpl')) {
              this.copyTpl({
                  templatePath: absFile,
                  target: join(opts.target, file.replace(/\.tpl$/, '')),
                  context: opts.context
              });
          } else {
              console.log(`${chalk.green('Copy: ')} ${file}`);
              const absTarget = join(opts.target, file);
              mkdirp.sync(dirname(absTarget));
              copyFileSync(absFile, absTarget);
          }
      });
  }
}

export default Generator;


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/webpackConfig/define.js<|end_filename|>
import webpack from 'webpack';
import resolveDefine from './resolveDefine';

export default function createDefineWebpackConfig({
    config,
    webpackConfig
}) {
    webpackConfig.plugin('define')
        .use(webpack.DefinePlugin, [
            resolveDefine(config)
        ]);
}


<|start_filename|>packages/fes-utils/src/mergeConfig.js<|end_filename|>

export default function mergeConfig(defaultConfig, ...configs) {
    const ret = { ...defaultConfig };
    configs.forEach((config) => {
        if (!config) return;
        Object.keys(config).forEach((key) => {
            const val = config[key];
            if (typeof val === 'function') {
                ret[key] = val(ret[key]);
            } else {
                ret[key] = val;
            }
        });
    });
    return ret;
}


<|start_filename|>packages/fes-plugin-request/__tests__/request.js<|end_filename|>


test('button disabled', async () => {
    expect(1).toBe(1);
});


<|start_filename|>packages/fes-plugin-icon/src/runtime/Icon/index.js<|end_filename|>
import Icon from './Icon';
import './icon.less';

export default Icon;


<|start_filename|>packages/fes-preset-built-in/src/plugins/misc/route/index.js<|end_filename|>
import { readdirSync, statSync, readFileSync } from 'fs';
import {
    join, extname, posix, basename
} from 'path';
import { lodash } from '@fesjs/utils';
import { parse } from '@vue/compiler-sfc';
import { runtimePath } from '../../../utils/constants';

//   pages
//  ├── index.vue         # 根路由页面 路径 /
//  ├── *.vue             # 模糊匹配 路径 *
//  ├── a.vue             # 路径 /a
//  ├── b
//  │   ├── index.vue     # 路径 /b
//  │   ├── @id.vue       # 动态路由 /b/:id
//  │   └── c.vue         # 路径 /b/c
//  └── layout.vue        # 根路由下所有page共用的外层

const isProcessFile = function (path) {
    const ext = extname(path);
    return statSync(path).isFile() && ['.vue'].includes(ext);
};

const isProcessDirectory = function (path, item) {
    const component = join(path, item);
    return statSync(component).isDirectory() && !['components'].includes(item);
};

const checkHasLayout = function (path) {
    const dirList = readdirSync(path);
    return dirList.some((item) => {
        if (!isProcessFile(join(path, item))) {
            return false;
        }
        const ext = extname(item);
        const fileName = basename(item, ext);
        return fileName === 'layout';
    });
};

const getRouteName = function (parentRoutePath, fileName) {
    const routeName = posix.join(parentRoutePath, fileName);
    return routeName
        .slice(1)
        .replace(/\//g, '_')
        .replace(/@/g, '_')
        .replace(/\*/g, 'FUZZYMATCH');
};

const getComponentPath = function (parentRoutePath, fileName, config) {
    const pagesName = config.singular ? 'page' : 'pages';
    return posix.join(`@/${pagesName}/`, parentRoutePath, fileName);
};

const getRoutePath = function (parentRoutePath, fileName) {
    // /index.vue -> /
    if (fileName === 'index') {
        fileName = '';
    }
    // /@id.vue -> /:id
    if (fileName.startsWith('@')) {
        fileName = fileName.replace(/@/, ':');
    }
    // /*.vue -> :pathMatch(.*)
    if (fileName.includes('*')) {
        fileName = fileName.replace('*', ':pathMatch(.*)');
    }
    return posix.join(parentRoutePath, fileName);
};

// TODO 约定 layout 目录作为布局文件夹，
const genRoutes = function (parentRoutes, path, parentRoutePath, config) {
    const dirList = readdirSync(path);
    const hasLayout = checkHasLayout(path);
    const layoutRoute = {
        children: []
    };
    if (hasLayout) {
        layoutRoute.path = parentRoutePath;
        parentRoutes.push(layoutRoute);
    }
    dirList.forEach((item) => {
        // 文件或者目录的绝对路径
        const component = join(path, item);
        if (isProcessFile(component)) {
            const { descriptor } = parse(readFileSync(component, 'utf-8'));
            const routeMetaBlock = descriptor.customBlocks.find(
                b => b.type === 'config'
            );
            const ext = extname(item);
            const fileName = basename(item, ext);
            // 路由的path
            const routePath = getRoutePath(parentRoutePath, fileName);
            // 路由名称
            const routeName = getRouteName(parentRoutePath, fileName);
            const componentPath = getComponentPath(parentRoutePath, fileName, config);
            const routeMeta = routeMetaBlock?.content ? JSON.parse(routeMetaBlock.content) : {};
            const routeConfig = {
                path: routePath,
                component: componentPath,
                name: routeMeta.name || routeName,
                meta: routeMeta
            };
            if (hasLayout) {
                if (fileName === 'layout') {
                    layoutRoute.component = componentPath;
                } else {
                    layoutRoute.children.push(routeConfig);
                }
            } else {
                parentRoutes.push(routeConfig);
            }
        }
    });

    dirList.forEach((item) => {
        if (isProcessDirectory(path, item)) {
            // 文件或者目录的绝对路径
            const component = join(path, item);
            const nextParentRouteUrl = posix.join(parentRoutePath, item);
            if (hasLayout) {
                genRoutes(layoutRoute.children, component, nextParentRouteUrl, config);
            } else {
                genRoutes(parentRoutes, component, nextParentRouteUrl, config);
            }
        }
    });
};

/**
 * 智能路由
 * 1、路由的路径是多个“/”组成的字符串，使用“/”分割后得到不同的子项
 * 2、计算子项个数，用个数乘以4，计入得分
 * 3、判断子项是否是静态的，即不包含“：”、“*”等特殊字符串，若是计入3分。
 * 4、判断子项是否是动态的，即包含“：”特殊字符，若是计入2分。
 * 5、判断子项是否是模糊匹配，即包含“*”特殊字符，若是扣除1分。
 * 6、判断子项是否是根端，即只是“/”，若是计入1分。

 * @param {*} routes
 */
const rank = function (routes) {
    routes.forEach((item) => {
        const path = item.path;
        let arr = path.split('/');
        if (arr[0] === '') {
            arr = arr.slice(1);
        }
        let count = 0;
        arr.forEach((sonPath) => {
            count += 4;
            if (sonPath.indexOf(':') !== -1 && sonPath.indexOf(':pathMatch(.*)') === -1) {
                count += 2;
            } else if (sonPath.indexOf(':pathMatch(.*)') !== -1) {
                count -= 1;
            } else if (sonPath === '') {
                count += 1;
            } else {
                count += 3;
            }
        });
        item.count = count;
        if (item.children && item.children.length) {
            rank(item.children);
        }
    });
    routes.sort((a, b) => b.count - a.count);
};

const getRoutes = function ({ config, absPagesPath }) {
    // 用户配置了routes则使用用户配置的
    const configRoutes = config.router.routes;
    if (configRoutes && configRoutes.length > 0) return configRoutes;

    const routes = [];
    genRoutes(routes, absPagesPath, '/', config);
    rank(routes);
    return routes;
};

const getRoutesJSON = function ({ routes, config }) {
    // 因为要往 routes 里加无用的信息，所以必须 deep clone 一下，避免污染
    const clonedRoutes = lodash.cloneDeep(routes);

    function isFunctionComponent(component) {
        // eslint-disable-next-line
        return (
            /^\((.+)?\)(\s+)?=>/.test(component)
            || /^function([^(]+)?\(([^)]+)?\)([^{]+)?{/.test(component)
        );
    }

    function replacer(key, value) {
        switch (key) {
            case 'component':
                if (isFunctionComponent(value)) return value;
                if (config.dynamicImport) {
                    // TODO 针对目录进行 chunk 划分，import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
                    return `() => import('${value}')`;
                }
                return `require('${value}').default`;
            default:
                return value;
        }
    }

    return JSON.stringify(clonedRoutes, replacer, 2)
        .replace(
            /"component": ("(.+?)")/g,
            (global, m1, m2) => `"component": ${m2.replace(/\^/g, '"')}`
        )
        .replace(/\\r\\n/g, '\r\n')
        .replace(/\\n/g, '\r\n');
};

export default function (api) {
    api.describe({
        key: 'router',
        config: {
            schema(joi) {
                return joi
                    .object({
                        routes: joi.array(),
                        mode: joi.string()
                    });
            },
            default: {
                mode: 'hash'
            }
        }
    });

    api.registerMethod({
        name: 'getRoutes',
        async fn() {
            return api.applyPlugins({
                key: 'modifyRoutes',
                type: api.ApplyPluginsType.modify,
                initialValue: getRoutes({
                    config: api.config,
                    absPagesPath: api.paths.absPagesPath
                })
            });
        }
    });

    api.registerMethod({
        name: 'getRoutesJSON',
        async fn() {
            const routes = await api.getRoutes();
            return getRoutesJSON({ routes, config: api.config });
        }
    });

    const {
        utils: { Mustache }
    } = api;

    const namespace = 'core/routes';

    const absCoreFilePath = join(namespace, 'routes.js');

    const absRuntimeFilePath = join(namespace, 'runtime.js');

    const historyType = {
        history: 'createWebHistory',
        hash: 'createWebHashHistory',
        memory: 'createMemoryHistory'
    };

    api.onGenerateFiles(async () => {
        const routesTpl = readFileSync(join(__dirname, 'template/routes.tpl'), 'utf-8');
        const routes = await api.getRoutesJSON();

        api.writeTmpFile({
            path: absCoreFilePath,
            content: Mustache.render(routesTpl, {
                runtimePath,
                routes,
                config: api.config,
                routerBase: api.config.base,
                CREATE_HISTORY: historyType[api.config.router.mode] || 'createWebHashHistory'
            })
        });

        api.writeTmpFile({
            path: absRuntimeFilePath,
            content: readFileSync(join(__dirname, 'template/runtime.tpl'), 'utf-8')
        });
    });

    api.addCoreExports(() => [
        {
            specifiers: ['getRoutes', 'getRouter', 'getHistory', 'destroyRouter'],
            source: absCoreFilePath
        }
    ]);

    api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/analyze.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'analyze',
        config: {
            schema(joi) {
                return joi
                    .object({
                        analyzerMode: joi.string().valid('server', 'static', 'disabled'),
                        analyzerHost: joi.string(),
                        analyzerPort: joi.alternatives(joi.number(), 'auto'),
                        openAnalyzer: joi.boolean(),
                        generateStatsFile: joi.boolean(),
                        statsFilename: joi.string(),
                        logLevel: joi.string().valid('info', 'warn', 'error', 'silent'),
                        defaultSizes: joi.string().valid('stat', 'parsed', 'gzip')
                    })
                    .unknown(true);
            },
            default: {
                analyzerMode: process.env.ANALYZE_MODE || 'server',
                analyzerPort: process.env.ANALYZE_PORT || 8888,
                openAnalyzer: process.env.ANALYZE_OPEN !== 'none',
                // generate stats file while ANALYZE_DUMP exist
                generateStatsFile: !!process.env.ANALYZE_DUMP,
                statsFilename: process.env.ANALYZE_DUMP || 'stats.json',
                logLevel: process.env.ANALYZE_LOG_LEVEL || 'info',
                defaultSizes: 'parsed' // stat  // gzip
            }
        },
        enableBy: () => !!process.env.ANALYZE
    });
    api.chainWebpack((webpackConfig) => {
        webpackConfig
            .plugin('bundle-analyzer')
            .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [
                api.config?.analyze || {}
            ]);
        return webpackConfig;
    });
};


<|start_filename|>.eslintrc.js<|end_filename|>

module.exports = {
    extends: [
        '@webank/eslint-config-webank/vue.js'
    ],
    globals: {
        // 这里填入你的项目需要的全局变量
        // 这里值为 false 表示这个全局变量不允许被重新赋值，比如：
        //
        // Vue: false
        __DEV__: false
    },
    rules: {
        'vue/comment-directive': 'off',
        'global-require': 'off',
        'import/no-unresolved': 'off',
        'no-restricted-syntax': 'off',
        'no-undefined': 'off',
        'vue/valid-template-root': 'off'
    },
    env: {
        jest: true
    }
};


<|start_filename|>packages/fes-plugin-request/src/template/paramsProcess.js<|end_filename|>
import { checkHttpRequestHasBody, trimObj } from './helpers';

export default async (ctx, next) => {
    const config = ctx.config;
    if (checkHttpRequestHasBody(config.method)) {
        trimObj(config.data);
    } else {
        trimObj(config.params);
    }
    await next();
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/proxy.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'proxy',
        config: {
            onChange: () => {
                // todo 重新执行proxy的逻辑
            },
            schema(joi) {
                return joi.object();
            }
        }
    });
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/html.js<|end_filename|>
export default (api) => {
    api.describe({
        key: 'html',
        config: {
            schema(joi) {
                return joi
                    .object()
                    .description(
                        'more html-webpack-plugin options see https://github.com/jantimon/html-webpack-plugin#configuration'
                    );
            },
            default: {
            }
        }
    });
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/dev/devServer.js<|end_filename|>
import WebpackDevServer from 'webpack-dev-server';
import webpack from 'webpack';
import fs from 'fs';
import path from 'path';


export function startDevServer({
    webpackConfig,
    host,
    port,
    proxy,
    https,
    beforeMiddlewares,
    afterMiddlewares,
    customerDevServerConfig
}) {
    const options = {
        contentBase: webpackConfig.output.path,
        hot: true,
        host,
        sockHost: host,
        sockPort: port,
        proxy,
        compress: true,
        noInfo: true,
        disableHostCheck: true,
        clientLogLevel: 'silent',
        stats: 'errors-only',
        before: (app) => {
            beforeMiddlewares.forEach((middleware) => {
                app.use(middleware);
            });
        },
        after: (app) => {
            afterMiddlewares.forEach((middleware) => {
                app.use(middleware);
            });
        },
        headers: {
            'access-control-allow-origin': '*'
        },
        ...(customerDevServerConfig || {})
    };
    if (https) {
        options.https = true;
        options.key = fs.readFileSync(path.resolve(__dirname, './cert/key.pem'));
        options.cert = fs.readFileSync(path.resolve(__dirname, './cert/cert.pem'));
    }
    WebpackDevServer.addDevServerEntrypoints(webpackConfig, options);
    const compiler = webpack(webpackConfig);
    const server = new WebpackDevServer(compiler, options);

    server.listen(port, host, (err) => {
        if (err) {
            console.error(err);
        }
    });
    return server;
}


<|start_filename|>packages/fes-compiler/src/service/utils/pluginUtils.js<|end_filename|>
import {
    dirname, join, basename, relative, extname
} from 'path';
import {
    compatESModuleRequire,
    resolve,
    winPath,
    pkgUp,
    lodash
} from '@fesjs/utils';

import { PluginType } from '../enums';

const RE = {
    [PluginType.plugin]: /^(@fesjs\/|@webank\/fes-|fes-)plugin-/,
    [PluginType.preset]: /^(@fesjs\/|@webank\/fes-|fes-)preset-/
};

export function isPluginOrPreset(type, name) {
    const hasScope = name.charAt(0) === '@';
    const re = RE[type];
    if (hasScope) {
        return re.test(name.split('/')[1]) || re.test(name);
    }
    return re.test(name);
}

export function getPluginsOrPresets(type, opts) {
    const upperCaseType = type.toUpperCase();
    return [
        // dependencies
        // opts
        ...((opts[type === PluginType.preset ? 'presets' : 'plugins']) || []),
        // env
        ...(process.env[`FES_${upperCaseType}S`] || '').split(',').filter(Boolean),
        ...Object.keys(opts.pkg.devDependencies || {})
            .concat(Object.keys(opts.pkg.dependencies || {}))
            .filter(isPluginOrPreset.bind(null, type)),
        // user config
        ...((opts[
            type === PluginType.preset ? 'userConfigPresets' : 'userConfigPlugins'
        ]) || [])
    ].map(path => resolve.sync(path, {
        basedir: opts.cwd,
        extensions: ['.js', '.ts']
    }));
}

// e.g.
// initial-state -> initialState
// webpack.css-loader -> webpack.cssLoader
function nameToKey(name) {
    return name
        .split('.')
        .map(part => lodash.camelCase(part))
        .join('.');
}

function pkgNameToKey(pkgName, type) {
    if (pkgName.charAt(0) === '@' && !pkgName.startsWith('@fesjs/')) {
        pkgName = pkgName.split('/')[1];
    }
    return nameToKey(pkgName.replace(RE[type], ''));
}

export function pathToObj({ path, type, cwd }) {
    let pkg = null;
    let isPkgPlugin = false;
    const pkgJSONPath = pkgUp.sync({ cwd: path });
    if (pkgJSONPath) {
        // eslint-disable-next-line
        pkg = require(pkgJSONPath);
        isPkgPlugin = winPath(join(dirname(pkgJSONPath), pkg.main || 'index.js'))
            === winPath(path);
    }

    let id;
    if (isPkgPlugin) {
        id = pkg.name;
    } else if (winPath(path).startsWith(winPath(cwd))) {
        id = `./${winPath(relative(cwd, path))}`;
    } else if (pkgJSONPath) {
        id = winPath(join(pkg.name, relative(dirname(pkgJSONPath), path)));
    } else {
        id = winPath(path);
    }
    id = id.replace('@fesjs/preset-built-in/lib/plugins', '@@');
    id = id.replace(/\.js$/, '');

    const key = isPkgPlugin
        ? pkgNameToKey(pkg.name, type)
        : nameToKey(basename(path, extname(path)));

    return {
        id,
        key,
        path: winPath(path),
        apply() {
            // use function to delay require
            try {
                // eslint-disable-next-line
                const ret = require(path);
                // use the default member for es modules
                return compatESModuleRequire(ret);
            } catch (e) {
                throw new Error(`Register ${path} failed, since ${e.message}`);
            }
        },
        defaultConfig: null
    };
}

export function resolvePresets(opts) {
    const type = PluginType.preset;
    const presets = [...getPluginsOrPresets(type, opts)];
    return presets.map(path => pathToObj({
        type,
        path,
        cwd: opts.cwd
    }));
}

export function resolvePlugins(opts) {
    const type = PluginType.plugin;
    const plugins = getPluginsOrPresets(type, opts);
    return plugins.map(path => pathToObj({
        path,
        type,
        cwd: opts.cwd
    }));
}

export function isValidPlugin(plugin) {
    return plugin.id && plugin.key && plugin.apply;
}


<|start_filename|>packages/fes-plugin-model/src/utils/index.js<|end_filename|>
import path from 'path';
import { EOL } from 'os';
import { readFileSync } from 'fs';
import { parser, traverse, winPath } from '@fesjs/utils';

const getFileName = (name) => {
    const fileName = path.basename(name, path.extname(name));
    if (fileName.endsWith('.model') || fileName.endsWith('.models')) {
        return fileName.split('.').slice(0, -1).join('.');
    }
    return fileName;
};

export const getName = (absPath, absSrcPath) => {
    const relativePath = path.relative(absSrcPath, absPath);
    // model files with namespace
    const dirList = path.dirname(relativePath).split(path.sep);
    try {
        const validDirs = dirList.filter(
            ele => !['src', 'page', 'pages', 'model', 'models'].includes(ele)
        );
        if (validDirs && validDirs.length) {
            return `${validDirs.join('.')}.${getFileName(relativePath)}`;
        }
        return getFileName(relativePath);
    } catch (e) {
        return getFileName(relativePath);
    }
};

export const getPath = (absPath) => {
    const info = path.parse(absPath);
    return winPath(path.join(info.dir, info.name).replace(/'/, "'"));
};

export const genImports = imports => imports
    .map(
        (ele, index) => `import model${index} from "${winPath(getPath(ele))}";`
    )
    .join(EOL);

export const genExtraModels = (models = [], absSrcPath) => models.map((ele) => {
    if (typeof ele === 'string') {
        return {
            importPath: getPath(ele),
            importName: path.basename(ele).split('.')[0],
            namespace: getName(ele, absSrcPath)
        };
    }
    return {
        importPath: getPath(ele.absPath),
        importName: path.basename(ele.absPath).split('.')[0],
        namespace: ele.namespace,
        exportName: ele.exportName
    };
});

export const sort = (ns) => {
    let final = [];
    ns.forEach((item, index) => {
        if (item.use && item.use.length) {
            const itemGroup = [...item.use, item.namespace];

            const cannotUse = [item.namespace];
            for (let i = 0; i <= index; i += 1) {
                if (ns[i].use.filter(v => cannotUse.includes(v)).length) {
                    if (!cannotUse.includes(ns[i].namespace)) {
                        cannotUse.push(ns[i].namespace);
                        i = -1;
                    }
                }
            }

            const errorList = item.use.filter(v => cannotUse.includes(v));
            if (errorList.length) {
                throw Error(
                    `Circular dependencies: ${
                        item.namespace
                    } can't use ${errorList.join(', ')}`
                );
            }

            const intersection = final.filter(v => itemGroup.includes(v));
            if (intersection.length) {
                // first intersection
                const finalIndex = final.indexOf(intersection[0]);
                // replace with groupItem
                final = final
                    .slice(0, finalIndex)
                    .concat(itemGroup)
                    .concat(final.slice(finalIndex + 1));
            } else {
                final.push(...itemGroup);
            }
        }
        if (!final.includes(item.namespace)) {
            // first occurance append to the end
            final.push(item.namespace);
        }
    });

    return [...new Set(final)];
};

export const genModels = (imports, absSrcPath) => {
    const contents = imports.map(absPath => ({
        namespace: getName(absPath, absSrcPath),
        content: readFileSync(absPath).toString()
    }));
    const allUserModel = imports.map(absPath => getName(absPath, absSrcPath));

    const checkDuplicates = list => new Set(list).size !== list.length;

    const raw = contents.map((ele, index) => {
        const ast = parser.parse(ele.content, {
            sourceType: 'module',
            plugins: ['jsx', 'typescript']
        });

        const use = [];

        traverse(ast, {
            enter(astPath) {
                if (astPath.isIdentifier({ name: 'useModel' })) {
                    try {
                        // string literal
                        const ns = astPath.parentPath.node.arguments[0].value;
                        if (allUserModel.includes(ns)) {
                            use.push(ns);
                        }
                    } catch (e) {
                        // console.log(e)
                    }
                }
            }
        });

        return { namespace: ele.namespace, use, importName: `model${index}` };
    });

    const models = sort(raw);

    if (checkDuplicates(contents.map(ele => ele.namespace))) {
        throw Error('plugin-model: models 中包含重复的 namespace！');
    }
    return raw.sort(
        (a, b) => models.indexOf(a.namespace) - models.indexOf(b.namespace)
    );
};

export const isValidHook = (filePath) => {
    const content = readFileSync(filePath, { encoding: 'utf-8' }).toString();

    const ast = parser.parse(content, {
        sourceType: 'module',
        plugins: [
            'classProperties',
            'dynamicImport',
            'exportDefaultFrom',
            'exportNamespaceFrom',
            'functionBind',
            'nullishCoalescingOperator',
            'objectRestSpread',
            'optionalChaining',
            'decorators-legacy'
        ].filter(Boolean)
    });
    let valid = false;
    let identifierName = '';
    traverse(ast, {
        enter(p) {
            if (p.isExportDefaultDeclaration()) {
                const { type } = p.node.declaration;
                try {
                    if (
                        type === 'ArrowFunctionExpression'
                        || type === 'FunctionDeclaration'
                    ) {
                        valid = true;
                    } else if (type === 'Identifier') {
                        identifierName = p.node.declaration.name;
                    }
                } catch (e) {
                    console.error(e);
                }
            }
        }
    });

    try {
        if (identifierName) {
            ast.program.body.forEach((ele) => {
                if (ele.type === 'FunctionDeclaration') {
                    if (ele.id?.name === identifierName) {
                        valid = true;
                    }
                }
                if (ele.type === 'VariableDeclaration') {
                    if (
                        ele.declarations[0].id.name === identifierName
                        && ele.declarations[0].init.type
                            === 'ArrowFunctionExpression'
                    ) {
                        valid = true;
                    }
                }
            });
        }
    } catch (e) {
        valid = false;
    }

    return valid;
};

export const getValidFiles = (files, modelsDir) => files
    .map((file) => {
        const filePath = path.join(modelsDir, file);
        const valid = isValidHook(filePath);
        if (valid) {
            return filePath;
        }
        return '';
    })
    .filter(ele => !!ele);


<|start_filename|>packages/fes/src/index.js<|end_filename|>

export * from '@@/core/coreExports';
// @ts-ignore
export * from '@@/core/pluginExports';


<|start_filename|>packages/fes-utils/src/winPath.js<|end_filename|>
export default function (path) {
    const isExtendedLengthPath = /^\\\\\?\\/.test(path);
    if (isExtendedLengthPath) {
        return path;
    }

    return path.replace(/\\/g, '/');
}


<|start_filename|>packages/fes-plugin-request/src/template/scheduler.js<|end_filename|>

class Scheduler {
    constructor() {
        this.middlewares = [];
    }

    use(fn) {
        if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');
        this.middlewares.push(fn);
        return this;
    }

    compose() {
        return (context, next) => {
            let index = -1;
            const dispatch = (i) => {
                if (i <= index) return Promise.reject(new Error('next() called multiple times'));
                index = i;
                let fn = this.middlewares[i];
                if (index === this.middlewares.length) fn = next;
                if (!fn) return Promise.resolve();
                try {
                    return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
                } catch (e) {
                    return Promise.reject(e);
                }
            };
            return dispatch(0);
        };
    }
}

export default new Scheduler();


<|start_filename|>packages/fes-runtime/src/plugin/index.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/blob/master/packages/runtime/src/Plugin/Plugin.ts
 */

import { assert } from '../utils';

function _compose({ fns, args }) {
    if (fns.length === 1) {
        return fns[0];
    }
    const last = fns.pop();
    return fns.reduce((a, b) => () => b(a, args), last);
}

function isPromiseLike(obj) {
    return !!obj && typeof obj === 'object' && typeof obj.then === 'function';
}

export const ApplyPluginsType = {
    compose: 'compose',
    event: 'event',
    modify: 'modify'
};

export default class Plugin {
    constructor(opts) {
        this.validKeys = opts?.validKeys || [];
        this.hooks = {};
        // 共享
        this.shared = {};
    }

    share(key, obj) {
        assert(!Object.keys(this.shared).includes(key), 'share failed, key repeat');
        this.shared[key] = obj;
    }

    getShared(key) {
        return this.shared[key];
    }

    register(plugin) {
        assert(!!plugin.apply, 'register failed, plugin.apply must supplied');
        assert(!!plugin.path, 'register failed, plugin.path must supplied');
        Object.keys(plugin.apply).forEach((key) => {
            assert(
                this.validKeys.indexOf(key) > -1,
                `register failed, invalid key ${key} from plugin ${plugin.path}.`
            );
            if (!this.hooks[key]) this.hooks[key] = [];
            this.hooks[key] = this.hooks[key].concat(plugin.apply[key]);
        });
    }

    getHooks(keyWithDot) {
        const [key, ...memberKeys] = keyWithDot.split('.');
        let hooks = this.hooks[key] || [];
        if (memberKeys.length) {
            hooks = hooks
                .map((hook) => {
                    try {
                        let ret = hook;
                        for (const memberKey of memberKeys) {
                            ret = ret[memberKey];
                        }
                        return ret;
                    } catch (e) {
                        return null;
                    }
                })
                .filter(Boolean);
        }
        return hooks;
    }

    applyPlugins({
        key,
        type,
        initialValue,
        args,
        async
    }) {
        const hooks = this.getHooks(key) || [];

        if (args) {
            assert(
                typeof args === 'object',
                'applyPlugins failed, args must be plain object.'
            );
        }

        switch (type) {
            case ApplyPluginsType.modify:
                if (async) {
                    return hooks.reduce(
                        async (memo, hook) => {
                            assert(typeof hook === 'function' || typeof hook === 'object' || isPromiseLike(hook),
                                `applyPlugins failed, all hooks for key ${key} must be function, plain object or Promise.`);
                            if (isPromiseLike(memo)) {
                                memo = await memo;
                            }
                            if (typeof hook === 'function') {
                                const ret = hook(memo, args);
                                if (isPromiseLike(ret)) {
                                    return ret;
                                }
                                return ret;
                            }
                            if (isPromiseLike(hook)) {
                                hook = await hook;
                            }
                            return { ...memo, ...hook };
                        },
                        isPromiseLike(initialValue)
                            ? initialValue
                            : Promise.resolve(initialValue)
                    );
                }
                return hooks.reduce((memo, hook) => {
                    assert(
                        typeof hook === 'function' || typeof hook === 'object',
                        `applyPlugins failed, all hooks for key ${key} must be function or plain object.`
                    );
                    if (typeof hook === 'function') {
                        return hook(memo, args);
                    }
                    return { ...memo, ...hook };
                }, initialValue);


            case ApplyPluginsType.event:
                return hooks.forEach((hook) => {
                    assert(
                        typeof hook === 'function',
                        `applyPlugins failed, all hooks for key ${key} must be function.`
                    );
                    hook(args);
                });

            case ApplyPluginsType.compose:
                return () => _compose({
                    fns: hooks.concat(initialValue),
                    args
                })();
            default:
                return null;
        }
    }
}


<|start_filename|>packages/fes-plugin-locale/src/runtime/runtime.js<|end_filename|>
import { install } from './core';

export function onAppCreated({ app }) {
    install(app);
}


<|start_filename|>packages/fes-plugin-qiankun/src/main/index.js<|end_filename|>
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import {
    defaultMainRootId,
    defaultHistoryType,
    qiankunStateForMicroModelNamespace
} from '../constants';
import modifyRoutes from './modifyRoutes';

const namespace = 'plugin-qiankun/main';

export function isMasterEnable(api) {
    return (
        !!api.userConfig?.qiankun?.main
        || !!process.env.INITIAL_QIANKUN_MAIN_OPTIONS
    );
}

export default function (api) {
    const {
        utils: { Mustache, winPath }
    } = api;

    api.describe({
        enableBy: () => isMasterEnable(api)
    });

    api.modifyDefaultConfig(config => ({
        ...config,
        mountElementId: defaultMainRootId
    }));

    modifyRoutes({ api, namespace });

    const absMicroAppPath = join(namespace, 'MicroApp.js');
    const absMicroAppWithMemoHistoryPath = join(namespace, 'MicroAppWithMemoHistory.js');
    const absRuntimePath = join(namespace, 'runtime.js');
    const absMasterOptionsPath = join(namespace, 'masterOptions.js');
    const absGetMicroAppRouteCompPath = join(
        namespace,
        'getMicroAppRouteComponent.js'
    );

    api.onGenerateFiles(() => {
        const HAS_PLUGIN_MODEL = api.hasPlugins(['@fesjs/plugin-model']);
        api.writeTmpFile({
            path: absMicroAppPath,
            content: Mustache.render(
                readFileSync(join(__dirname, 'runtime/MicroApp.tpl'), 'utf-8'),
                {
                    qiankunStateForMicroModelNamespace,
                    HAS_PLUGIN_MODEL:
                        HAS_PLUGIN_MODEL
                        && existsSync(
                            winPath(
                                join(
                                    api.paths.absSrcPath,
                                    'models/qiankunStateForMicro.js'
                                )
                            )
                        )
                }
            )
        });

        api.writeTmpFile({
            path: absMicroAppWithMemoHistoryPath,
            content: Mustache.render(
                readFileSync(join(__dirname, 'runtime/MicroAppWithMemoHistory.tpl'), 'utf-8'),
                {
                }
            )
        });

        api.writeTmpFile({
            path: absRuntimePath,
            content: readFileSync(
                join(__dirname, 'runtime/runtime.tpl'),
                'utf-8'
            )
        });

        api.writeTmpFile({
            path: absGetMicroAppRouteCompPath,
            content: readFileSync(
                join(__dirname, 'runtime/getMicroAppRouteComponent.tpl'),
                'utf-8'
            )
        });

        const { main: options } = api.config?.qiankun || {};
        const masterHistoryType = api.config?.router?.mode || defaultHistoryType;
        const base = api.config.base;
        api.writeTmpFile({
            path: absMasterOptionsPath,
            content: `
            let options = ${JSON.stringify({
        masterHistoryType,
        base,
        ...options
    })};
            export const getMasterOptions = () => options;
            export const setMasterOptions = (newOpts) => options = ({ ...options, ...newOpts });
            `
        });
    });

    api.addPluginExports(() => [
        {
            specifiers: ['MicroApp'],
            source: absMicroAppPath
        }
    ]);

    api.addPluginExports(() => [
        {
            specifiers: ['MicroAppWithMemoHistory'],
            source: absMicroAppWithMemoHistoryPath
        }
    ]);

    api.addPluginExports(() => [
        {
            specifiers: ['getMicroAppRouteComponent'],
            source: absGetMicroAppRouteCompPath
        }
    ]);
}


<|start_filename|>packages/fes-plugin-request/src/template/resDataAdaptor.js<|end_filename|>
import { isFunction, isObject, isString } from './helpers';

export default async ({ response, responseDataAdaptor }, next) => {
    if (isFunction(responseDataAdaptor) && response && (isObject(response.data) || isString(response.data))) {
        response.data = responseDataAdaptor(response.data);
    }
    await next();
};


<|start_filename|>packages/fes/src/serviceWithBuiltIn.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/blob/master/packages/umi/src/ServiceWithBuiltIn.ts
 */

import { dirname } from 'path';
import { Service as CoreService } from '@fesjs/compiler';

class Service extends CoreService {
    constructor(opts) {
        process.env.FES_VERSION = require('../package').version;
        process.env.FES_DIR = dirname(require.resolve('../package'));

        super({
            ...opts,
            presets: [
                require.resolve('@fesjs/preset-built-in'),
                ...(opts.presets || [])
            ],
            plugins: [...(opts.plugins || [])]
        });
    }
}

export { Service };


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/webpack/index.js<|end_filename|>
export default function (api) {
    api.registerCommand({
        command: 'webpack',
        description: 'inspect webpack configurations',
        options: [{
            name: '--rule <ruleName>',
            description: 'inspect a specific module rule'
        }, {
            name: '--plugin <pluginName>',
            description: 'inspect a specific plugin'
        }, {
            name: '--rules',
            description: 'list all module rule names'
        }, {
            name: '--plugins',
            description: 'list all plugin names'
        }, {
            name: '--verbose',
            description: 'show full function definitions in output'
        }],
        async fn({ options }) {
            const assert = require('assert');
            const { getBundleAndConfigs } = require('../buildDevUtils');
            const { toString } = require('webpack-chain');
            const { highlight } = require('cli-highlight');
            const { bundleConfig } = await getBundleAndConfigs({ api });

            let config = bundleConfig;
            assert(config, 'No valid config found with fes entry.');

            if (options.rule) {
                config = config.module.rules.find(
                    r => r.__ruleNames[0] === options.rule
                );
            } else if (options.plugin) {
                config = config.plugins.find(
                    p => p.__pluginName === options.plugin
                );
            } else if (options.rules) {
                config = config.module.rules.map(r => r.__ruleNames[0]);
            } else if (options.plugins) {
                config = config.plugins.map(
                    p => p.__pluginName || p.constructor.name
                );
            }

            console.log(highlight(toString(config, { verbose: options.verbose }), { language: 'js' }));
        }
    });
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/targets.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'targets',
        config: {
            default: {
                chrome: 49,
                firefox: 64,
                safari: 10,
                edge: 13,
                ios: 10
            },
            schema(joi) {
                return joi.object();
            }
        }
    });
};


<|start_filename|>packages/fes-utils/src/parseRequireDeps.js<|end_filename|>

// @ts-ignore
import crequire from 'crequire';
import lodash from 'lodash';
import resolve from 'resolve';
import { readFileSync } from 'fs';
import { dirname } from 'path';
import winPath from './winPath';

function parse(filePath) {
    const content = readFileSync(filePath, 'utf-8');
    return (crequire(content))
        .map(o => o.path)
        .filter(path => path.charAt(0) === '.')
        .map(path => winPath(
            resolve.sync(path, {
                basedir: dirname(filePath),
                extensions: ['.tsx', '.ts', '.jsx', '.js']
            })
        ));
}

export default function parseRequireDeps(filePath) {
    const paths = [filePath];
    const ret = [winPath(filePath)];

    while (paths.length) {
    // 避免依赖循环
        const extraPaths = lodash.pullAll(parse(paths.shift()), ret);
        if (extraPaths.length) {
            paths.push(...extraPaths);
            ret.push(...extraPaths);
        }
    }

    return ret;
}


<|start_filename|>packages/fes-plugin-request/src/template/resErrorProcess.js<|end_filename|>
import { isObject } from './helpers';

// 错误处理等副作用网上提
export default async (ctx, next) => {
    const {
        response,
        config
    } = ctx;
    if (!config.closeResDataCheck && response && isObject(response.data)) {
        const code = response.data.code;
        if (code !== '0') {
            ctx.error = response; // code 不为零进入 reject
        }
    }

    await next();
};


<|start_filename|>packages/fes-template/mock.js<|end_filename|>
export default function ({ cgiMock, mockjs, utils }) {
    const { Random } = mockjs;

    // 测试 proxy 与 mock 用例集合
    cgiMock('/movie/in_theaters_mock', (req, res) => {
        res.send(JSON.stringify({
            code: '0',
            msg: '',
            result: {
                text: 'movie:  movie/in_theaters_mock ~~~~~'
            }
        }));
    });
    cgiMock('/movie/test_mock', (req, res) => {
        res.send(JSON.stringify({
            code: '0',
            msg: '',
            result: {
                text: 'mock:  movie/test_mock'
            }
        }));
    });

    // 测试用例: mock.js change，重现请求，需要能拉最新的数据
    cgiMock('/watchtest', (req, res) => {
        res.send(JSON.stringify({
            code: '0',
            msg: '',
            result: {
                text: '通过 register 测试 mock watch: 初始状态'
            }
        }));
    });

    // 返回一个数字
    // cgiMock('/number', 666);
    cgiMock('/number', 999);

    // 返回一个json
    cgiMock({
        url: '/json',
        result: {
            code: '400101', msg: "不合法的请求:Missing cookie 'wb_app_id' for method parameter of type String", transactionTime: '20170309171146', success: false
        }
    });

    // 利用 mock.js 产生随机文本
    cgiMock('/text', Random.cparagraph());

    // 返回一个字符串 利用 mock.js 产生随机字符
    cgiMock('/random', mockjs.mock({
        'string|1-10': '★'
    }));

    // 正则匹配url, 返回一个字符串
    cgiMock(/\/abc|\/xyz/, 'regexp test!');

    // option.result 参数如果是一个函数, 可以实现自定义返回内容, 接收的参数是是经过 express 封装的 req 和 res 对象.
    cgiMock(/\/function$/, (req, res) => {
        res.send('function test');
    });

    // 返回文本 readFileSync
    cgiMock('/file', utils.file('./package.json'));

    // 更复杂的规则配置
    cgiMock({
        url: /\/who/,
        method: 'GET',
        result(req, res) {
            if (req.query.name === 'kwan') {
                res.json({ kwan: '孤独患者' });
            } else {
                res.send('Nooooooooooo');
            }
        },
        headers: {
            'Content-Type': 'text/plain',
            'Content-Length': '123',
            ETag: '12345'
        },
        cookies: [
            {
                name: 'myname', value: 'kwan', maxAge: 900000, httpOnly: true
            }
        ]
    });

    // 携带参数的请求
    cgiMock('/v2/audit/list', (req, res) => {
        const {
            currentPage, pageSize, isAudited
        } = req.body;
        res.send({
            code: '0',
            msg: '',
            data: {
                currentPage,
                pageSize,
                totalPage: 2,
                totalCount: 12,
                pageData: Array.from({ length: pageSize }, () => ({
                    title: Random.title(),
                    authorName: Random.cname(),
                    authorId: Random.name(),
                    createTime: Date.now(),
                    updateTime: Date.now(),
                    readCount: Random.integer(60, 1000),
                    favoriteCount: Random.integer(1, 50),
                    postId: '12323',
                    serviceTag: '业务类型',
                    productTag: '产品类型',
                    requestTag: '需求类型',
                    handleTag: '已采纳',
                    postType: 'voice',
                    postStatus: isAudited ? 'pass' : 'auditing',
                    auditStatus: 'audit1'
                }))
            }
        });
    });

    // multipart/form-data 类型
    cgiMock('/v2/upload', (req, res) => {
        res.send({
            code: '0',
            msg: '文件上传成功'
        });
    });
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/dynamicImport.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'dynamicImport',
        config: {
            schema(joi) {
                return joi.boolean();
            }
        },
        default: false
    });
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/extraBabelPlugins.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'extraBabelPlugins',
        config: {
            schema(joi) {
                return joi.array();
            }
        }
    });
};


<|start_filename|>packages/fes-plugin-vuex/build.config.js<|end_filename|>

module.exports = {
    copy: ['runtime']
};


<|start_filename|>packages/fes/src/utils/getCwd.js<|end_filename|>
import { join, isAbsolute } from 'path';

export default () => {
    const cwd = process.cwd();
    if (process.env.APP_ROOT) {
    // avoid repeat cwd path
        if (!isAbsolute(process.env.APP_ROOT)) {
            return join(cwd, process.env.APP_ROOT);
        }
        return process.env.APP_ROOT;
    }
    return cwd;
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/publicPath.js<|end_filename|>
export default (api) => {
    api.describe({
        key: 'publicPath',
        config: {
            default: '/',
            schema(joi) {
                return joi
                    .string()
                    .regex(/\/$/)
                    .error(new Error('config.publicPath must end with /.'));
            }
        }
    });
};


<|start_filename|>packages/fes-plugin-qiankun/src/constants.js<|end_filename|>
export const defaultMainRootId = 'root-master';
export const defaultHistoryType = 'hash';
export const qiankunStateForMicroModelNamespace = 'qiankunStateForMicro';
export const qiankunStateFromMainModelNamespace = 'qiankunStateFromMain';


<|start_filename|>packages/fes-plugin-sass/src/index.js<|end_filename|>

export default (api) => {
    const {
        utils
    } = api;

    api.describe({
        key: 'sass',
        config: {
            schema(joi) {
                return joi.object({
                    implementation: joi.any(),
                    sassOptions: joi.object(),
                    prependData: joi.alternatives(joi.string(), joi.func()),
                    sourceMap: joi.boolean(),
                    webpackImporter: joi.boolean()
                });
            },
            default: {}
        }
    });

    api.chainWebpack((memo, { createCSSRule }) => {
        createCSSRule({
            lang: 'sass',
            test: /\.(sass|scss)(\?.*)?$/,
            loader: require.resolve('sass-loader'),
            options: utils.deepmerge(
                {
                    implementation: require('sass')
                },
                api.config.sass || {}
            )
        });
        return memo;
    });
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/exportStatic.js<|end_filename|>
export default (api) => {
    api.describe({
        key: 'exportStatic',
        config: {
            schema(joi) {
                return joi.object({
                    htmlSuffix: joi.boolean(),
                    dynamicRoot: joi.boolean()
                });
            }
        },
        // TODO: api.EnableBy.config 读取的 userConfig，modifyDefaultConfig hook 修改后对这个判断不起效
        enableBy: () => ('exportStatic' in api.userConfig
            ? api.userConfig.exportStatic
            : api.config?.exportStatic)
    });
};


<|start_filename|>packages/fes-utils/src/index.js<|end_filename|>
import chalk from 'chalk';
import yParser from 'yargs-parser';
import lodash from 'lodash';
import * as chokidar from 'chokidar';
import semver from 'semver';
import deepmerge from 'deepmerge';
import glob from 'glob';
import createDebug from 'debug';
import * as parser from '@babel/parser';
import traverse from '@babel/traverse';
import rimraf from 'rimraf';
import mkdirp from 'mkdirp';
import pkgUp from 'pkg-up';
import portfinder from 'portfinder';
import resolve from 'resolve';
import Mustache from 'mustache';

import Generator from './Generator';
import winPath from './winPath';
import delay from './delay';
import compatESModuleRequire from './compatESModuleRequire';
import cleanRequireCache from './cleanRequireCache';
import parseRequireDeps from './parseRequireDeps';
import mergeConfig from './mergeConfig';

export {
    chalk,
    yParser,
    lodash,
    chokidar,
    semver,
    deepmerge,
    glob,
    createDebug,
    mkdirp,
    Mustache,
    parser,
    rimraf,
    traverse,
    pkgUp,
    portfinder,
    resolve
};

export {
    Generator,
    winPath,
    delay,
    compatESModuleRequire,
    cleanRequireCache,
    parseRequireDeps,
    mergeConfig
};


<|start_filename|>packages/fes-template/src/stores/plugin-loger.js<|end_filename|>
import { createLogger } from 'vuex';

export default createLogger();


<|start_filename|>packages/fes-compiler/src/service/pluginAPI.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/tree/master/packages/core
 */

import assert from 'assert';
import * as utils from '@fesjs/utils';
import { isValidPlugin, pathToObj } from './utils/pluginUtils';
import { EnableBy, PluginType, ServiceStage } from './enums';
import Logger from '../logger';

// TODO
// 标准化 logger
export default class PluginAPI {
    constructor(opts) {
        this.id = opts.id;
        this.key = opts.key;
        this.service = opts.service;
        this.utils = utils;
        this.logger = new Logger(`fes:plugin:${this.id || this.key}`);
    }

    // TODO: reversed keys
    describe({
        id,
        key,
        config,
        enableBy
    } = {}) {
        const { plugins } = this.service;
        // this.id and this.key is generated automatically
        // so we need to diff first
        if (id && this.id !== id) {
            if (plugins[id]) {
                const name = plugins[id].isPreset ? 'preset' : 'plugin';
                throw new Error(
                    `api.describe() failed, ${name} ${id} is already registered by ${plugins[id].path}.`
                );
            }
            plugins[id] = plugins[this.id];
            plugins[id].id = id;
            delete plugins[this.id];
            this.id = id;
        }
        if (key && this.key !== key) {
            this.key = key;
            plugins[this.id].key = key;
        }

        if (config) {
            plugins[this.id].config = config;
        }

        plugins[this.id].enableBy = enableBy || EnableBy.register;
    }

    register(hook) {
        assert(
            hook.key && typeof hook.key === 'string',
            `api.register() failed, hook.key must supplied and should be string, but got ${hook.key}.`
        );
        assert(
            hook.fn && typeof hook.fn === 'function',
            `api.register() failed, hook.fn must supplied and should be function, but got ${hook.fn}.`
        );
        this.service.hooksByPluginId[this.id] = (
            this.service.hooksByPluginId[this.id] || []
        ).concat(hook);
    }

    registerCommand(commandOption) {
        const { command, fn } = commandOption;
        assert(
            !this.service.commands[command],
            `api.registerCommand() failed, the command ${command} is exists.`
        );
        assert(
            typeof command === 'string',
            'api.registerCommand() failed, the command must be String.'
        );
        assert(
            typeof fn === 'function',
            'api.registerCommand() failed, the fn must be function.'
        );
        this.service.commands[command] = commandOption;
    }

    // 在 preset 初始化阶段放后面，在插件注册阶段放前面
    registerPlugins(plugins) {
        assert(
            this.service.stage === ServiceStage.initPresets
        || this.service.stage === ServiceStage.initPlugins,
            'api.registerPlugins() failed, it should only be used in registering stage.'
        );
        assert(
            Array.isArray(plugins),
            'api.registerPlugins() failed, plugins must be Array.'
        );
        const extraPlugins = plugins.map(plugin => (isValidPlugin(plugin)
            ? (plugin)
            : pathToObj({
                type: PluginType.plugin,
                path: plugin,
                cwd: this.service.cwd
            })));
        if (this.service.stage === ServiceStage.initPresets) {
            this.service._extraPlugins.push(...extraPlugins);
        } else {
            this.service._extraPlugins.splice(0, 0, ...extraPlugins);
        }
    }

    registerPresets(presets) {
        assert(
            this.service.stage === ServiceStage.initPresets,
            'api.registerPresets() failed, it should only used in presets.'
        );
        assert(
            Array.isArray(presets),
            'api.registerPresets() failed, presets must be Array.'
        );
        const extraPresets = presets.map(preset => (isValidPlugin(preset)
            ? (preset)
            : pathToObj({
                type: PluginType.preset,
                path: preset,
                cwd: this.service.cwd
            })));
        // 插到最前面，下个 while 循环优先执行
        this.service._extraPresets.splice(0, 0, ...extraPresets);
    }

    registerMethod({
        name,
        fn,
        exitsError = true
    }) {
        if (this.service.pluginMethods[name]) {
            if (exitsError) {
                throw new Error(
                    `api.registerMethod() failed, method ${name} is already exist.`
                );
            } else {
                return;
            }
        }
        this.service.pluginMethods[name] = fn
      // 这里不能用 arrow function，this 需指向执行此方法的 PluginAPI
      // 否则 pluginId 会不会，导致不能正确 skip plugin
      || function (hookFn) {
          const hook = {
              key: name,
              ...(utils.lodash.isPlainObject(hookFn) ? hookFn : { fn: hookFn })
          };
          // @ts-ignore
          this.register(hook);
      };
    }

    skipPlugins(pluginIds) {
        pluginIds.forEach((pluginId) => {
            this.service.skipPluginIds.add(pluginId);
        });
    }
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/plugins.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'plugins',
        config: {
            schema(joi) {
                return joi.array().items(joi.string());
            }
        }
    });
};


<|start_filename|>packages/fes-compiler/src/logger/index.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/tree/master/packages/core
 */

import {
    createDebug,
    chalk
} from '@fesjs/utils';
import readline from 'readline';

export default class Logger {
    LOG = chalk.black.bgBlue('LOG');

    INFO = chalk.black.bgBlue('INFO');

    WARN = chalk.black.bgHex('#faad14')('WARN');

    ERROR = chalk.black.bgRed('ERROR');

    PROFILE = chalk.black.bgCyan('PROFILE');

    constructor(namespace) {
        // TODO: get namespace filename accounding caller function
        if (!namespace) {
            throw new Error('logger needs namespace');
        }
        this.namespace = namespace;
        this.profilers = {};
        this.debug = createDebug(this.namespace);
    }

    log(...args) {
        // TODO: node env production
        console.log(this.LOG, ...args);
    }

    /**
     * The {@link logger.info} function is an alias for {@link logger.log()}.
     * @param args
     */
    info(...args) {
        console.log(this.INFO, ...args);
    }

    error(...args) {
        console.error(this.ERROR, ...args);
    }

    warn(...args) {
        console.warn(this.WARN, ...args);
    }

    formatTiming(timing) {
        return timing < 60 * 1000
            ? `${Math.round(timing / 10) / 100}s`
            : `${Math.round(timing / 600) / 100}m`;
    }

    profile(id, message) {
        const time = Date.now();
        const namespace = `${this.namespace}:${id}`;
        // for test
        let msg;
        if (this.profilers[id]) {
            const timeEnd = this.profilers[id];
            delete this.profilers[id];
            process.stderr.write(`${this.PROFILE} `);
            msg = `${this.PROFILE} ${chalk.cyan(
                `└ ${namespace}`
            )} Completed in ${this.formatTiming(time - timeEnd)}`;
            console.log(msg);
        } else {
            msg = `${this.PROFILE} ${chalk.cyan(`┌ ${namespace}`)} ${message || ''}`;
            console.log(msg);
        }

        this.profilers[id] = time;
        return msg;
    }

    clearConsole(title) {
        if (process.stdout.isTTY) {
            const blank = '\n'.repeat(process.stdout.rows);
            console.log(blank);
            readline.cursorTo(process.stdout, 0, 0);
            readline.clearScreenDown(process.stdout);
            if (title) {
                console.log(title);
            }
        }
    }
}


<|start_filename|>packages/fes-plugin-vuex/src/helper.js<|end_filename|>
import { parser, winPath } from '@fesjs/utils';
import { readdirSync, readFileSync, statSync } from 'fs';
import { join } from 'path';

/**
 * 获取文件夹所有JS文件路径
 * @param {string} dir
 */
function getDirFilePaths(dir) {
    const dirs = readdirSync(dir);
    let pathList = [];
    for (const name of dirs) {
        const path = winPath(join(dir, name));
        const info = statSync(path);
        if (info.isDirectory()) {
            pathList = pathList.concat(getDirFilePaths(path));
        } else if (path.endsWith('.js')) {
            pathList.push(path);
        }
    }
    return pathList;
}

/**
 * 路径转驼峰
 * @param {*} path
 */
function pathToHump(path, root) {
    return path.replace(root, '')
        .replace('.js', '')
        .replace(RegExp('(/|\\.|-|_)\\S', 'g'), text => text[1].toUpperCase())
        .replace(/\S/, text => text.toLowerCase());
}

/**
 * 获取vuex模块的mutations、actions、getters类型
 * @param {*} ast
 * @param {*} name
 */
function getModelTypes(ast, name, namespace = '') {
    const types = {
        mutations: {},
        actions: {},
        getters: {}
    };
    let namespaced = false;
    if (ast.type !== 'ObjectExpression') return types;
    ast.properties.forEach((node) => {
        if (node.key.name === 'namespaced' && node.value.value) {
            namespaced = true;
            return;
        }
        if (Object.keys(types).includes(node.key.name)) {
            let type = types[node.key.name];
            if (namespaced) {
                type = types[node.key.name][name];
                if (!type) {
                    // eslint-disable-next-line no-multi-assign
                    type = types[node.key.name][name] = {};
                }
            }
            node.value.properties.forEach((prop) => {
                const key = prop.key && prop.key.name;
                if (key) {
                    type[key] = `${namespace}${namespaced ? `${name}/` : ''}${key}`;
                }
            });
            return;
        }
        if (node.key.name === 'modules') {
            node.value.properties.forEach((prop) => {
                const subTypes = getModelTypes(prop.value, prop.key.name, `${namespace}${namespaced ? `${name}/` : ''}`);
                Object.keys(types).forEach((key) => {
                    if (namespaced) {
                        types[key][name] = {
                            ...subTypes[key],
                            ...types[key][name]
                        };
                    } else {
                        types[key] = {
                            ...subTypes[key],
                            ...types[key]
                        };
                    }
                });
            });
        }
    });
    return types;
}

/**
 * 解析模块
 * @param {*} paths
 * @param {*} root
 */
function parseModel(paths = [], root) {
    const modules = [];
    const importModules = [];
    let MUTATION_TYPES = {};
    let ACTION_TYPES = {};
    let GETTER_TYPES = {};
    paths.forEach((path) => {
        const moduleName = pathToHump(path, root);
        importModules.push(`import ${moduleName} from '${path}'`);
        modules.push(moduleName);
        const content = readFileSync(path).toString('utf-8');
        let ast = parser.parse(content, {
            sourceType: 'module',
            plugins: ['jsx', 'typescript']
        });
        ast = ast.program.body.filter(body => body.type === 'ExportDefaultDeclaration')[0];
        if (ast) {
            const { mutations, actions, getters } = getModelTypes(ast.declaration, moduleName);
            MUTATION_TYPES = {
                ...mutations,
                ...MUTATION_TYPES
            };
            ACTION_TYPES = {
                ...actions,
                ...ACTION_TYPES
            };
            GETTER_TYPES = {
                ...getters,
                ...GETTER_TYPES
            };
        }
    });
    return {
        modules, importModules, MUTATION_TYPES, ACTION_TYPES, GETTER_TYPES
    };
}

function parsePlugin(paths = [], root) {
    const plugins = [];
    const importPlugins = [];
    paths.forEach((path) => {
        const moduleName = pathToHump(path, root);
        importPlugins.push(`import ${moduleName} from '${path}'`);
        plugins.push(moduleName);
    });
    return { plugins, importPlugins };
}

export function parseStore(root) {
    const paths = getDirFilePaths(root);
    const modelPaths = [];
    const pluginPaths = [];
    paths.forEach((path) => {
        if (path.indexOf('plugin') > -1) {
            pluginPaths.push(path);
        } else {
            modelPaths.push(path);
        }
    });
    return {
        ...parsePlugin(pluginPaths, root),
        ...parseModel(modelPaths, root)
    };
}


<|start_filename|>packages/fes-template/src/locales/pt-BR.js<|end_filename|>

export default {
    'navBar.lang': 'Idiomas',
    'layout.user.link.help': 'ajuda',
    'layout.user.link.privacy': 'política de privacidade',
    'layout.user.link.terms': 'termos de serviços',
    'app.preview.down.block': 'Download this page to your local project'
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/webpackConfig/getBabelOpts.js<|end_filename|>
import {
    winPath
} from '@fesjs/utils';

function getBabelOpts({
    cwd,
    targets,
    config,
    presetOpts
}) {
    const presets = [
        [
            require.resolve('@babel/preset-env'),
            {
                targets,
                useBuiltIns: 'usage',
                corejs: {
                    version: 3,
                    proposals: true
                },
                modules: false
            }
        ],
        [
            // FEATURE 实现类型安全检查
            require('@babel/preset-typescript').default,
            {
                // https://babeljs.io/docs/en/babel-plugin-transform-typescript#impartial-namespace-support
                allowNamespaces: true,
                isTSX: true,
                allExtensions: true
            }
        ],
        ...(config.extraBabelPresets || [])
    ];
    const plugins = [
        require('@babel/plugin-proposal-export-default-from').default,
        [
            require('@babel/plugin-proposal-pipeline-operator').default,
            {
                proposal: 'minimal'
            }
        ],
        require('@babel/plugin-proposal-do-expressions').default,
        require('@babel/plugin-proposal-function-bind').default,
        [
            require.resolve('@babel/plugin-transform-runtime'),
            {
                useESModules: true,
                ...presetOpts.transformRuntime
            }
        ],
        ...(presetOpts.import
            ? presetOpts.import.map(importOpts => [
                require.resolve('babel-plugin-import'),
                importOpts,
                importOpts.libraryName
            ])
            : []),
        require.resolve('@vue/babel-plugin-jsx'),
        ...(config.extraBabelPlugins || [])
    ];
    return {
        babelrc: false,
        configFile: false,
        cacheDirectory: process.env.BABEL_CACHE !== 'none' ? winPath(`${cwd}/.cache/babel-loader`) : false,
        presets,
        plugins,
        overrides: [{
            test: [/[\\/]node_modules[\\/]/, /\.fes/],
            sourceType: 'unambiguous'
        }]
    };
}


export default async ({
    cwd,
    config,
    modifyBabelOpts,
    modifyBabelPresetOpts,
    targets
}) => {
    let presetOpts = {
        transformRuntime: {}
    };
    if (modifyBabelPresetOpts) {
        presetOpts = await modifyBabelPresetOpts(presetOpts);
    }
    let babelOpts = getBabelOpts({
        cwd,
        config,
        presetOpts,
        targets
    });
    if (modifyBabelOpts) {
        babelOpts = await modifyBabelOpts(babelOpts);
    }
    return babelOpts;
};


<|start_filename|>packages/create-fes-app/src/generator/App.js<|end_filename|>
import { Generator } from '@fesjs/utils';

export default class AppGenerator extends Generator {
    constructor({
        cwd, args, path, targetDir
    }) {
        super({
            cwd,
            args
        });
        this.path = path;
        this.targetDir = targetDir;
    }

    async writing() {
        this.copyDirectory({
            context: {
                version: require('../../package.json').version
            },
            path: this.path,
            target: this.targetDir
        });
    }
}


<|start_filename|>packages/fes/src/utils/getPkg.js<|end_filename|>
import { join } from 'path';
import getCwd from './getCwd';

export default (dir) => {
    try {
        // eslint-disable-next-line
        return require(join(getCwd(), 'package.json'));
    } catch (error) {
        try {
            // eslint-disable-next-line
            return require(join(dir, 'package.json'));
        } catch (err) {
            return null;
        }
    }
};


<|start_filename|>packages/fes-plugin-request/src/index.js<|end_filename|>
import { Logger } from '@fesjs/compiler';
import { readFileSync } from 'fs';
import { join } from 'path';

const logger = new Logger('fes:plugin-request');

export default (api) => {
    api.addRuntimePluginKey(() => 'request');
    // 配置
    api.describe({
        key: 'request',
        config: {
            schema(joi) {
                return joi.object({
                    dataField: joi
                        .string()
                        .pattern(/^[a-zA-Z]*$/)
                        .allow(''),
                    base: joi
                        .string()
                        .allow('')
                });
            },
            default: {
                base: '',
                dataField: ''
            }
        }
    });

    const namespace = 'plugin-request';
    const absoluteFilePath = `${namespace}/request.js`;
    const requestTemplate = readFileSync(join(__dirname, 'template', 'request.js'), 'utf-8');
    api.onGenerateFiles(() => {
        // 文件写出
        const { dataField = '', base = '' } = api.config.request;

        if (base) {
            // DEPRECATED
            logger.warn('[DEPRECATED]: reqeust base 即将废弃，建议使用 axios baseURL代替：https://github.com/axios/axios');
        }
        api.writeTmpFile({
            path: absoluteFilePath,
            content: requestTemplate
                .replace('REPLACE_DATA_FIELD', JSON.stringify(dataField))
                .replace('REPLACE_BASE', base || '')
        });
    });

    let generatedOnce = false;
    api.onGenerateFiles(() => {
        if (generatedOnce) return;
        generatedOnce = true;
        api.copyTmpFiles({
            namespace,
            path: join(__dirname, 'template'),
            ignore: ['request.js']
        });
    });

    api.addPluginExports(() => [
        {
            exportAll: true,
            source: absoluteFilePath
        }
    ]);
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/outputPath.js<|end_filename|>
export default (api) => {
    api.describe({
        key: 'outputPath',
        config: {
            default: 'dist',
            schema(joi) {
                return joi
                    .string()
                    .not('src', 'public', 'pages', 'mock', 'config')
                    .allow('');
            }
        }
    });
};


<|start_filename|>packages/fes/bin/fes.js<|end_filename|>
#!/usr/bin/env node

const resolveCwd = require('resolve-cwd');

const { name, bin } = require('../package.json');

const localCLI = resolveCwd.silent(`${name}/${bin.fes}`);
if (!process.env.USE_GLOBAL_FES && localCLI && localCLI !== __filename) {
    // eslint-disable-next-line
    require(localCLI);
} else {
    require('../lib/cli');
}


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/webpackConfig/resolveDefine.js<|end_filename|>
const prefixRE = /^FES_APP_/;

const ENV_SHOULD_PASS = ['NODE_ENV', 'FES_ENV'];

export default function resolveDefine(opts = {}, raw) {
    const env = {};
    Object.keys(process.env).forEach((key) => {
        if (prefixRE.test(key) || ENV_SHOULD_PASS.includes(key)) {
            env[key] = process.env[key];
        }
    });

    env.BASE_URL = opts.publicPath;

    if (raw) {
        return env;
    }

    for (const key in env) {
        if (Object.prototype.hasOwnProperty.call(env, key)) {
            env[key] = JSON.stringify(env[key]);
        }
    }

    const define = {
        __VUE_OPTIONS_API__: true,
        __VUE_PROD_DEVTOOLS__: false,
        ...opts.define
    };

    for (const key in define) {
        if (Object.prototype.hasOwnProperty.call(define, key)) {
            define[key] = JSON.stringify(define[key]);
        }
    }

    return {
        'process.env': env,
        ...define
    };
}


<|start_filename|>packages/fes-plugin-request/src/template/preventRepeatReq.js<|end_filename|>
const requestMap = new Map();

const mergeRequestMap = new Map();
const requestQueue = new Map();

function handleCachingStart(ctx) {
    const isRequesting = mergeRequestMap.get(ctx.key);
    if (isRequesting) {
        return new Promise((resolve) => {
            const queue = requestQueue.get(ctx.key) || [];
            requestQueue.set(ctx.key, queue.concat(resolve));
        });
    }
    mergeRequestMap.set(ctx.key, true);
}

function handleRepeatRequest(ctx) {
    const queue = requestQueue.get(ctx.key);
    if (queue && queue.length > 0) {
        queue.forEach((resolve) => {
            if (ctx.error) {
                resolve({
                    error: ctx.error
                });
            } else {
                resolve({
                    response: ctx.response
                });
            }
        });
    }
    requestQueue.delete(ctx.key);
    mergeRequestMap.delete(ctx.key);
}

export default async (ctx, next) => {
    if (ctx.config.mergeRequest) {
        const result = await handleCachingStart(ctx);
        if (result) {
            Object.keys(result).forEach((key) => {
                ctx[key] = result[key];
            });
            return;
        }
    } else {
        if (requestMap.get(ctx.key) && !ctx.config.mergeRequest) {
            ctx.error = {
                type: 'REPEAT',
                msg: '重复请求',
                config: ctx.config
            };
            return;
        }
        requestMap.set(ctx.key, true);
    }

    await next();

    if (ctx.config.mergeRequest) {
        handleRepeatRequest(ctx);
    } else {
        requestMap.delete(ctx.key);
    }
};


<|start_filename|>packages/create-fes-app/templates/app/h5/src/app.js<|end_filename|>
export const request = {
    errorConfig: {
        404() {
            console.log('to 404 page');
        }
    }
};


<|start_filename|>packages/fes-compiler/src/config/index.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/tree/master/packages/core
 */

import { existsSync } from 'fs';
import { extname, join } from 'path';
import {
    chalk,
    chokidar,
    compatESModuleRequire,
    deepmerge,
    cleanRequireCache,
    lodash,
    parseRequireDeps,
    winPath
} from '@fesjs/utils';
import assert from 'assert';
import joi from 'joi';
import { ServiceStage } from '../service/enums';
import {
    getUserConfigWithKey,
    updateUserConfigWithKey
} from './utils/configUtils';
import isEqual from './utils/isEqual';
import mergeDefault from './utils/mergeDefault';

const CONFIG_FILES = ['.fes.js'];

// TODO:
// 1. custom config file
export default class Config {
    cwd;

    service;

    config;

    localConfig;

    configFile;

    constructor(opts) {
        this.cwd = opts.cwd || process.cwd();
        this.service = opts.service;
        this.localConfig = opts.localConfig;
    }

    async getDefaultConfig() {
        const pluginIds = Object.keys(this.service.plugins);

        // collect default config
        const defaultConfig = pluginIds.reduce((memo, pluginId) => {
            const { key, config = {} } = this.service.plugins[pluginId];
            if ('default' in config) memo[key] = config.default;
            return memo;
        }, {});

        return defaultConfig;
    }

    getConfig({ defaultConfig }) {
        assert(
            this.service.stage >= ServiceStage.pluginReady,
            'Config.getConfig() failed, it should not be executed before plugin is ready.'
        );

        const userConfig = this.getUserConfig();
        // 用于提示用户哪些 key 是未定义的
        // TODO: 考虑不排除 false 的 key
        const userConfigKeys = Object.keys(userConfig).filter(
            key => userConfig[key] !== false
        );

        // get config
        const pluginIds = Object.keys(this.service.plugins);
        pluginIds.forEach((pluginId) => {
            const { key, config = {} } = this.service.plugins[pluginId];
            // recognize as key if have schema config
            if (!config.schema) return;

            const value = getUserConfigWithKey({
                key,
                userConfig
            });
            // 不校验 false 的值，此时已禁用插件
            if (value === false) return;

            // do validate
            const schema = config.schema(joi);
            assert(
                joi.isSchema(schema),
                `schema return from plugin ${pluginId} is not valid schema.`
            );
            const { error } = schema.validate(value);
            if (error) {
                const e = new Error(
                    `Validate config "${key}" failed, ${error.message}`
                );
                e.stack = error.stack;
                throw e;
            }

            // remove key
            const index = userConfigKeys.indexOf(key.split('.')[0]);
            if (index !== -1) {
                userConfigKeys.splice(index, 1);
            }

            // update userConfig with defaultConfig
            if (key in defaultConfig) {
                const newValue = mergeDefault({
                    defaultConfig: defaultConfig[key],
                    config: value
                });
                updateUserConfigWithKey({
                    key,
                    value: newValue,
                    userConfig
                });
            }
        });

        if (userConfigKeys.length) {
            const keys = userConfigKeys.length > 1 ? 'keys' : 'key';
            throw new Error(
                `Invalid config ${keys}: ${userConfigKeys.join(', ')}`
            );
        }

        return userConfig;
    }

    getUserConfig() {
        const configFile = this.getConfigFile();
        this.configFile = configFile;
        if (configFile.length > 0) {
            // clear require cache and set babel register
            const requireDeps = configFile.reduce((memo, file) => {
                memo = memo.concat(parseRequireDeps(file));
                return memo;
            }, []);
            requireDeps.forEach(cleanRequireCache);
            this.service.babelRegister.setOnlyMap({
                key: 'config',
                value: requireDeps
            });

            // require config and merge
            return this.mergeConfig(...this.requireConfigs(configFile));
        }
        return {};
    }

    addAffix(file, affix) {
        const ext = extname(file);
        return file.replace(new RegExp(`${ext}$`), `.${affix}${ext}`);
    }

    requireConfigs(configFiles) {
        // eslint-disable-next-line
        return configFiles.map((f) => compatESModuleRequire(require(f)));
    }

    mergeConfig(...configs) {
        let ret = {};
        for (const config of configs) {
            // TODO: 精细化处理，比如处理 dotted config key
            ret = deepmerge(ret, config);
        }
        return ret;
    }

    getConfigFile() {
        // TODO: support custom config file
        let configFile = CONFIG_FILES.find(f => existsSync(join(this.cwd, f)));
        if (!configFile) return [];
        configFile = winPath(configFile);
        let envConfigFile;
        // 潜在问题：
        // .local 和 .env 的配置必须有 configFile 才有效
        if (process.env.FES_ENV) {
            envConfigFile = this.addAffix(
                configFile,
                process.env.FES_ENV
            );
            if (!existsSync(join(this.cwd, envConfigFile))) {
                throw new Error(
                    `get user config failed, ${envConfigFile} does not exist, but process.env.FES_ENV is set to ${process.env.FES_ENV}.`
                );
            }
        }
        const files = [
            configFile,
            envConfigFile,
            this.localConfig && this.addAffix(configFile, 'local')
        ]
            .filter(f => !!f)
            .map(f => join(this.cwd, f))
            .filter(f => existsSync(f));
        return files;
    }

    getWatchFilesAndDirectories() {
        const fesEnv = process.env.FES_ENV;
        const configFiles = lodash.clone(CONFIG_FILES);
        CONFIG_FILES.forEach((f) => {
            if (this.localConfig) configFiles.push(this.addAffix(f, 'local'));
            if (fesEnv) configFiles.push(this.addAffix(f, fesEnv));
        });

        const configDir = winPath(join(this.cwd, 'config'));

        const files = configFiles
            .reduce((memo, f) => {
                const file = winPath(join(this.cwd, f));
                if (existsSync(file)) {
                    memo = memo.concat(parseRequireDeps(file));
                } else {
                    memo.push(file);
                }
                return memo;
            }, [])
            .filter(f => !f.startsWith(configDir));

        return [configDir].concat(files);
    }

    watch(opts) {
        let paths = this.getWatchFilesAndDirectories();
        let userConfig = opts.userConfig;
        const watcher = chokidar.watch(paths, {
            ignoreInitial: true,
            cwd: this.cwd
        });
        watcher.on('all', (event, path) => {
            console.log(chalk.green(`[${event}] ${path}`));
            const newPaths = this.getWatchFilesAndDirectories();
            const diffs = lodash.difference(newPaths, paths);
            if (diffs.length) {
                watcher.add(diffs);
                paths = paths.concat(diffs);
            }

            const newUserConfig = this.getUserConfig();
            const pluginChanged = [];
            const valueChanged = [];
            Object.keys(this.service.plugins).forEach((pluginId) => {
                const { key, config = {} } = this.service.plugins[pluginId];
                // recognize as key if have schema config
                if (!config.schema) return;
                if (!isEqual(newUserConfig[key], userConfig[key])) {
                    const changed = {
                        key,
                        pluginId
                    };
                    if (
                        newUserConfig[key] === false
                        || userConfig[key] === false
                    ) {
                        pluginChanged.push(changed);
                    } else {
                        valueChanged.push(changed);
                    }
                }
            });

            if (pluginChanged.length || valueChanged.length) {
                opts.onChange({
                    userConfig: newUserConfig,
                    pluginChanged,
                    valueChanged
                });
            }
            userConfig = newUserConfig;
        });

        return () => {
            watcher.close();
        };
    }
}


<|start_filename|>packages/fes-template-h5/.fes.prod.js<|end_filename|>
// .fes.js 只负责管理编译时配置，只能使用plain Object


export default {
    request: {
        base: '/api'
    }
};


<|start_filename|>packages/fes-plugin-enums/src/index.js<|end_filename|>
import { readFileSync } from 'fs';
import { join } from 'path';

const namespace = 'plugin-enums';

export default (api) => {
    const {
        utils: { Mustache }
    } = api;

    api.describe({
        key: 'enums',
        config: {
            schema(joi) {
                return joi.object();
            },
            onChange: api.ConfigChangeType.regenerateTmpFiles
        }
    });

    const absoluteFilePath = join(namespace, 'core.js');
    api.onGenerateFiles(() => {
        // 文件写出
        const enums = api.config.enums || {};
        api.writeTmpFile({
            path: absoluteFilePath,
            content: Mustache.render(
                readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'),
                {
                    REPLACE_ENUMS: JSON.stringify(enums)
                }
            )
        });

        api.copyTmpFiles({
            namespace,
            path: join(__dirname, 'runtime'),
            ignore: ['.tpl']
        });
    });

    api.addPluginExports(() => [
        {
            specifiers: ['enums'],
            source: absoluteFilePath
        }
    ]);
};


<|start_filename|>packages/fes-plugin-qiankun/src/index.js<|end_filename|>
export default (api) => {
    api.describe({
        key: 'qiankun',
        config: {
            schema(joi) {
                return joi.object().keys({
                    micro: joi.object(),
                    main: joi.object()
                });
            }
        }
    });

    api.addRuntimePluginKey(() => 'qiankun');

    api.registerPlugins([
        require.resolve('./main'),
        require.resolve('./micro')
    ]);
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/mountElementId.js<|end_filename|>

export default (api) => {
    api.describe({
        key: 'mountElementId',
        config: {
            default: 'app',
            schema(joi) {
                return joi.string().allow('');
            }
        }
    });
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/features/vueLoader.js<|end_filename|>


export default (api) => {
    api.describe({
        key: 'vueLoader',
        config: {
            schema(joi) {
                return joi
                    .object({})
                    .description(
                        'more vue-loader options see https://vue-loader.vuejs.org/'
                    );
            }
        }
    });
};


<|start_filename|>packages/fes-plugin-monaco-editor/src/index.js<|end_filename|>
import { readFileSync } from 'fs';
import { join } from 'path';

const namespace = 'plugin-monaco-editor';

export default (api) => {
    const {
        utils: { Mustache }
    } = api;

    api.describe({
        key: 'monacoEditor',
        config: {
            schema(joi) {
                return joi.object().keys({
                    filename: joi.string(),
                    publicPath: joi.string(),
                    languages: joi.array(),
                    features: joi.array(),
                    globalAPI: joi.boolean()
                });
            }
        },
        default: {
        }
    });

    const absoluteFilePath = join(namespace, 'core.js');

    const absRuntimeFilePath = join(namespace, 'runtime.js');

    const absLoaderFilePath = join(namespace, 'loader.js');

    api.onGenerateFiles(() => {
        // 文件写出
        api.writeTmpFile({
            path: absoluteFilePath,
            content: Mustache.render(
                readFileSync(join(__dirname, 'runtime/core.tpl'), 'utf-8'),
                {
                }
            )
        });

        api.writeTmpFile({
            path: absRuntimeFilePath,
            content: Mustache.render(
                readFileSync(join(__dirname, 'runtime/runtime.tpl'), 'utf-8')
            )
        });

        api.writeTmpFile({
            path: absLoaderFilePath,
            content: Mustache.render(
                readFileSync(join(__dirname, 'runtime/loader.tpl'), 'utf-8')
            )
        });

        api.copyTmpFiles({
            namespace,
            path: join(__dirname, 'runtime'),
            ignore: ['.tpl']
        });
    });

    api.addPluginExports(() => [
        {
            specifiers: ['monaco', 'MonacoEditor'],
            source: absoluteFilePath
        }
    ]);

    api.addRuntimePluginKey(() => 'monacoEditor');

    api.addRuntimePlugin(() => `@@/${absRuntimeFilePath}`);

    api.chainWebpack((webpackConfig) => {
        webpackConfig
            .plugin('monaco-editor')
            .use(require('monaco-editor-webpack-plugin'), [
                api.config?.monacoEditor || {}
            ]);
        return webpackConfig;
    });
};


<|start_filename|>packages/fes-preset-built-in/src/plugins/commands/dev/watchPkg.js<|end_filename|>
/**
 * @copy 该文件代码大部分出自 umi，有需要请参考：
 * https://github.com/umijs/umi/blob/master/packages/preset-built-in/src/plugins/commands/dev/watchPkg.ts
 */

import { join } from 'path';
import { chokidar, winPath, lodash } from '@fesjs/utils';
import { existsSync, readFileSync } from 'fs';
import { isPluginOrPreset, PluginType } from '@fesjs/compiler';

function getPlugins(opts) {
    return Object.keys({
        ...opts.pkg.dependencies,
        ...opts.pkg.devDependencies
    }).filter(name => (
        isPluginOrPreset(PluginType.plugin, name)
        || isPluginOrPreset(PluginType.preset, name)
    ));
}

function getPluginsFromPkgPath(opts) {
    let pkg = {};
    if (existsSync(opts.pkgPath)) {
        try {
            pkg = JSON.parse(readFileSync(opts.pkgPath, 'utf-8'));
        } catch (e) {
            // ignore
        }
    }
    return getPlugins({ pkg });
}

export function watchPkg(opts) {
    const pkgPath = join(opts.cwd, 'package.json');
    const plugins = getPluginsFromPkgPath({ pkgPath });
    const watcher = chokidar.watch(pkgPath, {
        ignoreInitial: true
    });
    watcher.on('all', () => {
        const newPlugins = getPluginsFromPkgPath({ pkgPath });
        if (!lodash.isEqual(plugins, newPlugins)) {
            // 已经重启了，只处理一次就够了
            opts.onChange();
        }
    });
    return () => {
        watcher.close();
    };
}

export function watchPkgs(opts) {
    const unwatchs = [watchPkg({ cwd: opts.cwd, onChange: opts.onChange })];
    if (winPath(opts.cwd) !== winPath(process.cwd())) {
        unwatchs.push(watchPkg({ cwd: process.cwd(), onChange: opts.onChange }));
    }
    return () => {
        unwatchs.forEach((unwatch) => {
            unwatch();
        });
    };
}


<|start_filename|>packages/fes-compiler/src/config/utils/configUtils.js<|end_filename|>
import { lodash } from '@fesjs/utils';
import set from 'set-value';

export function updateUserConfigWithKey({
    key,
    value,
    userConfig
}) {
    set(userConfig, key, value);
}

export function getUserConfigWithKey({
    key,
    userConfig
}) {
    return lodash.get(userConfig, key);
}


<|start_filename|>packages/fes-runtime/package.json<|end_filename|>
{
  "name": "@fesjs/runtime",
  "version": "2.0.2",
  "description": "@fesjs/runtime",
  "main": "es/index.js",
  "module": "es/index.js",
  "files": [
    "lib",
    "es"
  ],
  "repository": {
    "type": "git",
    "url": "git+https://github.com/WeBankFinTech/fes.js.git",
    "directory": "packages/fes-runtime"
  },
  "keywords": [
    "fes"
  ],
  "sideEffects": false,
  "author": "qlin",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/WeBankFinTech/fes.js/issues"
  },
  "homepage": "https://github.com/WeBankFinTech/fes.js#readme",
  "publishConfig": {
    "access": "public"
  },
  "peerDependencies": {
    "vue": "^3.0.5"
  },
  "dependencies": {
    "vue-router": "^4.0.1"
  }
}


<|start_filename|>packages/fes-plugin-request/src/template/genRequestKey.js<|end_filename|>
import { isURLSearchParams } from './helpers';
/**
 * 唯一定位一个请求（url, data | params, method）
 *      其中请求参数(data, params)根据请求方法,只使用其中一个
 *      一个请求同时包含 data | params 参数的设计本身不合理
 *      不对这种情况进行兼容
 */
export default async function genRequestKey(ctx, next) {
    const { url, data, method } = ctx.config;
    if (isURLSearchParams(data)) {
        ctx.key = `${url}${data.toString()}${method}`;
    }
    ctx.key = `${url}${JSON.stringify(data)}${method}`;
    await next();
}
