UNPKG

@fesjs/compiler

Version:
233 lines (225 loc) 7.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _fs = require("fs"); var _path = require("path"); var _assert = _interopRequireDefault(require("assert")); var _utils = require("@fesjs/utils"); var _joi = _interopRequireDefault(require("joi")); var _enums = require("../service/enums"); var _configUtils = require("./utils/configUtils"); var _isEqual = _interopRequireDefault(require("./utils/isEqual")); var _mergeDefault = _interopRequireDefault(require("./utils/mergeDefault")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * @copy 该文件代码大部分出自 umi,有需要请参考: * https://github.com/umijs/umi/tree/master/packages/core */ const CONFIG_FILES = ['.fes.js']; 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 }) { (0, _assert.default)(this.service.stage >= _enums.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 = (0, _configUtils.getUserConfigWithKey)({ key, userConfig }); // 不校验 false 的值,此时已禁用插件 if (value === false) return; // do validate const schema = config.schema(_joi.default); (0, _assert.default)(_joi.default.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 = (0, _mergeDefault.default)({ defaultConfig: defaultConfig[key], config: value }); (0, _configUtils.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((0, _utils.parseRequireDeps)(file)); return memo; }, []); requireDeps.forEach(_utils.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 = (0, _path.extname)(file); return file.replace(new RegExp(`${ext}$`), `.${affix}${ext}`); } requireConfigs(configFiles) { // eslint-disable-next-line return configFiles.map(f => (0, _utils.compatESModuleRequire)(require(f))); } mergeConfig(...configs) { let ret = {}; for (const config of configs) { // TODO: 精细化处理,比如处理 dotted config key ret = (0, _utils.deepmerge)(ret, config); } return ret; } getConfigFile() { // TODO: support custom config file let configFile = CONFIG_FILES.find(f => (0, _fs.existsSync)((0, _path.join)(this.cwd, f))); if (!configFile) return []; configFile = (0, _utils.winPath)(configFile); let envConfigFile; // 潜在问题: // .local 和 .env 的配置必须有 configFile 才有效 if (process.env.FES_ENV) { envConfigFile = this.addAffix(configFile, process.env.FES_ENV); if (!(0, _fs.existsSync)((0, _path.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 => (0, _path.join)(this.cwd, f)).filter(f => (0, _fs.existsSync)(f)); return files; } getWatchFilesAndDirectories() { const fesEnv = process.env.FES_ENV; const configFiles = _utils.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 = (0, _utils.winPath)((0, _path.join)(this.cwd, 'config')); const files = configFiles.reduce((memo, f) => { const file = (0, _utils.winPath)((0, _path.join)(this.cwd, f)); if ((0, _fs.existsSync)(file)) { memo = memo.concat((0, _utils.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 = _utils.chokidar.watch(paths, { ignoreInitial: true, cwd: this.cwd }); watcher.on('all', (event, path) => { console.log(_utils.chalk.green(`[${event}] ${path}`)); const newPaths = this.getWatchFilesAndDirectories(); const diffs = _utils.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 (!(0, _isEqual.default)(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(); }; } } exports.default = Config;