From e0656a4d59beef103a5627461d9b9c87996928e3 Mon Sep 17 00:00:00 2001 From: Gxkl Date: Fri, 19 Apr 2024 13:45:00 +0800 Subject: [PATCH] feat: use app.loader.getTypeFiles to generate module config file names (#213) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ##### Checklist - [ ] `npm test` passes - [ ] tests and/or benchmarks are included - [ ] documentation is changed or added - [ ] commit message follows commit guidelines ##### Affected core subsystem(s) ##### Description of change ## Summary by CodeRabbit - **New Features** - Introduced default configuration loading for specific module files. - **Refactor** - Enhanced module configuration management with new methods and asynchronous loading capabilities. - Updated import syntax and added comprehensive tests for configuration loading. --------- Co-authored-by: killa --- core/common-util/src/ModuleConfig.ts | 109 +++++++++++++-------- core/common-util/test/ModuleConfig.test.ts | 24 ++++- plugin/config/app.ts | 16 ++- standalone/standalone/src/Runner.ts | 18 +++- 4 files changed, 116 insertions(+), 51 deletions(-) diff --git a/core/common-util/src/ModuleConfig.ts b/core/common-util/src/ModuleConfig.ts index cc51e17b..ca5d845d 100644 --- a/core/common-util/src/ModuleConfig.ts +++ b/core/common-util/src/ModuleConfig.ts @@ -29,18 +29,10 @@ const DEFAULT_READ_MODULE_REF_OPTS = { }; export class ModuleConfigUtil { - public static moduleYamlPath(modulePath: string, env?: string): string { - if (env) { - return path.join(modulePath, `module.${env}.yml`); - } - return path.join(modulePath, 'module.yml'); - } + static configNames: string[] | undefined; - public static moduleJsonPath(modulePath: string, env?: string): string { - if (env) { - return path.join(modulePath, `module.${env}.json`); - } - return path.join(modulePath, 'module.json'); + public static setConfigNames(configNames: string[] | undefined) { + ModuleConfigUtil.configNames = configNames; } public static readModuleReference(baseDir: string, options?: ReadModuleReferenceOptions): readonly ModuleReference[] { @@ -205,25 +197,42 @@ export class ModuleConfigUtil { return ModuleConfigUtil.getModuleName(pkg); } - public static async loadModuleConfig(moduleDir: string, baseDir?: string, env?: string): Promise { - moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); - let defaultConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir); - if (!defaultConfig) { - defaultConfig = await ModuleConfigUtil.loadModuleJson(moduleDir); - } - let envConfig: ModuleConfig | undefined; + public static async loadModuleConfig(moduleDir: string, baseDir?: string, env?: string): Promise { + const modulePath = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); + let configNames: string[]; if (env) { - envConfig = await ModuleConfigUtil.loadModuleYaml(moduleDir, env); - if (!envConfig) { - envConfig = await ModuleConfigUtil.loadModuleJson(moduleDir, env); + configNames = [ 'module', `module.${env}` ]; + } else { + // assert(ModuleConfigUtil.configNames, 'should setConfigNames before load module config'); + configNames = ModuleConfigUtil.configNames || [ 'module' ]; + } + + const target: ModuleConfig = {}; + for (const configName of configNames) { + let config = await ModuleConfigUtil.#loadOne(modulePath, configName); + // both module.yml and module.default.yml are ok for default config + if (configName === 'module.default' && !config) { + config = await ModuleConfigUtil.#loadOne(modulePath, 'module'); + } + if (config) { + extend(true, target, config); } } - extend(true, defaultConfig, envConfig); - return defaultConfig; + + return target; } - private static async loadModuleJson(moduleDir: string, env?: string): Promise { - const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir, env); + static async #loadOne(moduleDir: string, configName: string): Promise { + const yamlConfigPath = path.join(moduleDir, `${configName}.yml`); + let config = await ModuleConfigUtil.#loadYaml(yamlConfigPath); + if (!config) { + const jsonConfigPath = path.join(moduleDir, `${configName}.json`); + config = await ModuleConfigUtil.#loadJson(jsonConfigPath); + } + return config; + } + + static async #loadJson(moduleJsonPath: string): Promise { const moduleJsonPathExists = await FSUtil.fileExists(moduleJsonPath); if (!moduleJsonPathExists) { return; @@ -233,8 +242,7 @@ export class ModuleConfigUtil { return moduleJson.config; } - private static async loadModuleYaml(moduleDir: string, env?: string): Promise { - const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir, env); + static async #loadYaml(moduleYamlPath: string): Promise { const moduleYamlPathExists = await FSUtil.fileExists(moduleYamlPath); if (!moduleYamlPathExists) { return; @@ -243,24 +251,42 @@ export class ModuleConfigUtil { return yaml.safeLoad(moduleYamlContent) as ModuleConfigUtil; } - public static loadModuleConfigSync(moduleDir: string, baseDir?: string, env?: string): ModuleConfig | undefined { - moduleDir = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); - let defaultConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir); - if (!defaultConfig) { - defaultConfig = ModuleConfigUtil.loadModuleJsonSync(moduleDir); - } - let envConfig: ModuleConfig | undefined; + public static loadModuleConfigSync(moduleDir: string, baseDir?: string, env?: string): ModuleConfig { + const modulePath = ModuleConfigUtil.resolveModuleDir(moduleDir, baseDir); + let configNames: string[]; if (env) { - envConfig = ModuleConfigUtil.loadModuleYamlSync(moduleDir, env); - if (!envConfig) { - envConfig = ModuleConfigUtil.loadModuleJsonSync(moduleDir, env); + configNames = [ 'module', `module.${env}` ]; + } else { + // assert(ModuleConfigUtil.configNames, 'should setConfigNames before load module config'); + configNames = ModuleConfigUtil.configNames || [ 'module' ]; + } + + const target: ModuleConfig = {}; + for (const configName of configNames) { + let config = ModuleConfigUtil.#loadOneSync(modulePath, configName); + // both module.yml and module.default.yml are ok for default config + if (configName === 'module.default' && !config) { + config = ModuleConfigUtil.#loadOneSync(modulePath, 'module'); } + if (config) { + extend(true, target, config); + } + } + + return target; + } + + static #loadOneSync(moduleDir: string, configName: string): ModuleConfig | undefined { + const yamlConfigPath = path.join(moduleDir, `${configName}.yml`); + let config = ModuleConfigUtil.#loadYamlSync(yamlConfigPath); + if (!config) { + const jsonConfigPath = path.join(moduleDir, `${configName}.json`); + config = ModuleConfigUtil.#loadJsonSync(jsonConfigPath); } - return extend(true, defaultConfig, envConfig); + return config; } - private static loadModuleJsonSync(moduleDir: string, env?: string): ModuleConfig | undefined { - const moduleJsonPath = ModuleConfigUtil.moduleJsonPath(moduleDir, env); + static #loadJsonSync(moduleJsonPath: string): ModuleConfig | undefined { const moduleJsonPathExists = fs.existsSync(moduleJsonPath); if (!moduleJsonPathExists) { return; @@ -270,8 +296,7 @@ export class ModuleConfigUtil { return moduleJson.config; } - private static loadModuleYamlSync(moduleDir: string, env?: string): ModuleConfig | undefined { - const moduleYamlPath = ModuleConfigUtil.moduleYamlPath(moduleDir, env); + static #loadYamlSync(moduleYamlPath: string): ModuleConfig | undefined { const moduleYamlPathExists = fs.existsSync(moduleYamlPath); if (!moduleYamlPathExists) { return; diff --git a/core/common-util/test/ModuleConfig.test.ts b/core/common-util/test/ModuleConfig.test.ts index 7e60c0ba..e1ebb747 100644 --- a/core/common-util/test/ModuleConfig.test.ts +++ b/core/common-util/test/ModuleConfig.test.ts @@ -1,9 +1,13 @@ -import assert from 'node:assert'; +import { strict as assert } from 'node:assert'; import path from 'node:path'; import { ModuleConfigUtil } from '../src/ModuleConfig'; describe('test/ModuleConfig.test.ts', () => { describe('load yaml config', () => { + afterEach(() => { + ModuleConfigUtil.setConfigNames(undefined); + }); + it('should work', () => { const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/foo-yaml')); assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1' } }); @@ -13,6 +17,24 @@ describe('test/ModuleConfig.test.ts', () => { const config = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config'), undefined, 'dev'); assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1', port: 11306 } }); }); + + it('should load with configNames', async () => { + ModuleConfigUtil.setConfigNames([ 'module.default', 'module.dev' ]); + const config = await ModuleConfigUtil.loadModuleConfig(path.join(__dirname, './fixtures/modules/dev-module-config')); + const configSync = ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config')); + assert.deepStrictEqual(config, { mysql: { host: '127.0.0.1', port: 11306 } }); + assert.deepStrictEqual(configSync, { mysql: { host: '127.0.0.1', port: 11306 } }); + }); + + // it('should throw error without initialization', async () => { + // await assert.rejects(async () => { + // await ModuleConfigUtil.loadModuleConfig(path.join(__dirname, './fixtures/modules/dev-module-config')); + // }, /should setConfigNames before load module config/); + // + // assert.throws(() => { + // ModuleConfigUtil.loadModuleConfigSync(path.join(__dirname, './fixtures/modules/dev-module-config')); + // }, /should setConfigNames before load module config/); + // }); }); describe('load module reference', () => { diff --git a/plugin/config/app.ts b/plugin/config/app.ts index 8e62520c..e7520507 100644 --- a/plugin/config/app.ts +++ b/plugin/config/app.ts @@ -1,12 +1,15 @@ -import { Application } from 'egg'; -import { ModuleConfigUtil, ModuleReference } from '@eggjs/tegg-common-util'; +import type { Application, IBoot } from 'egg'; +import { ModuleConfigUtil } from '@eggjs/tegg-common-util'; +import type { ModuleReference } from '@eggjs/tegg-common-util'; import { ModuleScanner } from './lib/ModuleScanner'; -export default class App { +export default class App implements IBoot { private readonly app: Application; constructor(app: Application) { this.app = app; + const configNames = this.app.loader.getTypeFiles('module'); + ModuleConfigUtil.setConfigNames(configNames); } configWillLoad() { @@ -15,6 +18,7 @@ export default class App { this.app.moduleReferences = moduleScanner.loadModuleReferences(); this.app.moduleConfigs = {}; + for (const reference of this.app.moduleReferences) { const absoluteRef: ModuleReference = { path: ModuleConfigUtil.resolveModuleDir(reference.path, this.app.baseDir), @@ -26,8 +30,12 @@ export default class App { this.app.moduleConfigs[moduleName] = { name: moduleName, reference: absoluteRef, - config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path, undefined, this.app.config.env) || {}, + config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path), }; } } + + async beforeClose() { + ModuleConfigUtil.setConfigNames(undefined); + } } diff --git a/standalone/standalone/src/Runner.ts b/standalone/standalone/src/Runner.ts index 33420e9e..22c14f91 100644 --- a/standalone/standalone/src/Runner.ts +++ b/standalone/standalone/src/Runner.ts @@ -1,13 +1,17 @@ import { ModuleConfigUtil, ModuleReference, ReadModuleReferenceOptions, RuntimeConfig } from '@eggjs/tegg-common-util'; import { - EggPrototype, EggPrototypeLifecycleUtil, + EggPrototype, + EggPrototypeLifecycleUtil, LoadUnit, LoadUnitFactory, - LoadUnitLifecycleUtil, LoadUnitMultiInstanceProtoHook, + LoadUnitLifecycleUtil, + LoadUnitMultiInstanceProtoHook, } from '@eggjs/tegg-metadata'; import { ContextHandler, - EggContainerFactory, EggContext, EggObjectLifecycleUtil, + EggContainerFactory, + EggContext, + EggObjectLifecycleUtil, LoadUnitInstance, LoadUnitInstanceFactory, ModuleLoadUnitInstance, @@ -104,6 +108,10 @@ export class Runner { obj: runtimeConfig, }]; + // load module.yml and module.env.yml by default + if (!ModuleConfigUtil.configNames) { + ModuleConfigUtil.configNames = [ 'module.default', `module.${this.env}` ]; + } for (const reference of this.moduleReferences) { const absoluteRef = { path: ModuleConfigUtil.resolveModuleDir(reference.path, this.cwd), @@ -114,7 +122,7 @@ export class Runner { this.moduleConfigs[moduleName] = { name: moduleName, reference: absoluteRef, - config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path, undefined, this.env) || {}, + config: ModuleConfigUtil.loadModuleConfigSync(absoluteRef.path), }; } for (const moduleConfig of Object.values(this.moduleConfigs)) { @@ -267,5 +275,7 @@ export class Runner { MysqlDataSourceManager.instance.clear(); SqlMapManager.instance.clear(); TableModelManager.instance.clear(); + // clear configNames + ModuleConfigUtil.setConfigNames(undefined); } }