Repository: artusjs/core (ts)

Issue:
[BUG] 多环境 Config 合并不正确(v1.0.0-alpha.16)
### 现象

运行时 env 设定失效

### 成因

#131 调整配置合并逻辑为运行时 Object.assign 所有配置文件，这一行为基于 Manifest 中应只包括自身环境的配置文件，`Scanner` 中对此估计不足，现时实现不具备这一效果，导致运行时将全部 Config 合并，造成配置项混乱

Pull request:
feat(scanner): filter config with env
resolve #154 

临时方案，暂时解决 BUG，后续需对 Scanner 进行重构，理清递归过程

Patch:
diff --git a/src/loader/impl/config.ts b/src/loader/impl/config.ts
index c64ccdc..e79cc86 100644
--- a/src/loader/impl/config.ts
+++ b/src/loader/impl/config.ts
@@ -1,17 +1,13 @@
 import * as path from 'path';
 import { Container } from '@artus/injection';
 import ConfigurationHandler from '../../configuration';
-import { ArtusInjectEnum, ARTUS_DEFAULT_CONFIG_ENV, CONFIG_PATTERN } from '../../constant';
+import { ArtusInjectEnum, CONFIG_PATTERN } from '../../constant';
 import { DefineLoader } from '../decorator';
 import { ManifestItem, Loader, LoaderFindOptions } from '../types';
 import compatibleRequire from '../../utils/compatible_require';
 import { isMatch } from '../../utils';
 import { Application } from '../../types';
-
-export interface ConfigFileMeta {
-  env: string;
-  namespace?: string;
-}
+import { getConfigMetaFromFilename } from '../utils/config_file_meta';
 
 @DefineLoader('config')
 class ConfigLoader implements Loader {
@@ -42,7 +38,7 @@ class ConfigLoader implements Loader {
   }
 
   async load(item: ManifestItem) {
-    const { namespace, env } = await this.getConfigFileMeta(item);
+    const { namespace, env } = getConfigMetaFromFilename(item.filename);
     let configObj = await this.loadConfigFile(item);
     if (namespace) {
       configObj = {
@@ -53,21 +49,6 @@ class ConfigLoader implements Loader {
     return configObj;
   }
 
-  protected async getConfigFileMeta(item: ManifestItem): Promise<ConfigFileMeta> {
-    let [namespace, env, extname] = item.filename.split('.');
-    if (!extname) {
-      // No env flag, set to Default
-      env = ARTUS_DEFAULT_CONFIG_ENV.DEFAULT;
-    }
-    const meta: ConfigFileMeta = {
-      env,
-    };
-    if (namespace !== 'config') {
-      meta.namespace = namespace;
-    }
-    return meta;
-  }
-
   protected async loadConfigFile(item: ManifestItem): Promise<Record<string, any>> {
     const originConfigObj = await compatibleRequire(item.path);
     let configObj = originConfigObj;
diff --git a/src/loader/impl/framework_config.ts b/src/loader/impl/framework_config.ts
index 2b82a8f..5644c7d 100644
--- a/src/loader/impl/framework_config.ts
+++ b/src/loader/impl/framework_config.ts
@@ -4,6 +4,7 @@ import { ManifestItem, Loader, LoaderFindOptions } from '../types';
 import { FRAMEWORK_PATTERN } from '../../constant';
 import ConfigLoader from './config';
 import { isMatch } from '../../utils';
+import { getConfigMetaFromFilename } from '../utils/config_file_meta';
 
 @DefineLoader('framework-config')
 class FrameworkConfigLoader extends ConfigLoader implements Loader {
@@ -15,7 +16,7 @@ class FrameworkConfigLoader extends ConfigLoader implements Loader {
   }
 
   async load(item: ManifestItem) {
-    const { env } = await this.getConfigFileMeta(item);
+    const { env } = getConfigMetaFromFilename(item.filename);
     const configObj = (await this.loadConfigFile(item)) as FrameworkObject;
     this.configurationHandler.addFramework(item.source || 'app', configObj, {
       env,
diff --git a/src/loader/impl/plugin_config.ts b/src/loader/impl/plugin_config.ts
index 032cac0..ec1544a 100644
--- a/src/loader/impl/plugin_config.ts
+++ b/src/loader/impl/plugin_config.ts
@@ -4,6 +4,7 @@ import { PluginConfigItem } from '../../plugin/types';
 import { isMatch } from '../../utils';
 import { DefineLoader } from '../decorator';
 import { ManifestItem, Loader, LoaderFindOptions } from '../types';
+import { getConfigMetaFromFilename } from '../utils/config_file_meta';
 import ConfigLoader from './config';
 
 @DefineLoader('plugin-config')
@@ -16,7 +17,7 @@ class PluginConfigLoader extends ConfigLoader implements Loader {
   }
 
   async load(item: ManifestItem) {
-    const { env } = await this.getConfigFileMeta(item);
+    const { env } = getConfigMetaFromFilename(item.filename);
     const configObj = await this.loadConfigFile(item);
     for (const pluginName of Object.keys(configObj)) {
       const pluginConfigItem: PluginConfigItem = configObj[pluginName];
diff --git a/src/loader/utils/config_file_meta.ts b/src/loader/utils/config_file_meta.ts
new file mode 100644
index 0000000..6c2331c
--- /dev/null
+++ b/src/loader/utils/config_file_meta.ts
@@ -0,0 +1,21 @@
+import { ARTUS_DEFAULT_CONFIG_ENV } from '../../constant';
+
+export interface ConfigFileMeta {
+  env: string;
+  namespace?: string;
+}
+
+export const getConfigMetaFromFilename = (filename: string): ConfigFileMeta => {
+  let [namespace, env, extname] = filename.split('.');
+  if (!extname) {
+    // No env flag, set to Default
+    env = ARTUS_DEFAULT_CONFIG_ENV.DEFAULT;
+  }
+  const meta: ConfigFileMeta = {
+    env,
+  };
+  if (namespace !== 'config') {
+    meta.namespace = namespace;
+  }
+  return meta;
+};
diff --git a/src/scanner/scan.ts b/src/scanner/scan.ts
index b166816..d6b47af 100644
--- a/src/scanner/scan.ts
+++ b/src/scanner/scan.ts
@@ -17,6 +17,7 @@ import { FrameworkConfig, FrameworkHandler } from '../framework';
 import { BasePlugin, PluginFactory } from '../plugin';
 import { ScanUtils } from './utils';
 import { PluginMetadata } from '../plugin/types';
+import { getConfigMetaFromFilename } from '../loader/utils/config_file_meta';
 
 export class Scanner {
   private moduleExtensions = ['.js', '.json', '.node'];
@@ -90,16 +91,17 @@ export class Scanner {
     // 0. init clean itemMap
     await this.initItemMap();
 
+    // 1. Pre-Scan all config files
     const config = await this.getAllConfig(root, env);
 
-    // 1. scan all file in framework
+    // 2. scan all file in framework
     const frameworkDirs = await this.getFrameworkDirs(config.framework, root, env);
     for (const frameworkDir of frameworkDirs) {
       await this.walk(frameworkDir, this.formatWalkOptions('framework', frameworkDir));
     }
 
 
-    // 2. scan all file in plugin
+    // 3. scan all file in plugin
     if (this.tmpConfigStore.has(env)) {
       const configList = this.tmpConfigStore.get(env) ?? [];
       configList.forEach(config => this.configHandle.setConfig(env, config));
@@ -115,11 +117,11 @@ export class Scanner {
       );
     }
 
-    // 3. scan all file in app
+    // 4. scan all file in app
     await this.walk(root, this.formatWalkOptions('app', root, ''));
 
     const result: Manifest = {
-      items: this.getItemsFromMap(this.options.useRelativePath, root),
+      items: this.getItemsFromMap(this.options.useRelativePath, root, env),
       relative: this.options.useRelativePath,
     };
     return result;
@@ -237,16 +239,28 @@ export class Scanner {
     return opts;
   }
 
-  private getItemsFromMap(relative: boolean, appRoot: string): ManifestItem[] {
+  private getItemsFromMap(relative: boolean, appRoot: string, env: string): ManifestItem[] {
     let items: ManifestItem[] = [];
     for (const [, unitItems] of this.itemMap) {
       items = items.concat(unitItems);
     }
     relative && items.forEach(item => (item.path = path.relative(appRoot, item.path)));
-    return items.filter(item => (
+    return items.filter(item => {
       // remove PluginConfig to avoid re-merge on application running
-      item.loader !== 'plugin-config'
-    ));
+      if (item.loader === 'plugin-config') {
+        return false;
+      }
+
+      // remove other env config
+      if (item.loader === 'config' || item.loader === 'framework-config') {
+        const { env: filenameEnv } = getConfigMetaFromFilename(item.filename);
+        if (env !== filenameEnv && filenameEnv !== ARTUS_DEFAULT_CONFIG_ENV.DEFAULT) {
+          return false;
+        }
+      }
+
+      return true;
+    });
   }
 
   private async writeFile(filename = 'manifest.json', data: string) {


Test patch:
diff --git a/test/fixtures/app_koa_with_ts/src/config/config.dev.ts b/test/fixtures/app_koa_with_ts/src/config/config.dev.ts
new file mode 100644
index 0000000..9b863dc
--- /dev/null
+++ b/test/fixtures/app_koa_with_ts/src/config/config.dev.ts
@@ -0,0 +1,4 @@
+export default {
+  name: 'artus',
+  testStr: 'This is dev config',
+};
diff --git a/test/scanner.test.ts b/test/scanner.test.ts
index dbdcb2d..92ac416 100644
--- a/test/scanner.test.ts
+++ b/test/scanner.test.ts
@@ -31,7 +31,8 @@ describe('test/scanner.test.ts', () => {
     // console.log('devManifest', devManifest);
     expect(devManifest).toBeDefined();
     expect(devManifest.items).toBeDefined();
-    expect(devManifest.items.length).toBe(12);
+    expect(devManifest.items.length).toBe(13);
+    expect(devManifest.items.filter(item => item.loader === 'config').length).toBe(2);
     expect(devManifest.items.filter(item => item.loader === 'plugin-meta').length).toBe(2);
     expect(devManifest.items.find(item => item.unitName === 'testDuplicate')).toBeDefined();
   });