From ba0649dd91b5164abf6d27f45cd39ab773956175 Mon Sep 17 00:00:00 2001 From: Noah Gao Date: Thu, 18 May 2023 10:49:16 +0800 Subject: [PATCH] refactor: next version manifest & scanner (#245) * feat(scanner): refactor scanner workflow by task-queue (#242) * refactor: support load manifest version 2 (#243) * refactor(loader): strip findLoader * refactor(loader): support manifest version 2 * refactor: remove framework (#244) * feat: scanner typings & config handler * feat: support env list * refactor: not support manifest version 1 * test: better windows support * refactor: remove trigger & pipeline * refactor: fixed order for walkDir * feat: scanner support manifest filePath * refactor: optimize scanner to speed up * fix: scanner resolve plugin root * fix: scanner exclude path * refactor: use IoC to manage env * refactor: use IoC to manage all runtime class * refactor(scanner): use ScanTaskRunner instead of fns * test: add plugin preset case * chore(plugin): typo * fix(scanner): order of plugin task for correct behavior of deepmerge * fix: loader/lifecycle default class scope * fix(loader): side effect of metadata * feat(scanner): multipie version check * fix(scanner): order of app * feat(scanner): add scan check for different path with same version * refactor(scanner): manage task queue by ScanTaskRunner * test: handle new relativedPath field for windows --- package.json | 7 +- src/application.ts | 59 +- src/configuration/decorator.ts | 10 - src/configuration/index.ts | 68 +-- src/constant.ts | 8 +- src/exception/impl.ts | 14 - src/framework/handler.ts | 36 -- src/framework/index.ts | 1 - src/index.ts | 2 - src/lifecycle/index.ts | 26 +- src/loader/factory.ts | 163 +++--- src/loader/helper.ts | 85 +++ src/loader/impl/config.ts | 13 +- src/loader/impl/framework_config.ts | 29 - src/loader/impl/index.ts | 4 - src/loader/impl/lifecycle.ts | 3 +- src/loader/impl/package.ts | 29 - src/loader/index.ts | 1 + src/loader/types.ts | 43 +- src/pipeline.ts | 1 - src/plugin/factory.ts | 29 +- src/plugin/impl.ts | 15 +- src/plugin/types.ts | 5 +- src/scanner/impl.ts | 57 ++ src/scanner/index.ts | 5 +- src/scanner/scan.ts | 303 ---------- src/scanner/task.ts | 263 +++++++++ src/scanner/types.ts | 38 +- src/scanner/utils.ts | 172 +++--- src/trigger/index.ts | 37 -- src/types.ts | 18 +- src/utils/index.ts | 4 +- test/__snapshots__/scanner.test.ts.snap | 547 ++++++++++++++++++ test/app.test.ts | 9 +- test/config.test.ts | 20 +- test/exception_filter.test.ts | 26 +- test/fixtures/app_empty/.gitkeep | 0 .../fixtures/app_koa_with_ts/src/bootstrap.ts | 84 ++- .../app_koa_with_ts/src/http_trigger.ts | 25 - .../src/{app.ts => lifecycle.ts} | 29 +- test/fixtures/app_with_config/bootstrap.ts | 61 +- .../config/plugin.default.ts | 10 + .../package.json | 4 + .../tsconfig.json | 4 +- .../config/plugin.default.ts | 9 + test/fixtures/app_with_preset_b/package.json | 4 + .../event => app_with_preset_b}/tsconfig.json | 4 +- .../application_specific/package.json | 3 - .../src/config/config.default.ts | 1 - .../src/config/config.private.ts | 1 - .../src/config/plugin.default.ts | 16 - .../src/controller/config.ts | 17 - .../src/controller/hello.ts | 32 - .../src/controller/plugin.ts | 39 -- .../application_specific/src/index.ts | 35 -- .../src/plugins/artus_plugin_base/meta.json | 3 - .../plugins/artus_plugin_base/package.json | 3 - .../artus_plugin_base/src/config.default.ts | 5 - .../plugins/artus_plugin_mysql_ob/meta.json | 9 - .../artus_plugin_mysql_ob/package.json | 3 - .../plugins/artus_plugin_mysql_ob/src/app.ts | 19 - .../artus_plugin_mysql_ob/src/client.ts | 21 - .../src/custom_config/config.default.ts | 5 - .../plugins/artus_plugin_mysql_rds/meta.json | 3 - .../artus_plugin_mysql_rds/package.json | 3 - .../plugins/artus_plugin_mysql_rds/src/app.ts | 19 - .../artus_plugin_mysql_rds/src/client.ts | 21 - .../src/config/config.default.ts | 5 - .../src/plugins/artus_plugin_redis/meta.json | 3 - .../plugins/artus_plugin_redis/package.json | 3 - .../src/plugins/artus_plugin_redis/src/app.ts | 19 - .../plugins/artus_plugin_redis/src/client.ts | 21 - .../src/config/config.default.ts | 5 - test/fixtures/artus_application/package.json | 3 - .../src/config/config.default.ts | 1 - .../src/config/config.private.ts | 1 - .../artus_application/src/config/framework.ts | 5 - .../src/config/plugin.default.ts | 16 - .../src/config/plugin.private.ts | 11 - .../src/controller/config.ts | 17 - .../artus_application/src/controller/hello.ts | 32 - .../src/controller/plugin.ts | 51 -- test/fixtures/artus_application/src/index.ts | 35 -- .../src/plugins/artus_plugin_base/meta.json | 3 - .../plugins/artus_plugin_base/package.json | 3 - .../artus_plugin_base/src/config.default.ts | 5 - .../plugins/artus_plugin_mysql_ob/meta.json | 9 - .../artus_plugin_mysql_ob/package.json | 3 - .../plugins/artus_plugin_mysql_ob/src/app.ts | 19 - .../artus_plugin_mysql_ob/src/client.ts | 21 - .../src/custom_config/config.default.ts | 5 - .../plugins/artus_plugin_mysql_rds/meta.json | 3 - .../artus_plugin_mysql_rds/package.json | 3 - .../plugins/artus_plugin_mysql_rds/src/app.ts | 19 - .../artus_plugin_mysql_rds/src/client.ts | 21 - .../src/config/config.default.ts | 5 - .../src/plugins/artus_plugin_redis/meta.json | 3 - .../plugins/artus_plugin_redis/package.json | 3 - .../src/plugins/artus_plugin_redis/src/app.ts | 19 - .../plugins/artus_plugin_redis/src/client.ts | 21 - .../src/config/config.default.ts | 5 - test/fixtures/exception_filter/bootstrap.ts | 99 ++-- test/fixtures/exception_filter/service.ts | 20 + .../exception_invalid_filter/bootstrap.ts | 40 +- .../exception_with_ts_yaml/bootstrap.ts | 61 +- .../fixtures/frameworks/abstract/foo/index.ts | 7 - test/fixtures/frameworks/bar/package.json | 3 - .../bar/src/config/config.default.ts | 3 - .../bar/src/config/framework.default.ts | 5 - .../bar/src/config/framework.private.ts | 5 - test/fixtures/frameworks/bar/src/http.ts | 80 --- test/fixtures/frameworks/bar/src/index.ts | 20 - test/fixtures/frameworks/bar/src/lifecycle.ts | 22 - .../frameworks/layer/foo/foo1/package.json | 3 - .../foo/foo1/src/config/config.default.ts | 7 - .../foo/foo1/src/config/config.private.ts | 3 - .../frameworks/layer/foo/foo1/src/index.ts | 13 - .../layer/foo/foo1/src/lifecycle.ts | 35 -- .../layer/foo/foo1/src/trigger/http.ts | 34 -- .../node_modules/artus_plugin_hbase/index.ts | 1 - .../node_modules/artus_plugin_hbase/meta.json | 4 - .../artus_plugin_hbase/package.json | 3 - .../artus_plugin_hbase/src/app.ts | 19 - .../artus_plugin_hbase/src/client.ts | 21 - .../src/custom_config/config.default.ts | 5 - .../artus_plugin_mysql_rds/meta.json | 3 - .../artus_plugin_mysql_rds/package.json | 3 - .../artus_plugin_mysql_rds/src/app.ts | 19 - .../artus_plugin_mysql_rds/src/client.ts | 21 - .../src/config/config.default.ts | 5 - .../frameworks/layer/foo/foo2/package.json | 3 - .../foo/foo2/src/config/config.default.ts | 8 - .../foo/foo2/src/config/config.private.ts | 1 - .../layer/foo/foo2/src/config/plugin.ts | 6 - .../frameworks/layer/foo/foo2/src/index.ts | 13 - .../layer/foo/foo2/src/lifecycle.ts | 35 -- .../layer/foo/foo2/src/trigger/http.ts | 34 -- test/fixtures/logger/src/index.ts | 57 +- .../module_with_custom_loader/src/index.ts | 37 +- test/fixtures/module_with_js/src/index.js | 42 +- test/fixtures/module_with_ts/src/index.ts | 38 +- test/fixtures/plugins/plugin_a/package.json | 5 +- .../plugins/plugin_a_other_ver/meta.json | 3 + .../plugins/plugin_a_other_ver/package.json | 4 + .../plugins/plugin_a_same_ver/meta.json | 3 + .../plugins/plugin_a_same_ver/package.json | 4 + .../plugins/preset_a/config/plugin.default.ts | 21 + test/fixtures/plugins/preset_a/meta.json | 3 + test/fixtures/plugins/preset_a/package.json | 4 + test/fixtures/plugins/preset_a/tsconfig.json | 10 + .../plugins/preset_b/config/plugin.default.ts | 16 + test/fixtures/plugins/preset_b/meta.json | 3 + test/fixtures/plugins/preset_b/package.json | 4 + test/fixtures/plugins/preset_b/tsconfig.json | 10 + .../plugins/preset_c/config/plugin.default.ts | 22 + test/fixtures/plugins/preset_c/meta.json | 3 + test/fixtures/plugins/preset_c/package.json | 4 + test/fixtures/plugins/preset_c/tsconfig.json | 10 + test/fixtures/trigger/event/app.ts | 65 --- test/fixtures/trigger/event/bootstrap.ts | 37 -- test/fixtures/trigger/event/event_trigger.ts | 19 - test/fixtures/trigger/http/app.ts | 41 -- test/fixtures/trigger/http/bootstrap.ts | 35 -- test/fixtures/trigger/http/http_trigger.ts | 33 -- test/fixtures/trigger/http/tsconfig.json | 8 - test/fixtures/trigger/timer/app.ts | 66 --- test/fixtures/trigger/timer/bootstrap.ts | 37 -- test/fixtures/trigger/timer/timer_trigger.ts | 17 - test/framework.test.ts | 60 -- test/loader.test.ts | 35 +- test/logger.test.ts | 3 +- test/plugin.test.ts | 41 +- test/scanner.test.ts | 198 ++++--- test/specific.test.ts | 132 ----- test/trigger/event.test.ts | 28 - test/trigger/http.test.ts | 21 - test/trigger/timer.test.ts | 28 - test/utils/index.ts | 42 +- 178 files changed, 1912 insertions(+), 3041 deletions(-) delete mode 100644 src/configuration/decorator.ts delete mode 100644 src/framework/handler.ts delete mode 100644 src/framework/index.ts create mode 100644 src/loader/helper.ts delete mode 100644 src/loader/impl/framework_config.ts delete mode 100644 src/loader/impl/package.ts delete mode 100644 src/pipeline.ts create mode 100644 src/scanner/impl.ts delete mode 100644 src/scanner/scan.ts create mode 100644 src/scanner/task.ts delete mode 100644 src/trigger/index.ts create mode 100644 test/__snapshots__/scanner.test.ts.snap create mode 100644 test/fixtures/app_empty/.gitkeep delete mode 100644 test/fixtures/app_koa_with_ts/src/http_trigger.ts rename test/fixtures/app_koa_with_ts/src/{app.ts => lifecycle.ts} (59%) create mode 100644 test/fixtures/app_with_plugin_version_check/config/plugin.default.ts create mode 100644 test/fixtures/app_with_plugin_version_check/package.json rename test/fixtures/{trigger/timer => app_with_plugin_version_check}/tsconfig.json (52%) create mode 100644 test/fixtures/app_with_preset_b/config/plugin.default.ts create mode 100644 test/fixtures/app_with_preset_b/package.json rename test/fixtures/{trigger/event => app_with_preset_b}/tsconfig.json (52%) delete mode 100644 test/fixtures/application_specific/package.json delete mode 100644 test/fixtures/application_specific/src/config/config.default.ts delete mode 100644 test/fixtures/application_specific/src/config/config.private.ts delete mode 100644 test/fixtures/application_specific/src/config/plugin.default.ts delete mode 100644 test/fixtures/application_specific/src/controller/config.ts delete mode 100644 test/fixtures/application_specific/src/controller/hello.ts delete mode 100644 test/fixtures/application_specific/src/controller/plugin.ts delete mode 100644 test/fixtures/application_specific/src/index.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_base/meta.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_base/package.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_base/src/config.default.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/meta.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/package.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/app.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/client.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/meta.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/package.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/app.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/client.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_redis/meta.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_redis/package.json delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/app.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/client.ts delete mode 100644 test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/config/config.default.ts delete mode 100644 test/fixtures/artus_application/package.json delete mode 100644 test/fixtures/artus_application/src/config/config.default.ts delete mode 100644 test/fixtures/artus_application/src/config/config.private.ts delete mode 100644 test/fixtures/artus_application/src/config/framework.ts delete mode 100644 test/fixtures/artus_application/src/config/plugin.default.ts delete mode 100644 test/fixtures/artus_application/src/config/plugin.private.ts delete mode 100644 test/fixtures/artus_application/src/controller/config.ts delete mode 100644 test/fixtures/artus_application/src/controller/hello.ts delete mode 100644 test/fixtures/artus_application/src/controller/plugin.ts delete mode 100644 test/fixtures/artus_application/src/index.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_base/meta.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_base/package.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_base/src/config.default.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/meta.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/package.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/app.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/client.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/meta.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/package.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/app.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/client.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_redis/meta.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_redis/package.json delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/app.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/client.ts delete mode 100644 test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/config/config.default.ts create mode 100644 test/fixtures/exception_filter/service.ts delete mode 100644 test/fixtures/frameworks/abstract/foo/index.ts delete mode 100644 test/fixtures/frameworks/bar/package.json delete mode 100644 test/fixtures/frameworks/bar/src/config/config.default.ts delete mode 100644 test/fixtures/frameworks/bar/src/config/framework.default.ts delete mode 100644 test/fixtures/frameworks/bar/src/config/framework.private.ts delete mode 100644 test/fixtures/frameworks/bar/src/http.ts delete mode 100644 test/fixtures/frameworks/bar/src/index.ts delete mode 100644 test/fixtures/frameworks/bar/src/lifecycle.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo1/package.json delete mode 100644 test/fixtures/frameworks/layer/foo/foo1/src/config/config.default.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo1/src/config/config.private.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo1/src/index.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo1/src/lifecycle.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo1/src/trigger/http.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/index.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/meta.json delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/package.json delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/app.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/client.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/custom_config/config.default.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/meta.json delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/package.json delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/app.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/client.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/config/config.default.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/package.json delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/src/config/config.default.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/src/config/config.private.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/src/config/plugin.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/src/index.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/src/lifecycle.ts delete mode 100644 test/fixtures/frameworks/layer/foo/foo2/src/trigger/http.ts create mode 100644 test/fixtures/plugins/plugin_a_other_ver/meta.json create mode 100644 test/fixtures/plugins/plugin_a_other_ver/package.json create mode 100644 test/fixtures/plugins/plugin_a_same_ver/meta.json create mode 100644 test/fixtures/plugins/plugin_a_same_ver/package.json create mode 100644 test/fixtures/plugins/preset_a/config/plugin.default.ts create mode 100644 test/fixtures/plugins/preset_a/meta.json create mode 100644 test/fixtures/plugins/preset_a/package.json create mode 100644 test/fixtures/plugins/preset_a/tsconfig.json create mode 100644 test/fixtures/plugins/preset_b/config/plugin.default.ts create mode 100644 test/fixtures/plugins/preset_b/meta.json create mode 100644 test/fixtures/plugins/preset_b/package.json create mode 100644 test/fixtures/plugins/preset_b/tsconfig.json create mode 100644 test/fixtures/plugins/preset_c/config/plugin.default.ts create mode 100644 test/fixtures/plugins/preset_c/meta.json create mode 100644 test/fixtures/plugins/preset_c/package.json create mode 100644 test/fixtures/plugins/preset_c/tsconfig.json delete mode 100644 test/fixtures/trigger/event/app.ts delete mode 100644 test/fixtures/trigger/event/bootstrap.ts delete mode 100644 test/fixtures/trigger/event/event_trigger.ts delete mode 100644 test/fixtures/trigger/http/app.ts delete mode 100644 test/fixtures/trigger/http/bootstrap.ts delete mode 100644 test/fixtures/trigger/http/http_trigger.ts delete mode 100644 test/fixtures/trigger/http/tsconfig.json delete mode 100644 test/fixtures/trigger/timer/app.ts delete mode 100644 test/fixtures/trigger/timer/bootstrap.ts delete mode 100644 test/fixtures/trigger/timer/timer_trigger.ts delete mode 100644 test/framework.test.ts delete mode 100644 test/specific.test.ts delete mode 100644 test/trigger/event.test.ts delete mode 100644 test/trigger/http.test.ts delete mode 100644 test/trigger/timer.test.ts diff --git a/package.json b/package.json index b0ec60d9..f61cd41c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@artus/core", - "version": "1.0.9", + "version": "2.0.0", "description": "Core package of Artus", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -13,10 +13,6 @@ "types": "./lib/injection.d.ts", "default": "./lib/injection.js" }, - "./pipeline": { - "types": "./lib/pipeline.d.ts", - "default": "./lib/pipeline.js" - }, "./utils/*": { "types": "./lib/utils/*.d.ts", "default": "./lib/utils/*.js" @@ -66,7 +62,6 @@ }, "dependencies": { "@artus/injection": "^0.5.1", - "@artus/pipeline": "^0.2.2", "deepmerge": "^4.2.2", "minimatch": "^5.0.1" }, diff --git a/src/application.ts b/src/application.ts index e0dcf1af..31b86f39 100644 --- a/src/application.ts +++ b/src/application.ts @@ -5,7 +5,6 @@ import { ArtusStdError } from './exception'; import { HookFunction, LifecycleManager } from './lifecycle'; import { LoaderFactory, Manifest } from './loader'; import { Application, ApplicationInitOptions } from './types'; -import Trigger from './trigger'; import ConfigurationHandler from './configuration'; import { Logger, LoggerType } from './logger'; @@ -13,16 +12,15 @@ export class ArtusApplication implements Application { public manifest?: Manifest; public container: Container; - protected lifecycleManager: LifecycleManager; - protected loaderFactory: LoaderFactory; - constructor(opts?: ApplicationInitOptions) { this.container = new Container(opts?.containerName ?? ArtusInjectEnum.DefaultContainerName); - this.lifecycleManager = new LifecycleManager(this, this.container); - this.loaderFactory = LoaderFactory.create(this.container); - this.addLoaderListener(); + if (opts?.env) { + const envList = [].concat(opts.env); + this.container.set({ id: ArtusInjectEnum.EnvList, value: envList }); + } this.loadDefaultClass(); + this.addLoaderListener(); process.on('SIGINT', () => this.close(true)); process.on('SIGTERM', () => this.close(true)); @@ -32,16 +30,16 @@ export class ArtusApplication implements Application { return this.container.get(ArtusInjectEnum.Config); } - get frameworks(): Record { - return this.container.get(ArtusInjectEnum.Frameworks); + get configurationHandler(): ConfigurationHandler { + return this.container.get(ConfigurationHandler); } - get packages(): Record { - return this.container.get(ArtusInjectEnum.Packages); + get lifecycleManager(): LifecycleManager { + return this.container.get(LifecycleManager); } - get configurationHandler(): ConfigurationHandler { - return this.container.get(ConfigurationHandler); + get loaderFactory(): LoaderFactory { + return this.container.get(LoaderFactory); } get logger(): LoggerType { @@ -52,18 +50,19 @@ export class ArtusApplication implements Application { // load Artus default clazz this.container.set({ id: Container, value: this.container }); this.container.set({ id: ArtusInjectEnum.Application, value: this }); - this.container.set({ id: ArtusInjectEnum.LifecycleManager, value: this.lifecycleManager }); + this.container.set({ id: ArtusInjectEnum.Config, value: {} }); this.container.set({ type: ConfigurationHandler }); + this.container.set({ type: LoaderFactory }); + this.container.set({ type: LifecycleManager }); this.container.set({ type: Logger }); - this.container.set({ type: Trigger }); } async load(manifest: Manifest, root: string = process.cwd()) { // Load user manifest this.manifest = manifest; - await this.loaderFactory.loadManifest(manifest, manifest.relative ? root : undefined); + await this.loaderFactory.loadManifest(manifest, root); await this.lifecycleManager.emitHook('didLoad'); @@ -103,26 +102,18 @@ export class ArtusApplication implements Application { .addLoaderListener('config', { before: () => this.lifecycleManager.emitHook('configWillLoad'), after: () => { - this.container.set({ - id: ArtusInjectEnum.Config, - value: this.configurationHandler.getAllConfig(), - }); + this.updateConfig(); return this.lifecycleManager.emitHook('configDidLoad'); }, - }) - .addLoaderListener('framework-config', { - after: () => - this.container.set({ - id: ArtusInjectEnum.Frameworks, - value: this.configurationHandler.getFrameworkConfig(), - }), - }) - .addLoaderListener('package-json', { - after: () => - this.container.set({ - id: ArtusInjectEnum.Packages, - value: this.configurationHandler.getPackages(), - }), }); } + + protected updateConfig() { + const oldConfig = this.container.get(ArtusInjectEnum.Config, { noThrow: true }) ?? {}; + const newConfig = this.configurationHandler.getMergedConfig() ?? {}; + this.container.set({ + id: ArtusInjectEnum.Config, + value: Object.assign(oldConfig, newConfig), + }); + } } diff --git a/src/configuration/decorator.ts b/src/configuration/decorator.ts deleted file mode 100644 index f7172367..00000000 --- a/src/configuration/decorator.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { HOOK_CONFIG_HANDLE } from "../constant"; - -export function DefineConfigHandle(handleName?: string): PropertyDecorator { - return (target: any, propertyKey: string | symbol) => { - if (typeof propertyKey === 'symbol') { - throw new Error(`hookName is not support symbol [${propertyKey.description}]`); - } - Reflect.defineMetadata(`${HOOK_CONFIG_HANDLE}${handleName}`, propertyKey, target.constructor); - }; -} \ No newline at end of file diff --git a/src/configuration/index.ts b/src/configuration/index.ts index 6491f0d5..98311eef 100644 --- a/src/configuration/index.ts +++ b/src/configuration/index.ts @@ -1,9 +1,8 @@ -import { Injectable } from '@artus/injection'; -import { ARTUS_DEFAULT_CONFIG_ENV, ARTUS_SERVER_ENV } from '../constant'; +import { Container, Inject, Injectable } from '@artus/injection'; +import { ArtusInjectEnum, ARTUS_DEFAULT_CONFIG_ENV, ARTUS_SERVER_ENV } from '../constant'; import { ManifestItem } from '../loader'; import { mergeConfig } from '../loader/utils/merge'; import compatibleRequire from '../utils/compatible_require'; -import { DefineConfigHandle } from './decorator'; export type ConfigObject = Record; export type FrameworkObject = { path: string; env: string }; @@ -20,15 +19,19 @@ export default class ConfigurationHandler { return env; } - private configStore: Map = new Map(); - private frameworks: Map = new Map(); - private packages: Map = new Map(); + public configStore: Map = new Map(); - getMergedConfig(env?: string): ConfigObject { - const currentEnv = env ?? process.env[ARTUS_SERVER_ENV] ?? ARTUS_DEFAULT_CONFIG_ENV.DEV; + @Inject() + private container: Container; + + getMergedConfig(): ConfigObject { + let envList: string[] = this.container.get(ArtusInjectEnum.EnvList, { noThrow: true }); + if (!envList) { + envList = process.env[ARTUS_SERVER_ENV] ? [process.env[ARTUS_SERVER_ENV]] : [ARTUS_DEFAULT_CONFIG_ENV.DEV]; + } const defaultConfig = this.configStore.get(ARTUS_DEFAULT_CONFIG_ENV.DEFAULT) ?? {}; - const envConfig = this.configStore.get(currentEnv) ?? {}; - return mergeConfig(defaultConfig, envConfig); + const envConfigList = envList.map(currentEnv => (this.configStore.get(currentEnv) ?? {})); + return mergeConfig(defaultConfig, ...envConfigList); } getAllConfig(): ConfigObject { @@ -39,6 +42,10 @@ export default class ConfigurationHandler { return mergeConfig(defaultConfig, ...keys.map(key => this.configStore.get(key) ?? {})); } + clearStore(): void { + this.configStore.clear(); + } + setConfig(env: string, config: ConfigObject) { const storedConfig = this.configStore.get(env) ?? {}; this.configStore.set(env, mergeConfig(storedConfig, config)); @@ -51,45 +58,4 @@ export default class ConfigurationHandler { this.setConfig(env, configContent); } } - - @DefineConfigHandle('framework-config') - getFrameworkConfig( - env?: string, - key = 'app', - frameworkMap = new Map(), - ): Map { - if (!this.frameworks.has(key)) { - return frameworkMap; - } - const currentEnv = env ?? process.env[ARTUS_SERVER_ENV] ?? ARTUS_DEFAULT_CONFIG_ENV.DEV; - const list = this.frameworks.get(key) as unknown as FrameworkObject[]; - const defaultConfig = - list.filter(item => item.env === ARTUS_DEFAULT_CONFIG_ENV.DEFAULT)[0] ?? {}; - const envConfig = list.filter(item => item.env === currentEnv)[0] ?? {}; - const config = mergeConfig(defaultConfig, envConfig) as unknown as FrameworkObject; - frameworkMap.set(key, config); - - if (config.path) { - this.getFrameworkConfig(env, config.path, frameworkMap); - } - return frameworkMap; - } - - addFramework(source: string, framework: FrameworkObject, options: FrameworkOptions) { - const key = options.unitName || source; - const list = this.frameworks.get(key) ?? []; - framework.env = options.env; - list.push(framework); - this.frameworks.set(key, list); - } - - getPackages(): Map { - return this.packages; - } - - addPackage(source: string, pkg: PackageObject) { - const list = this.packages.get(source) ?? []; - list.push(pkg); - this.packages.set(source, list); - } } diff --git a/src/constant.ts b/src/constant.ts index 0fa47c03..be29418c 100644 --- a/src/constant.ts +++ b/src/constant.ts @@ -7,9 +7,7 @@ export enum ArtusInjectEnum { Application = 'artus#application', Config = 'artus#config', DefaultContainerName = 'artus#default_container', - Frameworks = 'artus#framework-config', - LifecycleManager = 'artus#lifecycle-manager', - Packages = 'artus#packages', + EnvList = 'artus#env_list', } export enum ARTUS_DEFAULT_CONFIG_ENV { @@ -30,7 +28,6 @@ export const ARTUS_SERVER_ENV = 'ARTUS_SERVER_ENV'; export const HOOK_NAME_META_PREFIX = 'hookName:'; export const HOOK_FILE_LOADER = 'appHook:fileLoader'; -export const HOOK_CONFIG_HANDLE = 'appHook:configHandle::'; export const DEFAULT_EXCLUDES = [ 'test', @@ -43,6 +40,8 @@ export const DEFAULT_EXCLUDES = [ 'LICENSE', 'pnpm-lock.yaml', ]; +export const DEFAULT_MODULE_EXTENSIONS = ['.js', '.json', '.node']; +export const DEFAULT_APP_REF = '_app'; export const FRAMEWORK_PATTERN = 'framework.*'; export const PLUGIN_CONFIG_PATTERN = 'plugin.*'; @@ -62,5 +61,6 @@ export const DEFAULT_LOADER_LIST_WITH_ORDER = [ ]; export const DEFAULT_CONFIG_DIR = 'src/config'; +export const DEFAULT_MANIFEST_FILENAME = 'manifest.json'; export const SHOULD_OVERWRITE_VALUE = 'shouldOverwrite'; diff --git a/src/exception/impl.ts b/src/exception/impl.ts index 0bb9419c..01f24847 100644 --- a/src/exception/impl.ts +++ b/src/exception/impl.ts @@ -1,19 +1,5 @@ -import { Middleware } from '@artus/pipeline'; import { ARTUS_EXCEPTION_DEFAULT_LOCALE } from '../constant'; import { ExceptionItem } from './types'; -import { matchExceptionFilter } from './utils'; - -export const exceptionFilterMiddleware: Middleware = async (ctx, next) => { - try { - await next(); - } catch (err) { - const filter = matchExceptionFilter(err, ctx.container); - if (filter) { - await filter.catch(err); - } - throw err; - } -}; export class ArtusStdError extends Error { name = 'ArtusStdError'; diff --git a/src/framework/handler.ts b/src/framework/handler.ts deleted file mode 100644 index 59537e37..00000000 --- a/src/framework/handler.ts +++ /dev/null @@ -1,36 +0,0 @@ -import path from 'path'; -import ConfigurationHandler, { ConfigObject } from '../configuration'; -import { ManifestItem } from '../loader/types'; - -export interface FrameworkConfig { - path?: string, - package?: string -} - -export class FrameworkHandler { - static async mergeConfig(env: string, frameworks: ManifestItem[], done: string[]): Promise<{ config: ConfigObject, done: string[] }> { - frameworks = frameworks.filter(item => !done.includes(item.path)); - const frameworkConfigHandler = new ConfigurationHandler(); - for (const frameworkConfigFile of frameworks) { - await frameworkConfigHandler.setConfigByFile(frameworkConfigFile); - } - - done = done.concat(frameworks.map(item => item.path)); - - return { config: frameworkConfigHandler.getMergedConfig(env), done }; - } - - static async handle(root: string, config: FrameworkConfig): Promise { - // no framework - if (!config.path && !config.package) { - return ''; - } - - try { - const baseFrameworkPath = config.path ?? path.dirname(require.resolve(config.package ?? '', { paths: [root] })); - return baseFrameworkPath; - } catch (err) { - throw new Error(`load framework faild: ${err}, framework config: ${JSON.stringify(config)}`); - } - } -} diff --git a/src/framework/index.ts b/src/framework/index.ts deleted file mode 100644 index 68ae53f6..00000000 --- a/src/framework/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './handler'; diff --git a/src/index.ts b/src/index.ts index efebf7d3..d584dce0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,5 +14,3 @@ export * from './constant'; import ConfigurationHandler from './configuration'; export { ConfigurationHandler }; -import Trigger from './trigger'; -export { Trigger }; diff --git a/src/lifecycle/index.ts b/src/lifecycle/index.ts index fd7dcd33..f8b69bfc 100644 --- a/src/lifecycle/index.ts +++ b/src/lifecycle/index.ts @@ -1,6 +1,7 @@ -import { Constructable, Container } from '@artus/injection'; +import { Constructable, Container, Inject, Injectable, ScopeEnum } from '@artus/injection'; import { Application, ApplicationLifecycle } from '../types'; import { + ArtusInjectEnum, HOOK_NAME_META_PREFIX, } from '../constant'; @@ -10,8 +11,13 @@ export type HookFunction = (hookProps: { payload?: T }) => void | Promise; +@Injectable({ + scope: ScopeEnum.SINGLETON, +}) export class LifecycleManager { - hookList: string[] = [ + public enable = true; // Enabled default, will NOT emit when enable is false + + private hookList: string[] = [ 'configWillLoad', // 配置文件即将加载,这是最后动态修改配置的时机 'configDidLoad', // 配置文件加载完成 'didLoad', // 文件加载完成 @@ -19,15 +25,14 @@ export class LifecycleManager { 'didReady', // 应用启动完成 'beforeClose', // 应用即将关闭 ]; - hookFnMap: Map = new Map(); - private app: Application; - private container: Container; + private hookFnMap: Map = new Map(); private hookUnitSet: Set> = new Set(); - constructor(app: Application, container: Container) { - this.app = app; - this.container = container; - } + @Inject(ArtusInjectEnum.Application) + private app: Application; + + @Inject() + private container: Container; insertHook(existHookName: string, newHookName: string) { const startIndex = this.hookList.findIndex(val => val === existHookName); @@ -66,6 +71,9 @@ export class LifecycleManager { } async emitHook(hookName: string, payload?: T) { + if (!this.enable) { + return; + } if (!this.hookFnMap.has(hookName)) { return; } diff --git a/src/loader/factory.ts b/src/loader/factory.ts index 44733777..2d551670 100644 --- a/src/loader/factory.ts +++ b/src/loader/factory.ts @@ -1,47 +1,46 @@ import * as path from 'path'; -import { isInjectable, Container } from '@artus/injection'; -import { ArtusInjectEnum, DEFAULT_LOADER, HOOK_FILE_LOADER, LOADER_NAME_META, ScanPolicy, DEFAULT_LOADER_LIST_WITH_ORDER } from '../constant'; +import { Container, Injectable, Inject, ScopeEnum } from '@artus/injection'; +import { DEFAULT_LOADER, LOADER_NAME_META, DEFAULT_LOADER_LIST_WITH_ORDER, DEFAULT_APP_REF } from '../constant'; import { Manifest, ManifestItem, LoaderConstructor, Loader, - LoaderFindOptions, - LoaderFindResult, } from './types'; import ConfigurationHandler from '../configuration'; import { LifecycleManager } from '../lifecycle'; -import compatibleRequire from '../utils/compatible_require'; import LoaderEventEmitter, { LoaderEventListener } from './loader_event'; -import { isClass } from '../utils/is'; +import { PluginConfigItem, PluginFactory } from '../plugin'; +import { Logger, LoggerType } from '../logger'; +@Injectable({ + scope: ScopeEnum.SINGLETON, +}) export class LoaderFactory { - private container: Container; - private static loaderClazzMap: Map = new Map(); - private loaderEmitter: LoaderEventEmitter; + public static loaderClazzMap: Map = new Map(); static register(clazz: LoaderConstructor) { const loaderName = Reflect.getMetadata(LOADER_NAME_META, clazz); this.loaderClazzMap.set(loaderName, clazz); } - constructor(container: Container) { - this.container = container; - this.loaderEmitter = new LoaderEventEmitter(); - } + @Inject() + private container: Container; - static create(container: Container): LoaderFactory { - return new LoaderFactory(container); - } + private loaderEmitter: LoaderEventEmitter = new LoaderEventEmitter(); get lifecycleManager(): LifecycleManager { - return this.container.get(ArtusInjectEnum.LifecycleManager); + return this.container.get(LifecycleManager); } get configurationHandler(): ConfigurationHandler { return this.container.get(ConfigurationHandler); } + get logger(): LoggerType { + return this.container.get(Logger); + } + addLoaderListener(eventName: string, listener: LoaderEventListener) { this.loaderEmitter.addListener(eventName, listener); return this; @@ -60,8 +59,53 @@ export class LoaderFactory { return new LoaderClazz(this.container); } - async loadManifest(manifest: Manifest, root?: string): Promise { - await this.loadItemList(manifest.items, root); + async loadManifest( + manifest: Manifest, + root: string = process.cwd(), + ): Promise { + if (!('version' in manifest) || manifest.version !== '2') { + throw new Error(`invalid manifest, @artus/core@2.x only support manifest version 2.`); + } + // Manifest Version 2 is supported mainly + + // Merge plugin config with ref + for (const [env, pluginConfig] of Object.entries(manifest.pluginConfig ?? {})) { + this.configurationHandler.setConfig(env, { + plugin: pluginConfig, + }); + } + const mergedPluginConfig: Record = Object.assign( + {}, + this.configurationHandler.getMergedConfig()?.plugin ?? {}, + ); // shallow copy to avoid side effect of writing metadata + for (const [pluginName, pluginConfigItem] of Object.entries(mergedPluginConfig)) { + const refItem = manifest.refMap[pluginConfigItem.refName]; + if (!refItem) { + continue; + } + mergedPluginConfig[pluginName] = { + ...pluginConfigItem, + metadata: refItem.pluginMetadata, + }; + } + + // sort ref(plugin) firstly + const sortedPluginList = await PluginFactory.createFromConfig(mergedPluginConfig, { + logger: this.logger, + }); + + // Merge itemList + let itemList: ManifestItem[] = []; + const sortedRefNameList: (string | null)[] = sortedPluginList + .map(plugin => ((plugin.enable && mergedPluginConfig[plugin.name]?.refName) || null)) + .concat([DEFAULT_APP_REF]); + for (const refName of sortedRefNameList) { + const refItem = manifest.refMap[refName]; + itemList = itemList.concat(refItem.items); + } + + // Load final item list(non-ordered) + await this.loadItemList(itemList, root); } async loadItemList(itemList: ManifestItem[] = [], root?: string): Promise { @@ -73,15 +117,19 @@ export class LoaderFactory { // compatible for custom loader itemMap.set(item.loader, []); } + let resolvedPath = item.path; + if (root && !path.isAbsolute(resolvedPath)) { + resolvedPath = path.resolve(root, resolvedPath); + } itemMap.get(item.loader)!.push({ ...item, - path: root ? path.join(root, item.path) : item.path, + path: resolvedPath, loader: item.loader ?? DEFAULT_LOADER, }); } // trigger loader - for (const [ loaderName, itemList ] of itemMap) { + for (const [loaderName, itemList] of itemMap) { await this.loaderEmitter.emitBefore(loaderName); for (const item of itemList) { @@ -101,77 +149,4 @@ export class LoaderFactory { loader.state = item.loaderState; return loader.load(item); } - - async findLoader(opts: LoaderFindOptions): Promise { - const { loader: loaderName, exportNames } = await this.findLoaderName(opts); - - if (!loaderName) { - return null; - } - - const loaderClazz = LoaderFactory.loaderClazzMap.get(loaderName); - if (!loaderClazz) { - throw new Error(`Cannot find loader '${loaderName}'`); - } - const result: LoaderFindResult = { - loaderName, - loaderState: { exportNames }, - }; - if (loaderClazz.onFind) { - result.loaderState = await loaderClazz.onFind(opts); - } - return result; - } - - async findLoaderName(opts: LoaderFindOptions): Promise<{ loader: string | null, exportNames: string[] }> { - for (const [loaderName, LoaderClazz] of LoaderFactory.loaderClazzMap.entries()) { - if (await LoaderClazz.is?.(opts)) { - return { loader: loaderName, exportNames: [] }; - } - } - const { root, filename, policy = ScanPolicy.All } = opts; - - // require file for find loader - const allExport = await compatibleRequire(path.join(root, filename), true); - const exportNames: string[] = []; - - let loaders = Object.entries(allExport) - .map(([name, targetClazz]) => { - if (!isClass(targetClazz)) { - // The file is not export with default class - return null; - } - - if (policy === ScanPolicy.NamedExport && name === 'default') { - return null; - } - - if (policy === ScanPolicy.DefaultExport && name !== 'default') { - return null; - } - - // get loader from reflect metadata - const loaderMd = Reflect.getMetadata(HOOK_FILE_LOADER, targetClazz); - if (loaderMd?.loader) { - exportNames.push(name); - return loaderMd.loader; - } - - // default loder with @Injectable - const injectableMd = isInjectable(targetClazz); - if (injectableMd) { - exportNames.push(name); - return DEFAULT_LOADER; - } - }) - .filter(v => v); - - loaders = Array.from(new Set(loaders)); - - if (loaders.length > 1) { - throw new Error(`Not support multiple loaders for ${path.join(root, filename)}`); - } - - return { loader: loaders[0] ?? null, exportNames }; - } } diff --git a/src/loader/helper.ts b/src/loader/helper.ts new file mode 100644 index 00000000..f7272232 --- /dev/null +++ b/src/loader/helper.ts @@ -0,0 +1,85 @@ +import path from 'path'; +import { isInjectable } from '@artus/injection'; +import { LoaderFactory } from './factory'; +import { + LoaderFindOptions, + LoaderFindResult, +} from './types'; +import { ScanPolicy, HOOK_FILE_LOADER, DEFAULT_LOADER } from '../constant'; +import compatibleRequire from '../utils/compatible_require'; +import { isClass } from '../utils/is'; + +export const findLoaderName = async (opts: LoaderFindOptions): Promise<{ loader: string | null, exportNames: string[] }> => { + // Use Loader.is to find loader + for (const [loaderName, LoaderClazz] of LoaderFactory.loaderClazzMap.entries()) { + if (await LoaderClazz.is?.(opts)) { + return { loader: loaderName, exportNames: [] }; + } + } + const { root, filename, policy = ScanPolicy.All } = opts; + + // require file for find loader + const allExport = await compatibleRequire(path.join(root, filename), true); + const exportNames: string[] = []; + + let loaders = Object.entries(allExport) + .map(([name, targetClazz]) => { + if (!isClass(targetClazz)) { + // The file is not export with default class + return null; + } + + if (policy === ScanPolicy.NamedExport && name === 'default') { + return null; + } + + if (policy === ScanPolicy.DefaultExport && name !== 'default') { + return null; + } + + // get loader from reflect metadata + const loaderMd = Reflect.getMetadata(HOOK_FILE_LOADER, targetClazz); + if (loaderMd?.loader) { + exportNames.push(name); + return loaderMd.loader; + } + + // default loder with @Injectable + const injectableMd = isInjectable(targetClazz); + if (injectableMd) { + exportNames.push(name); + return DEFAULT_LOADER; + } + }) + .filter(v => v); + + loaders = Array.from(new Set(loaders)); + + if (loaders.length > 1) { + throw new Error(`Not support multiple loaders for ${path.join(root, filename)}`); + } + + return { loader: loaders[0] ?? null, exportNames }; +}; + +export const findLoader = async (opts: LoaderFindOptions): Promise => { + const { loader: loaderName, exportNames } = await findLoaderName(opts); + + if (!loaderName) { + return null; + } + + const loaderClazz = LoaderFactory.loaderClazzMap.get(loaderName); + if (!loaderClazz) { + throw new Error(`Cannot find loader '${loaderName}'`); + } + const result: LoaderFindResult = { + loaderName, + loaderState: { exportNames }, + }; + if (loaderClazz.onFind) { + result.loaderState = await loaderClazz.onFind(opts); + } + return result; +}; + diff --git a/src/loader/impl/config.ts b/src/loader/impl/config.ts index 9c597f2f..b0c1575c 100644 --- a/src/loader/impl/config.ts +++ b/src/loader/impl/config.ts @@ -8,7 +8,7 @@ import compatibleRequire from '../../utils/compatible_require'; import { isMatch } from '../../utils'; import { Application } from '../../types'; import { getConfigMetaFromFilename } from '../utils/config_file_meta'; -import { PluginFactory } from '../../plugin'; +// import { PluginFactory } from '../../plugin'; @DefineLoader('config') class ConfigLoader implements Loader { @@ -41,11 +41,12 @@ class ConfigLoader implements Loader { async load(item: ManifestItem) { const { namespace, env } = getConfigMetaFromFilename(item.filename); let configObj = await this.loadConfigFile(item); - if (namespace === 'plugin') { - configObj = { - plugin: await PluginFactory.formatPluginConfig(configObj, item), - }; - } else if (namespace) { + // if (namespace === 'plugin') { + // configObj = { + // plugin: await PluginFactory.formatPluginConfig(configObj, item), + // }; + // } else + if (namespace) { configObj = { [namespace]: configObj, }; diff --git a/src/loader/impl/framework_config.ts b/src/loader/impl/framework_config.ts deleted file mode 100644 index 5644c7d1..00000000 --- a/src/loader/impl/framework_config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { FrameworkObject } from '../../configuration'; -import { DefineLoader } from '../decorator'; -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 { - static async is(opts: LoaderFindOptions): Promise { - if (this.isConfigDir(opts)) { - return isMatch(opts.filename, FRAMEWORK_PATTERN); - } - return false; - } - - async load(item: ManifestItem) { - const { env } = getConfigMetaFromFilename(item.filename); - const configObj = (await this.loadConfigFile(item)) as FrameworkObject; - this.configurationHandler.addFramework(item.source || 'app', configObj, { - env, - unitName: item.unitName || '', - }); - return configObj; - } -} - -export default FrameworkConfigLoader; diff --git a/src/loader/impl/index.ts b/src/loader/impl/index.ts index 5a1b8e61..3c4ca314 100644 --- a/src/loader/impl/index.ts +++ b/src/loader/impl/index.ts @@ -4,8 +4,6 @@ import ExceptionLoader from './exception'; import ExceptionFilterLoader from './exception_filter'; import LifecycleLoader from './lifecycle'; import PluginMetaLoader from './plugin_meta'; -import FrameworkConfigLoader from './framework_config'; -import PackageLoader from './package'; export { ModuleLoader, @@ -14,6 +12,4 @@ export { ExceptionFilterLoader, LifecycleLoader, PluginMetaLoader, - FrameworkConfigLoader, - PackageLoader, }; diff --git a/src/loader/impl/lifecycle.ts b/src/loader/impl/lifecycle.ts index 59e88cc0..128110da 100644 --- a/src/loader/impl/lifecycle.ts +++ b/src/loader/impl/lifecycle.ts @@ -1,5 +1,4 @@ import { Constructable, Container } from '@artus/injection'; -import { ArtusInjectEnum } from '../../constant'; import { LifecycleManager } from '../../lifecycle'; import { ApplicationLifecycle } from '../../types'; import { DefineLoader } from '../decorator'; @@ -15,7 +14,7 @@ class LifecycleLoader implements Loader { } get lifecycleManager(): LifecycleManager { - return this.container.get(ArtusInjectEnum.LifecycleManager); + return this.container.get(LifecycleManager); } async load(item: ManifestItem) { diff --git a/src/loader/impl/package.ts b/src/loader/impl/package.ts deleted file mode 100644 index 4a27dfc4..00000000 --- a/src/loader/impl/package.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Container } from '@artus/injection'; -import ConfigurationHandler from '../../configuration'; -import { DefineLoader } from '../decorator'; -import { ManifestItem, Loader, LoaderFindOptions } from '../types'; -import compatibleRequire from '../../utils/compatible_require'; -import { PACKAGE_JSON } from '../../constant'; -import { isMatch } from '../../utils'; - -@DefineLoader('package-json') -class PackageLoader implements Loader { - private container: Container; - - constructor(container) { - this.container = container; - } - - static async is(opts: LoaderFindOptions) { - return isMatch(opts.filename, PACKAGE_JSON); - } - - async load(item: ManifestItem) { - const originConfigObj = await compatibleRequire(item.path); - const configHandler = this.container.get(ConfigurationHandler); - configHandler.addPackage(item.source || 'app', originConfigObj); - return originConfigObj; - } -} - -export default PackageLoader; diff --git a/src/loader/index.ts b/src/loader/index.ts index c1bbf800..0acb4db4 100644 --- a/src/loader/index.ts +++ b/src/loader/index.ts @@ -9,6 +9,7 @@ for (const [_, impl] of Object.entries(LoaderImpls)) { LoaderFactory.register(impl); } +export * from './helper'; export * from './types'; export { diff --git a/src/loader/types.ts b/src/loader/types.ts index 6ed9ea7e..a2c36d17 100644 --- a/src/loader/types.ts +++ b/src/loader/types.ts @@ -1,14 +1,26 @@ import { Container } from '@artus/injection'; import { ScanPolicy } from '../constant'; -import { PluginConfigItem } from '../plugin/types'; +import { PluginConfig, PluginMetadata } from '../plugin/types'; -interface Manifest { +// Key: Env => PluginName => Value: PluginConfigItem +export type PluginConfigEnvMap = Record; + +export interface RefMapItem { + relativedPath?: string; + packageVersion?: string; + pluginMetadata?: PluginMetadata; items: ManifestItem[]; - pluginConfig?: Record; - relative?: boolean; +} +// Key: RefName => RefMapItem +export type RefMap = Record; + +export interface Manifest { + version: '2'; + pluginConfig: PluginConfigEnvMap; + refMap: RefMap; } -interface ManifestItem extends Record { +export interface ManifestItem extends Record { path: string; extname: string; filename: string; @@ -18,7 +30,7 @@ interface ManifestItem extends Record { loaderState?: LoaderState; } -interface LoaderFindOptions { +export interface LoaderFindOptions { filename: string; root: string; baseDir: string; @@ -26,32 +38,23 @@ interface LoaderFindOptions { policy?: ScanPolicy; } -interface LoaderFindResult { +export interface LoaderFindResult { loaderName: string; loaderState?: unknown; } -interface LoaderHookUnit { +export interface LoaderHookUnit { before?: Function; after?: Function; } -interface LoaderConstructor { - new (container: Container): Loader; +export interface LoaderConstructor { + new(container: Container): Loader; is?(opts: LoaderFindOptions): Promise; onFind?(opts: LoaderFindOptions): Promise; } -interface Loader { +export interface Loader { state?: any; load(item: ManifestItem): Promise; } -export { - Manifest, - ManifestItem, - LoaderHookUnit, - LoaderConstructor, - Loader, - LoaderFindOptions, - LoaderFindResult, -}; diff --git a/src/pipeline.ts b/src/pipeline.ts deleted file mode 100644 index 499e1b6e..00000000 --- a/src/pipeline.ts +++ /dev/null @@ -1 +0,0 @@ -export * from '@artus/pipeline'; \ No newline at end of file diff --git a/src/plugin/factory.ts b/src/plugin/factory.ts index b0f2e5b0..e8cdd428 100644 --- a/src/plugin/factory.ts +++ b/src/plugin/factory.ts @@ -1,9 +1,6 @@ -import path from 'path'; -import { ManifestItem } from '../loader'; -import { getInlinePackageEntryPath, getPackagePath, topologicalSort } from './common'; +import { topologicalSort } from './common'; import { Plugin } from './impl'; import { PluginConfigItem, PluginCreateOptions, PluginMap, PluginType } from './types'; -import { exists } from '../utils/fs'; export class PluginFactory { static async create(name: string, item: PluginConfigItem, opts?: PluginCreateOptions): Promise { @@ -33,28 +30,4 @@ export class PluginFactory { } return pluginSortResult.map(name => pluginInstanceMap.get(name)!); } - - static async formatPluginConfig(config: Record, manifestItem?: ManifestItem): Promise> { - const newConfig: Record = {}; - const loaderState = manifestItem?.loaderState as { baseDir: string }; - for (const pluginName of Object.keys(config)) { - const pluginConfigItem: PluginConfigItem = config[pluginName]; - if (pluginConfigItem.package) { - // convert package to path when load plugin config - if (pluginConfigItem.path) { - throw new Error( - `Plugin ${pluginName} config can't have both package and path at ${manifestItem?.path ?? 'UNKNOWN_PATH'}`, - ); - } - const requirePaths = loaderState?.baseDir ? [loaderState.baseDir] : undefined; - pluginConfigItem.path = getPackagePath(pluginConfigItem.package, requirePaths); - delete pluginConfigItem.package; - } else if (pluginConfigItem.path && await exists(path.resolve(pluginConfigItem.path, 'package.json'))) { - // plugin path is a npm package, need resolve main file - pluginConfigItem.path = await getInlinePackageEntryPath(pluginConfigItem.path); - } - newConfig[pluginName] = pluginConfigItem; - } - return newConfig; - } } diff --git a/src/plugin/impl.ts b/src/plugin/impl.ts index d02584d8..900ddc6d 100644 --- a/src/plugin/impl.ts +++ b/src/plugin/impl.ts @@ -10,7 +10,7 @@ export class Plugin implements PluginType { public name: string; public enable: boolean; public importPath = ''; - public metadata: Partial = {}; + public metadata: Partial; public metaFilePath = ''; private logger?: LoggerType; @@ -26,11 +26,14 @@ export class Plugin implements PluginType { } importPath = getPackagePath(configItem.package); } - if (!importPath) { + if (!importPath && !configItem.refName) { throw new Error(`Plugin ${name} need have path or package field`); } this.importPath = importPath; } + if (configItem.metadata) { + this.metadata = configItem.metadata; + } this.logger = opts?.logger; } @@ -52,9 +55,9 @@ export class Plugin implements PluginType { const instance = pluginMap.get(pluginName); if (!instance || !instance.enable) { if (optional) { - this.logger?.warn(`Plugin ${this.name} need have optional dependence: ${pluginName}.`); + this.logger?.warn(`Plugin ${this.name} need have optional dependency: ${pluginName}.`); } else { - throw new Error(`Plugin ${this.name} need have dependence: ${pluginName}.`); + throw new Error(`Plugin ${this.name} need have dependency: ${pluginName}.`); } } } @@ -67,6 +70,10 @@ export class Plugin implements PluginType { } private async checkAndLoadMetadata() { + // check metadata from configItem + if (this.metadata) { + return; + } // check import path if (!await exists(this.importPath)) { throw new Error(`load plugin <${this.name}> import path ${this.importPath} is not exists.`); diff --git a/src/plugin/types.ts b/src/plugin/types.ts index e40ea0e4..644a1f29 100644 --- a/src/plugin/types.ts +++ b/src/plugin/types.ts @@ -18,11 +18,14 @@ export interface PluginDependencyItem { } export interface PluginConfigItem { - enable: boolean; + enable?: boolean; path?: string; package?: string; + refName?: string; + metadata?: PluginMetadata; } +export type PluginConfig = Record; export type PluginMap = Map; export interface PluginType { diff --git a/src/scanner/impl.ts b/src/scanner/impl.ts new file mode 100644 index 00000000..bbf5fde7 --- /dev/null +++ b/src/scanner/impl.ts @@ -0,0 +1,57 @@ +import path from 'path'; +import { writeFile } from 'fs/promises'; +import { DEFAULT_CONFIG_DIR, DEFAULT_EXCLUDES, DEFAULT_MANIFEST_FILENAME, DEFAULT_MODULE_EXTENSIONS, ScanPolicy } from '../constant'; +import { Manifest } from '../loader'; +import { ScannerOptions, ScannerType } from './types'; +import { ScanTaskRunner } from './task'; + +export class ArtusScanner implements ScannerType { + private options: ScannerOptions; + + constructor(options: Partial = {}) { + this.options = { + needWriteFile: true, + manifestFilePath: DEFAULT_MANIFEST_FILENAME, + useRelativePath: true, + configDir: DEFAULT_CONFIG_DIR, + policy: ScanPolicy.All, + ...options, + exclude: DEFAULT_EXCLUDES.concat(options.exclude ?? []), + extensions: DEFAULT_MODULE_EXTENSIONS.concat(options.extensions ?? []), + }; + } + + /** + * The entrance of Scanner + */ + async scan(root: string): Promise { + // make sure the root path is absolute + if (!path.isAbsolute(root)) { + root = path.resolve(root); + } + + // Init scan-task scanner + const taskRunner = new ScanTaskRunner( + root, + this.options, + ); + + // Start scan + await taskRunner.runAll(); + + // Dump manifest + const manifestResult: Manifest = taskRunner.dump(); + if (this.options.needWriteFile) { + let { manifestFilePath } = this.options; + if (!path.isAbsolute(manifestFilePath)) { + manifestFilePath = path.resolve(root, manifestFilePath); + } + await writeFile( + manifestFilePath, + JSON.stringify(manifestResult, null, 2), + ); + } + + return manifestResult; + } +} diff --git a/src/scanner/index.ts b/src/scanner/index.ts index 9ba6b7cc..471625d9 100644 --- a/src/scanner/index.ts +++ b/src/scanner/index.ts @@ -1,4 +1,3 @@ +export * from './impl'; +export * from './types'; -export * from './scan'; - -export * from './types'; \ No newline at end of file diff --git a/src/scanner/scan.ts b/src/scanner/scan.ts deleted file mode 100644 index aae459bd..00000000 --- a/src/scanner/scan.ts +++ /dev/null @@ -1,303 +0,0 @@ -import 'reflect-metadata'; -import * as path from 'path'; -import * as fs from 'fs/promises'; -import deepmerge from 'deepmerge'; -import { Container } from '@artus/injection'; -import { - ArtusInjectEnum, - ARTUS_DEFAULT_CONFIG_ENV, - DEFAULT_CONFIG_DIR, - DEFAULT_EXCLUDES, - DEFAULT_LOADER_LIST_WITH_ORDER, - LOADER_NAME_META, - ScanPolicy, -} from '../constant'; -import { LoaderFactory, Manifest, ManifestItem } from '../loader'; -import { ScannerOptions, WalkOptions } from './types'; -import ConfigurationHandler, { ConfigObject } from '../configuration'; -import { FrameworkConfig, FrameworkHandler } from '../framework'; -import { PluginType, PluginFactory } from '../plugin'; -import { ScanUtils } from './utils'; -import { exists } from '../utils/fs'; -import { PluginConfigItem, PluginMetadata } from '../plugin/types'; -import { getConfigMetaFromFilename } from '../loader/utils/config_file_meta'; -import { Application } from '../types'; -import { ArtusApplication } from '../application'; - -export class Scanner { - private moduleExtensions = ['.js', '.json', '.node']; - private options: ScannerOptions; - private itemMap: Map = new Map(); - private tmpConfigStore: Map = new Map(); - private configHandle: ConfigurationHandler = new ConfigurationHandler(); - private app: Application; - - constructor(options: Partial = {}) { - this.options = { - appName: 'app', - needWriteFile: true, - useRelativePath: true, - configDir: DEFAULT_CONFIG_DIR, - loaderListGenerator: (defaultLoaderList: string[]) => defaultLoaderList, - policy: ScanPolicy.All, - ...options, - exclude: DEFAULT_EXCLUDES.concat(options.exclude ?? []), - extensions: [...new Set(this.moduleExtensions.concat(options.extensions ?? []))], - }; - this.app = options.app ?? new ArtusApplication(); - } - - private async initItemMap(): Promise { - this.itemMap = new Map( - this.options.loaderListGenerator(DEFAULT_LOADER_LIST_WITH_ORDER).map(loaderNameOrClazz => { - if (typeof loaderNameOrClazz === 'string') { - return [loaderNameOrClazz, []]; - } - const loaderClazz = loaderNameOrClazz; - const loaderName = Reflect.getMetadata(LOADER_NAME_META, loaderClazz); - if (!loaderName) { - throw new Error(`Loader ${loaderClazz.name} must have a @DefineLoader() decorator.`); - } - return [loaderName, []]; - }), - ); - } - - private async scanEnvList(root: string): Promise { - const { configDir, envs } = this.options; - if (Array.isArray(envs) && envs.length) { - return envs; - } - const absoluteConfigDir = path.resolve(root, configDir); - const configFileList = (await exists(absoluteConfigDir)) ? await fs.readdir(absoluteConfigDir) : []; - const envSet: Set = new Set([ARTUS_DEFAULT_CONFIG_ENV.DEFAULT]); - for (const configFilename of configFileList) { - if (configFilename.endsWith('.d.ts')) { - continue; - } - const env = ConfigurationHandler.getEnvFromFilename(configFilename); - envSet.add(env); - } - return [...envSet]; - } - - async scan(root: string): Promise> { - if (!path.isAbsolute(root)) { - // make sure the root path is absolute - root = path.resolve(root); - } - - const result = {}; - const envList = await this.scanEnvList(root); - - for (const env of envList) { - result[env] = await this.scanManifestByEnv(root, env); - } - - if (this.options.needWriteFile) { - await this.writeFile(path.resolve(root, 'manifest.json'), JSON.stringify(result, null, 2)); - } - - return result; - } - - private async scanManifestByEnv(root: string, env: string): Promise { - // 0. init clean itemMap - await this.initItemMap(); - - // 1. Pre-Scan all config files - const config = await this.getAllConfig(root, env); - - // 2. scan all file in framework - const frameworkConfig = config.framework ?? this.options.framework; - const frameworkDirs = await this.getFrameworkDirs(frameworkConfig, root, env); - for (const frameworkDir of frameworkDirs) { - await this.walk(frameworkDir, this.formatWalkOptions('framework', frameworkDir)); - } - - - // 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)); - } - const { plugin } = this.configHandle.getMergedConfig(env); - const pluginConfig = deepmerge.all([plugin || {}, this.options.plugin || {}]) as Record; - const pluginSortedList = await PluginFactory.createFromConfig(pluginConfig, { - logger: this.app.logger, - }); - for (const plugin of pluginSortedList) { - if (!plugin.enable) continue; - this.setPluginMeta(plugin); - await this.walk( - plugin.importPath, - this.formatWalkOptions('plugin', plugin.importPath, plugin.name, plugin.metadata), - ); - } - - // 4. scan all file in app - await this.walk(root, this.formatWalkOptions('app', root, '')); - - const relative = this.options.useRelativePath; - if (relative) { - for (const [pluginName, pluginConfigItem] of Object.entries(pluginConfig)) { - if (pluginConfigItem.path) { - pluginConfig[pluginName].path = path.relative(root, pluginConfigItem.path); - } - } - } - const result: Manifest = { - pluginConfig, - items: this.getItemsFromMap(relative, root, env), - relative, - }; - return result; - } - - private async walk(root: string, options: WalkOptions) { - await ScanUtils.walk(root, options); - } - - private setPluginMeta(plugin: PluginType) { - if (!this.itemMap.has('plugin-meta')) { - this.itemMap.set('plugin-meta', []); - } - const metaList = this.itemMap.get('plugin-meta'); - metaList.push({ - path: plugin.metaFilePath, - extname: path.extname(plugin.metaFilePath), - filename: path.basename(plugin.metaFilePath), - loader: 'plugin-meta', - source: 'plugin', - unitName: plugin.name, - }); - } - - private async getAllConfig(baseDir: string, env: string) { - const configDir = this.getConfigDir(baseDir, this.options.configDir); - if (!configDir) { - return {}; - } - const root = path.resolve(baseDir, configDir); - const configFileList = (await exists(root)) ? await fs.readdir(root) : []; - const container = new Container(ArtusInjectEnum.DefaultContainerName); - container.set({ type: ConfigurationHandler }); - container.set({ - id: ArtusInjectEnum.Application, - value: this.app, - }); - const loaderFactory = LoaderFactory.create(container); - const configItemList: (ManifestItem | null)[] = await Promise.all(configFileList.map(async filename => { - const extname = path.extname(filename); - if (ScanUtils.isExclude(filename, extname, this.options.exclude, this.options.extensions)) { - return null; - } - let { loader } = await loaderFactory.findLoaderName({ - filename, - baseDir, - root, - configDir, - policy: this.options.policy, - }); - if (loader === 'framework-config') { - // SEEME: framework-config is a special loader, cannot be used when scan, need refactor later - loader = 'config'; - } - return { - path: path.resolve(root, filename), - extname, - filename, - loader, - source: 'config', - loaderState: { - baseDir, - }, - }; - })); - await loaderFactory.loadItemList(configItemList.filter(v => v) as ManifestItem[]); - const configurationHandler = container.get(ConfigurationHandler); - const config = configurationHandler.getMergedConfig(env); - let configList = [config]; - if (this.tmpConfigStore.has(env)) { - // equal unshift config to configList - configList = configList.concat(this.tmpConfigStore.get(env) ?? []); - } - this.tmpConfigStore.set(env, configList); - return config; - } - - private getConfigDir(root: string, dir: string): string { - if (ScanUtils.exist(root, [dir])) { - return dir; - } - - if (ScanUtils.exist(root, [DEFAULT_CONFIG_DIR])) { - return DEFAULT_CONFIG_DIR; - } - - return ''; - } - - private async getFrameworkDirs( - config: FrameworkConfig, - root: string, - env: string, - dirs: string[] = [], - ): Promise { - if (!config || (!config.path && !config.package)) { - return dirs; - } - - const frameworkBaseDir = await FrameworkHandler.handle(root, config); - dirs.unshift(frameworkBaseDir); - - // scan recurse - const configInFramework = await this.getAllConfig(frameworkBaseDir, env); - const frameworkDirs = await this.getFrameworkDirs(configInFramework.framework, frameworkBaseDir, env, dirs); - return frameworkDirs; - } - - private formatWalkOptions(source: string, baseDir: string, unitName?: string, metaInfo?: Partial): WalkOptions { - const opts: WalkOptions = { - itemMap: this.itemMap, - source, - baseDir, - unitName: unitName ?? baseDir, - extensions: this.options.extensions, - exclude: this.options.exclude, - configDir: this.options.configDir, - policy: this.options.policy, - }; - - if (source === 'plugin') { - // TODO: Only support plugin meta now, need cover framework meta later - opts.exclude = DEFAULT_EXCLUDES.concat(metaInfo.exclude ?? []); - opts.configDir = metaInfo.configDir ?? this.options.configDir; - } - - return opts; - } - - 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 => { - // 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) { - await fs.writeFile(filename, data); - } -} diff --git a/src/scanner/task.ts b/src/scanner/task.ts new file mode 100644 index 00000000..0633c7cb --- /dev/null +++ b/src/scanner/task.ts @@ -0,0 +1,263 @@ +import path from 'path'; +import * as fs from 'fs/promises'; +import { ScannerOptions, ScanTaskItem, WalkOptions } from './types'; +import { existsAsync, getPackageVersion, isExclude, isPluginAsync, loadConfigItemList, resolvePluginConfigItemRef } from './utils'; +import { findLoader, Manifest, ManifestItem, PluginConfigEnvMap, RefMap, RefMapItem } from '../loader'; +import { PluginConfig, PluginMetadata } from '../plugin'; +import { mergeConfig } from '../loader/utils/merge'; +import { loadMetaFile } from '../utils/load_meta_file'; +import { ARTUS_DEFAULT_CONFIG_ENV, DEFAULT_APP_REF, PLUGIN_META_FILENAME } from '../constant'; +import { Application } from '../types'; +import { ArtusApplication } from '../application'; + +export class ScanTaskRunner { + private waitingTaskMap: Map = new Map(); // Key is pluginName, waiting to detect enabled + private enabledPluginSet: Set = new Set(); // Key is pluginName + private pluginConfigMap: PluginConfigEnvMap = {}; + private refMap: RefMap = {}; + private taskQueue: ScanTaskItem[] = []; + private app: Application; + + constructor( + private root: string, + private options: ScannerOptions, + ) { + this.app = options.app ?? new ArtusApplication(); + } + + /* + * Handler for walk directories and files recursively + */ + private async walk(curPath: string, options: WalkOptions) { + const { baseDir, configDir } = options; + if (!(await existsAsync(curPath))) { + this.app.logger.warn(`[scan->walk] ${curPath} is not exists.`); + return []; + } + + const stat = await fs.stat(curPath); + if (!stat.isDirectory()) { + return []; + } + + const items = await fs.readdir(curPath); + const itemWalkResult = await Promise.all(items.map(async (item): Promise => { + const realPath = path.resolve(curPath, item); + const extname = path.extname(realPath); + const relativePath = path.relative(baseDir, realPath); + if (isExclude(relativePath, options.exclude, options.extensions)) { + return []; + } + const itemStat = await fs.stat(realPath); + if (itemStat.isDirectory()) { + // ignore plugin dir + if (await isPluginAsync(realPath)) { + return []; + } + return this.walk(realPath, options); + } else if (itemStat.isFile()) { + if (!extname) { + // Exclude file without extname + return []; + } + const filename = path.basename(realPath); + const filenameWithoutExt = path.basename(realPath, extname); + const loaderFindResult = await findLoader({ + filename, + root: curPath, + baseDir, + configDir, + policy: options.policy, + }); + if (!loaderFindResult) { + return []; + } + const { loaderName, loaderState } = loaderFindResult; + const item: ManifestItem = { + path: path.resolve(curPath, filenameWithoutExt), + extname, + filename, + loader: loaderName, + source: options.source, + unitName: options.unitName, + }; + if (loaderState) { + item.loaderState = loaderState; + } + return [item]; + } else { + return []; + } + })); + return itemWalkResult.reduce((itemList, result) => itemList.concat(result), []); + } + + /* + * Handler for pluginConfig object + * Will push new task for plugin, and merge config by env + */ + public async handlePluginConfig( + pluginConfig: PluginConfig, + basePath: string, + env: string = ARTUS_DEFAULT_CONFIG_ENV.DEFAULT, + ): Promise { + const tPluginConfig: PluginConfig = {}; + for (const [pluginName, pluginConfigItem] of Object.entries(pluginConfig)) { + // Set temp pluginConfig in manifest + tPluginConfig[pluginName] = {}; + if (pluginConfigItem.enable !== undefined) { + tPluginConfig[pluginName].enable = pluginConfigItem.enable; + } + if (pluginConfigItem.enable) { + this.enabledPluginSet.add(pluginName); + } + + // Resolve ref and set + const isPluginEnabled = this.enabledPluginSet.has(pluginName); + const ref = await resolvePluginConfigItemRef(pluginConfigItem, basePath, this.root); + if (!ref?.name) { + continue; + } + tPluginConfig[pluginName].refName = ref.name; + // Generate and push scan task + const curRefTask: ScanTaskItem = { + curPath: ref.path, + refName: ref.name, + isPackage: ref.isPackage, + }; + const waitingTaskList = this.waitingTaskMap.get(pluginName) ?? []; + // Use unshift to make the items later in the list have higher priority + if (isPluginEnabled) { + // Ref need scan immediately and add all waiting task to queue + this.taskQueue.unshift(curRefTask); + for (const waitingTask of waitingTaskList) { + this.taskQueue.unshift(waitingTask); + } + this.waitingTaskMap.delete(pluginName); + } else { + // Need waiting to detect enabled, push refTask to waitingList + waitingTaskList.unshift(curRefTask); + this.waitingTaskMap.set(pluginName, waitingTaskList); + } + } + // Reverse Merge, The prior of top-level(exists) is higher + const existsPluginConfig = this.pluginConfigMap[env] ?? {}; + this.pluginConfigMap[env] = mergeConfig(tPluginConfig, existsPluginConfig) as PluginConfig; + } + + + /** + * Handler of single scan task(only a ref) + */ + public async run(taskItem: ScanTaskItem): Promise { + const { curPath = '', refName, isPackage } = taskItem; + let basePath = curPath; + if (!path.isAbsolute(basePath)) { + // basePath must be absolute path + basePath = path.resolve(this.root, curPath); + } + + // pre-scan check for multi-version package + const relativedPath = path.relative(this.root, basePath); + const packageVersion = await getPackageVersion( + (refName === DEFAULT_APP_REF || !isPackage) + ? basePath + : refName, + ); + + if (this.refMap[refName]) { + // Already scanned + if (refName === DEFAULT_APP_REF) { + // No need to check app level + return; + } + const refItem = this.refMap[refName]; + if (refItem.packageVersion && packageVersion && packageVersion !== refItem.packageVersion) { + // Do NOT allow multi-version of plugin package by different version number + throw new Error(`${refName} has multi version of ${packageVersion}, ${refItem.packageVersion}`); + } + if (refItem.relativedPath && relativedPath && relativedPath !== refItem.relativedPath) { + // Do NOT allow multi-version of plugin package by different path + throw new Error(`${refName} has multi path with same version in ${relativedPath} and ${refItem.relativedPath}`); + } + return; + } + + const walkOpts: WalkOptions = { + baseDir: basePath, + configDir: this.options.configDir, + exclude: this.options.exclude, + extensions: this.options.extensions, + policy: this.options.policy, + source: refName === DEFAULT_APP_REF ? 'app' : 'plugin', + unitName: refName, + }; + const refItem: RefMapItem = { + relativedPath, + packageVersion, + items: [], + }; + + if (await isPluginAsync(basePath)) { + const metaFilePath = path.resolve(basePath, PLUGIN_META_FILENAME); + const pluginMeta: PluginMetadata = await loadMetaFile(metaFilePath); + walkOpts.configDir = pluginMeta.configDir || walkOpts.configDir; + walkOpts.exclude = walkOpts.exclude.concat(pluginMeta.exclude ?? []); + walkOpts.unitName = pluginMeta.name; + refItem.pluginMetadata = pluginMeta; + } + + refItem.items = await this.walk(basePath, walkOpts); + const configItemList = refItem.items.filter(item => item.loader === 'config'); + const pluginConfigEnvMap = await loadConfigItemList<{ + plugin: PluginConfig; + }>(configItemList, this.app); + for (const [env, configObj] of Object.entries(pluginConfigEnvMap)) { + const pluginConfig = configObj?.plugin; + if (!pluginConfig) { + continue; + } + await this.handlePluginConfig(pluginConfig, basePath, env); + } + + if (this.options.useRelativePath) { + refItem.items = refItem.items.map(item => ({ + ...item, + path: path.relative(this.root, item.path), + })); + } + + this.refMap[refName] = refItem; + } + + public async runAll(): Promise { + // Add Task of options.plugin + if (this.options.plugin) { + await this.handlePluginConfig(this.options.plugin, this.root); + } + + // Add Root Task(make it as top/start) + this.taskQueue.unshift({ + curPath: '.', + refName: DEFAULT_APP_REF, + isPackage: false, + }); + + // Run task queue + while (this.taskQueue.length > 0) { + const taskItem = this.taskQueue.shift(); + await this.run(taskItem); + } + // Clean up + this.app.configurationHandler.clearStore(); + } + + public dump(): Manifest { + return { + version: '2', + pluginConfig: this.pluginConfigMap, + refMap: this.refMap, + }; + } +} + diff --git a/src/scanner/types.ts b/src/scanner/types.ts index 6cc17926..3b4e3d2e 100644 --- a/src/scanner/types.ts +++ b/src/scanner/types.ts @@ -1,36 +1,48 @@ -import { BaseLoader, ManifestItem } from "../loader"; -import { FrameworkConfig } from "../framework"; -import { PluginConfigItem } from "../plugin/types"; -import { Application } from "../types"; -import { ScanPolicy } from "../constant"; +import { Manifest } from '../loader'; +import { PluginConfig } from '../plugin/types'; +import { Application } from '../types'; +import { ScanPolicy } from '../constant'; + export interface ScannerOptions { - appName: string; extensions: string[]; needWriteFile: boolean; + manifestFilePath?: string; useRelativePath: boolean; exclude: string[]; configDir: string; policy: ScanPolicy; envs?: string[]; - framework?: FrameworkConfig; - plugin?: Record>; - loaderListGenerator: (defaultLoaderList: string[]) => (string | typeof BaseLoader)[]; + plugin?: PluginConfig; app?: Application; } export interface WalkOptions { - source: string; baseDir: string; configDir: string; policy: ScanPolicy; extensions: string[]; exclude: string[]; - itemMap: Map; + source?: string; unitName?: string; } export interface LoaderOptions { - root: string, - baseDir: string, + root: string; + baseDir: string; } + +export interface ScannerConstructor { + new(opts?: Partial): ScannerType; +} + +export interface ScannerType { + scan(root: string): Promise; +} + +export interface ScanTaskItem { + curPath: string; + refName: string; + isPackage: boolean; +} + diff --git a/src/scanner/utils.ts b/src/scanner/utils.ts index a2b3d871..62d82e1e 100644 --- a/src/scanner/utils.ts +++ b/src/scanner/utils.ts @@ -1,103 +1,99 @@ import 'reflect-metadata'; import * as path from 'path'; import * as fs from 'fs/promises'; -import { existsSync } from 'fs'; -import { Container } from '@artus/injection'; -import { - ArtusInjectEnum, - DEFAULT_LOADER, - PLUGIN_META_FILENAME, -} from '../constant'; -import { LoaderFactory, ManifestItem } from '../loader'; -import { WalkOptions } from './types'; import { isMatch } from '../utils'; +import compatibleRequire from '../utils/compatible_require'; +import { PLUGIN_META_FILENAME } from '../constant'; +import { PluginConfigItem } from '../plugin'; +import { getInlinePackageEntryPath, getPackagePath } from '../plugin/common'; +import { Application } from '../types'; +import { ManifestItem } from '../loader'; -export class ScanUtils { - static loaderFactory: LoaderFactory = LoaderFactory.create(new Container(ArtusInjectEnum.DefaultContainerName)); +export const getPackageVersion = async (basePath: string): Promise => { + try { + const packageJsonPath = path.resolve(basePath, 'package.json'); + const packageJson = await compatibleRequire(packageJsonPath); + return packageJson?.version; + } catch (error) { + return undefined; + } +}; - static async walk(root: string, options: WalkOptions) { - const { source, unitName, baseDir, configDir } = options; - if (!existsSync(root)) { - // TODO: use artus logger instead - console.warn(`[scan->walk] ${root} is not exists.`); - return; - } +export const existsAsync = async (filePath: string): Promise => { + try { + await fs.access(filePath); + return true; + } catch (error) { + return false; + } +}; - const stat = await fs.stat(root); - if (!stat.isDirectory()) { - return; - } +export const isExclude = (targetPath: string, exclude: string[], extensions: string[]): boolean => { + let result = false; + if (!result && exclude) { + result = isMatch(targetPath, exclude, true); + } - const items = await fs.readdir(root); - for (const item of items) { - const realPath = path.resolve(root, item); - const extname = path.extname(realPath); - if (this.isExclude(item, extname, options.exclude, options.extensions)) { - continue; - } - const itemStat = await fs.stat(realPath); - if (itemStat.isDirectory()) { - // ignore plugin dir - if (this.exist(realPath, [PLUGIN_META_FILENAME])) { - continue; - } - await ScanUtils.walk(realPath, options); - continue; - } + const extname = path.extname(targetPath); + if (!result && extname) { + result = !extensions.includes(extname); + } + return result; +}; - if (itemStat.isFile()) { - if (!extname) { - // Exclude file without extname - continue; - } - const filename = path.basename(realPath); - const filenameWithoutExt = path.basename(realPath, extname); - const loaderFindResult = await ScanUtils.loaderFactory.findLoader({ - filename, - root, - baseDir, - configDir, - policy: options.policy, - }); - if (!loaderFindResult) { - continue; - } - const { loaderName, loaderState } = loaderFindResult; - const item: ManifestItem = { - path: options.extensions.includes(extname) ? path.resolve(root, filenameWithoutExt) : realPath, - extname, - filename, - loader: loaderName, - source, - }; - if (loaderState) { - item.loaderState = loaderState; - } - unitName && (item.unitName = unitName); - const itemList = options.itemMap.get(item.loader ?? DEFAULT_LOADER); - if (Array.isArray(itemList)) { - itemList.push(item); - } - } - } +export const isPluginAsync = (basePath: string): Promise => { + return existsAsync(path.resolve(basePath, PLUGIN_META_FILENAME)); +}; + +export const loadConfigItemList = async >(configItemList: ManifestItem[], app: Application): Promise> => { + if (!configItemList.length) { + return {}; } + const enabledLifecycleManager = app.lifecycleManager.enable; + app.lifecycleManager.enable = false; + await app.loaderFactory.loadItemList(configItemList); + app.lifecycleManager.enable = enabledLifecycleManager; + return Object.fromEntries(app.configurationHandler.configStore.entries()) as Record; +}; - static isExclude(filename: string, extname: string, - exclude: string[], extensions: string[]): boolean { - let result = false; - if (!result && exclude) { - result = isMatch(filename, exclude); - } +export const resolvePluginConfigItemRef = async ( + pluginConfigItem: PluginConfigItem, + baseDir: string, + root: string, +): Promise<{ + name: string; + path: string; + isPackage: boolean; +} | null> => { + if (pluginConfigItem.refName) { + // For Unit-test + return { + name: pluginConfigItem.refName, + path: pluginConfigItem.path ?? pluginConfigItem.refName, + isPackage: !!pluginConfigItem.package, + }; + } + if (pluginConfigItem.package) { + const refPath = getPackagePath(pluginConfigItem.package, [baseDir]); + return { + name: pluginConfigItem.package, + path: refPath, + isPackage: true, + }; + } else if (pluginConfigItem.path) { + const refName = path.isAbsolute(pluginConfigItem.path) ? path.relative(root, pluginConfigItem.path) : pluginConfigItem.path; + let refPath = refName; - if (!result && extname) { - result = !extensions.includes(extname); + const packageJsonPath = path.resolve(pluginConfigItem.path, 'package.json'); + if (await existsAsync(packageJsonPath)) { + refPath = await getInlinePackageEntryPath(pluginConfigItem.path); } - return result; + return { + name: refName, + path: refPath, + isPackage: false, + }; } + return null; +}; - static exist(dir: string, filenames: string[]): boolean { - return filenames.some(filename => { - return existsSync(path.resolve(dir, `${filename}`)); - }); - } -} \ No newline at end of file diff --git a/src/trigger/index.ts b/src/trigger/index.ts deleted file mode 100644 index e8b78e63..00000000 --- a/src/trigger/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { ExecutionContainer, Inject, Injectable, ScopeEnum } from '@artus/injection'; -import { Input, Context, MiddlewareInput, Pipeline, Output } from '@artus/pipeline'; -import { ArtusInjectEnum } from '../constant'; -import { exceptionFilterMiddleware } from '../exception'; -import { Application, TriggerType } from '../types'; - -@Injectable({ scope: ScopeEnum.SINGLETON }) -export default class Trigger implements TriggerType { - private pipeline: Pipeline; - - @Inject(ArtusInjectEnum.Application) - private app: Application; - - constructor() { - this.pipeline = new Pipeline(); - this.pipeline.use(exceptionFilterMiddleware); - } - - async use(middleware: MiddlewareInput): Promise { - // TODO: async hook before pipeline.use(middleware) - this.pipeline.use(middleware); - } - - async initContext(input: Input = new Input(), output = new Output()): Promise { - const ctx = new Context(input, output); - ctx.container = new ExecutionContainer(ctx, this.app.container); - ctx.container.set({ - id: ExecutionContainer, - value: ctx.container, - }); - return ctx; - } - - async startPipeline(ctx: Context): Promise { - await this.pipeline.run(ctx); - } -} diff --git a/src/types.ts b/src/types.ts index 3d8c5808..aeb38665 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,7 +1,7 @@ import { Container } from '@artus/injection'; -import { BaseContext } from '@artus/pipeline'; -import { HookFunction } from './lifecycle'; -import { Manifest } from './loader'; +import ConfigurationHandler, { ConfigObject } from './configuration'; +import { HookFunction, LifecycleManager } from './lifecycle'; +import { LoaderFactory, Manifest } from './loader'; import { LoggerType } from './logger'; export interface ApplicationLifecycle { @@ -15,16 +15,19 @@ export interface ApplicationLifecycle { export interface ApplicationInitOptions { containerName?: string; - env: string; + env: string | string[]; } export interface Application { container: Container; manifest?: Manifest; - config?: Record; // getter + config: ConfigObject; + configurationHandler: ConfigurationHandler; + lifecycleManager: LifecycleManager; + loaderFactory: LoaderFactory; logger: LoggerType load(manifest: Manifest): Promise; @@ -32,8 +35,3 @@ export interface Application { registerHook(hookName: string, hookFn: HookFunction): void; } -export interface TriggerType { - use(...args): void | Promise; - initContext(...args): BaseContext | Promise; - startPipeline(...args): Promise; -} diff --git a/src/utils/index.ts b/src/utils/index.ts index aca9416e..997f5c8c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,9 +4,9 @@ export function getDefaultExtensions() { return Object.keys(require.extensions); } -export function isMatch(filename: string, patterns: string | string[]) { +export function isMatch(filename: string, patterns: string | string[], matchBase = false) { if (!Array.isArray(patterns)) { patterns = [patterns]; } - return patterns.some(pattern => minimatch(filename, pattern)); + return patterns.some(pattern => minimatch(filename, pattern, { matchBase })); } diff --git a/test/__snapshots__/scanner.test.ts.snap b/test/__snapshots__/scanner.test.ts.snap new file mode 100644 index 00000000..bc9ab153 --- /dev/null +++ b/test/__snapshots__/scanner.test.ts.snap @@ -0,0 +1,547 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`test/scanner.test.ts should be scan application 1`] = ` +Object { + "pluginConfig": Object { + "default": Object { + "mysql": Object { + "enable": false, + "refName": "src/mysql_plugin", + }, + "redis": Object { + "enable": true, + "refName": "src/redis_plugin", + }, + "testDuplicate": Object { + "enable": false, + "refName": "@artus/injection", + }, + }, + "dev": Object { + "testDuplicate": Object { + "enable": true, + "refName": "src/test_duplicate_plugin", + }, + }, + }, + "refMap": Object { + "@artus/injection": Object { + "items": Array [], + "packageVersion": undefined, + "relativedPath": "../../../node_modules/@artus/injection/lib", + }, + "_app": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "config.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "src/config/config.default", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "config.dev.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "src/config/config.dev", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "plugin.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "src/config/plugin.default", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "plugin.dev.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "src/config/plugin.dev", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "hello.ts", + "loader": "module", + "loaderState": Object { + "exportNames": Array [ + "default", + ], + }, + "path": "src/controllers/hello", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".json", + "filename": "exception.json", + "loader": "exception", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "src/exception", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "default.ts", + "loader": "exception-filter", + "loaderState": Object { + "exportNames": Array [ + "MockExceptionFilter", + ], + }, + "path": "src/filter/default", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "koa_app.ts", + "loader": "module", + "loaderState": Object { + "exportNames": Array [ + "default", + ], + }, + "path": "src/koa_app", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "lifecycle.ts", + "loader": "lifecycle-hook-unit", + "loaderState": Object { + "exportNames": Array [ + "default", + ], + }, + "path": "src/lifecycle", + "source": "app", + "unitName": "_app", + }, + Object { + "extname": ".ts", + "filename": "hello.ts", + "loader": "module", + "loaderState": Object { + "exportNames": Array [ + "default", + ], + }, + "path": "src/services/hello", + "source": "app", + "unitName": "_app", + }, + ], + "packageVersion": undefined, + "relativedPath": "", + }, + "src/redis_plugin": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "app.ts", + "loader": "lifecycle-hook-unit", + "loaderState": Object { + "exportNames": Array [ + "default", + ], + }, + "path": "src/redis_plugin/app", + "source": "plugin", + "unitName": "redis", + }, + ], + "packageVersion": undefined, + "pluginMetadata": Object { + "exclude": Array [ + "not_to_be_scanned_dir", + "not_to_be_scanned_file.ts", + ], + "name": "redis", + }, + "relativedPath": "src/redis_plugin", + }, + "src/test_duplicate_plugin": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "testDuplicate", + }, + "relativedPath": "src/test_duplicate_plugin", + }, + }, + "version": "2", +} +`; + +exports[`test/scanner.test.ts should scan application with nesting preset a which defined in options 1`] = ` +Object { + "pluginConfig": Object { + "default": Object { + "a": Object { + "enable": false, + "refName": "../plugins/plugin_a", + }, + "b": Object { + "enable": false, + "refName": "../plugins/plugin_b", + }, + "c": Object { + "enable": false, + "refName": "../plugins/plugin_c", + }, + "d": Object { + "enable": true, + "refName": "../plugins/plugin_d", + }, + "plugin-with-entry-a": Object { + "enable": true, + "refName": "../plugins/plugin_with_entry_a", + }, + "plugin-with-entry-b": Object { + "enable": true, + "refName": "../plugins/plugin_with_entry_b", + }, + "plugin-with-entry-c": Object { + "enable": false, + "refName": "../plugins/plugin_with_entry_c", + }, + "preset_a": Object { + "enable": true, + "refName": "../plugins/preset_a", + }, + "preset_b": Object { + "enable": true, + "refName": "../plugins/preset_b", + }, + "preset_c": Object { + "enable": true, + "refName": "../plugins/preset_c", + }, + }, + }, + "refMap": Object { + "../plugins/plugin_a": Object { + "items": Array [], + "packageVersion": "0.0.1", + "pluginMetadata": Object { + "dependencies": Array [ + Object { + "name": "plugin-b", + }, + Object { + "name": "plugin-c", + "optional": true, + }, + ], + "name": "plugin-a", + }, + "relativedPath": "../plugins/plugin_a", + }, + "../plugins/plugin_b": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "dependencies": Array [ + Object { + "name": "plugin-c", + }, + ], + "name": "plugin-b", + }, + "relativedPath": "../plugins/plugin_b", + }, + "../plugins/plugin_d": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "dependencies": Array [ + Object { + "name": "plugin-e", + "optional": true, + }, + ], + "name": "plugin-d", + }, + "relativedPath": "../plugins/plugin_d", + }, + "../plugins/plugin_with_entry_a": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "plugin-with-entry-a", + }, + "relativedPath": "../plugins/plugin_with_entry_a/mock_lib", + }, + "../plugins/plugin_with_entry_b": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "plugin-with-entry-b", + }, + "relativedPath": "../plugins/plugin_with_entry_b/mock_lib", + }, + "../plugins/preset_a": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "plugin.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "../plugins/preset_a/config/plugin.default", + "source": "plugin", + "unitName": "preset_a", + }, + ], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "preset_a", + }, + "relativedPath": "../plugins/preset_a", + }, + "../plugins/preset_b": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "plugin.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "../plugins/preset_b/config/plugin.default", + "source": "plugin", + "unitName": "preset_b", + }, + ], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "preset_b", + }, + "relativedPath": "../plugins/preset_b", + }, + "../plugins/preset_c": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "plugin.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "../plugins/preset_c/config/plugin.default", + "source": "plugin", + "unitName": "preset_c", + }, + ], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "preset_c", + }, + "relativedPath": "../plugins/preset_c", + }, + "_app": Object { + "items": Array [], + "packageVersion": undefined, + "relativedPath": "", + }, + }, + "version": "2", +} +`; + +exports[`test/scanner.test.ts should scan application with single preset b which defined in config 1`] = ` +Object { + "pluginConfig": Object { + "default": Object { + "a": Object { + "enable": true, + "refName": "../plugins/plugin_a", + }, + "b": Object { + "enable": true, + "refName": "../plugins/plugin_b", + }, + "plugin-with-entry-b": Object { + "enable": true, + "refName": "../plugins/plugin_with_entry_b", + }, + "preset_b": Object { + "enable": true, + "refName": "../plugins/preset_b", + }, + }, + }, + "refMap": Object { + "../plugins/plugin_a": Object { + "items": Array [], + "packageVersion": "0.0.1", + "pluginMetadata": Object { + "dependencies": Array [ + Object { + "name": "plugin-b", + }, + Object { + "name": "plugin-c", + "optional": true, + }, + ], + "name": "plugin-a", + }, + "relativedPath": "../plugins/plugin_a", + }, + "../plugins/plugin_b": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "dependencies": Array [ + Object { + "name": "plugin-c", + }, + ], + "name": "plugin-b", + }, + "relativedPath": "../plugins/plugin_b", + }, + "../plugins/plugin_with_entry_b": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "plugin-with-entry-b", + }, + "relativedPath": "../plugins/plugin_with_entry_b/mock_lib", + }, + "../plugins/preset_b": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "plugin.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "../plugins/preset_b/config/plugin.default", + "source": "plugin", + "unitName": "preset_b", + }, + ], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "preset_b", + }, + "relativedPath": "../plugins/preset_b", + }, + "_app": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "plugin.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "config/plugin.default", + "source": "app", + "unitName": "_app", + }, + ], + "packageVersion": undefined, + "relativedPath": "", + }, + }, + "version": "2", +} +`; + +exports[`test/scanner.test.ts should scan application with single preset c which defined in options 1`] = ` +Object { + "pluginConfig": Object { + "default": Object { + "b": Object { + "enable": false, + "refName": "../plugins/plugin_b", + }, + "c": Object { + "enable": false, + "refName": "../plugins/plugin_c", + }, + "d": Object { + "enable": true, + "refName": "../plugins/plugin_d", + }, + "plugin-with-entry-c": Object { + "enable": false, + "refName": "../plugins/plugin_with_entry_c", + }, + "preset_c": Object { + "enable": true, + "refName": "../plugins/preset_c", + }, + }, + }, + "refMap": Object { + "../plugins/plugin_d": Object { + "items": Array [], + "packageVersion": undefined, + "pluginMetadata": Object { + "dependencies": Array [ + Object { + "name": "plugin-e", + "optional": true, + }, + ], + "name": "plugin-d", + }, + "relativedPath": "../plugins/plugin_d", + }, + "../plugins/preset_c": Object { + "items": Array [ + Object { + "extname": ".ts", + "filename": "plugin.default.ts", + "loader": "config", + "loaderState": Object { + "exportNames": Array [], + }, + "path": "../plugins/preset_c/config/plugin.default", + "source": "plugin", + "unitName": "preset_c", + }, + ], + "packageVersion": undefined, + "pluginMetadata": Object { + "name": "preset_c", + }, + "relativedPath": "../plugins/preset_c", + }, + "_app": Object { + "items": Array [], + "packageVersion": undefined, + "relativedPath": "", + }, + }, + "version": "2", +} +`; diff --git a/test/app.test.ts b/test/app.test.ts index 1b0103e8..6677d755 100644 --- a/test/app.test.ts +++ b/test/app.test.ts @@ -1,7 +1,7 @@ import 'reflect-metadata'; import axios from 'axios'; import assert from 'assert'; -import { ArtusInjectEnum, ConfigurationHandler } from '../src'; +import { ArtusApplication, ArtusInjectEnum, ConfigurationHandler, LifecycleManager, LoaderFactory } from '../src'; describe('test/app.test.ts', () => { describe('app koa with ts', () => { @@ -23,9 +23,10 @@ describe('test/app.test.ts', () => { } = await import('./fixtures/app_koa_with_ts/src/bootstrap'); // Check Artus Default Class Inject to Contianer - expect(() => app.container.get(ArtusInjectEnum.Application)).not.toThrow(); - expect(() => app.container.get(ArtusInjectEnum.LifecycleManager)).not.toThrow(); - expect(() => app.container.get(ConfigurationHandler)).not.toThrow(); + expect(app.container.get(ArtusInjectEnum.Application)).toBeInstanceOf(ArtusApplication); + expect(app.container.get(LifecycleManager)).toBeInstanceOf(LifecycleManager); + expect(app.container.get(LoaderFactory)).toBeInstanceOf(LoaderFactory); + expect(app.container.get(ConfigurationHandler)).toBeInstanceOf(ConfigurationHandler); await main(); diff --git a/test/config.test.ts b/test/config.test.ts index 7644e7a0..9fa648ec 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -1,13 +1,17 @@ -import 'reflect-metadata'; -import { ARTUS_SERVER_ENV } from '../src/constant'; +import "reflect-metadata"; +import { ARTUS_SERVER_ENV } from "../src/constant"; -describe('test/app.test.ts', () => { - describe('app with config', () => { - it('should config load on application', async () => { - process.env[ARTUS_SERVER_ENV] = 'production'; - const { main } = await import('./fixtures/app_with_config/bootstrap'); +describe("test/config.test.ts", () => { + describe("app with config", () => { + it("should config load on application", async () => { + process.env[ARTUS_SERVER_ENV] = "production"; + const { main } = await import("./fixtures/app_with_config/bootstrap"); const app = await main(); - expect(app.config).toEqual({ name: 'test-for-config', test: 1, arr: [ 4, 5, 6 ] }); + expect(app.config).toEqual({ + name: "test-for-config", + test: 1, + arr: [4, 5, 6], + }); process.env[ARTUS_SERVER_ENV] = undefined; }); }); diff --git a/test/exception_filter.test.ts b/test/exception_filter.test.ts index ae57bef7..f7a0ba66 100644 --- a/test/exception_filter.test.ts +++ b/test/exception_filter.test.ts @@ -1,18 +1,12 @@ import 'reflect-metadata'; -import { ArtusApplication, ArtusStdError, Trigger } from '../src'; -import { Input } from '@artus/pipeline'; +import { ArtusStdError, matchExceptionFilter } from '../src'; +import MockErrorService from './fixtures/exception_filter/service'; describe('test/exception_filter.test.ts', () => { it('a standard exception catch logic with no filter', async () => { try { - const app = new ArtusApplication(); - const trigger = app.container.get(Trigger); - trigger.use(() => { - throw new ArtusStdError('TEST'); - }); - const ctx = await trigger.initContext(); try { - await trigger.startPipeline(ctx); + throw new ArtusStdError('TEST'); } catch (error) { expect(error).toBeInstanceOf(ArtusStdError); } @@ -27,7 +21,6 @@ describe('test/exception_filter.test.ts', () => { } = await import('./fixtures/exception_filter/bootstrap'); const app = await main(); - const trigger = app.container.get(Trigger); const mockSet: Set = app.container.get('mock_exception_set'); for (const [inputTarget, exceptedVal] of [ ['default', 'Error'], @@ -35,14 +28,13 @@ describe('test/exception_filter.test.ts', () => { ['wrapped', 'APP:WRAPPED_ERROR'], ['APP:TEST_ERROR', 'APP:TEST_ERROR'], ]) { - const input = new Input(); - input.params = { - target: inputTarget, - }; - const ctx = await trigger.initContext(input); + const mockErrorService = app.container.get(MockErrorService); try { - await trigger.startPipeline(ctx); - } catch (error) {} + mockErrorService.throw(inputTarget); + } catch (error) { + const filter = matchExceptionFilter(error, app.container); + await filter.catch(error); + } expect(mockSet.has(exceptedVal)).toBeTruthy(); } } catch (error) { diff --git a/test/fixtures/app_empty/.gitkeep b/test/fixtures/app_empty/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/app_koa_with_ts/src/bootstrap.ts b/test/fixtures/app_koa_with_ts/src/bootstrap.ts index a67652c2..0ee5ce33 100644 --- a/test/fixtures/app_koa_with_ts/src/bootstrap.ts +++ b/test/fixtures/app_koa_with_ts/src/bootstrap.ts @@ -1,49 +1,48 @@ -import 'reflect-metadata'; -import path from 'path'; -import { ArtusApplication } from '../../../../src'; -import { server } from './app'; +import "reflect-metadata"; +import path from "path"; +import { ArtusApplication } from "../../../../src"; +import { server } from "./lifecycle"; export const app: ArtusApplication = new ArtusApplication(); async function main() { await app.load({ - items: [ - { - path: path.resolve(__dirname, './app'), - extname: '.ts', - filename: 'app.ts', - loader: 'lifecycle-hook-unit', - source: 'app', + version: '2', + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(__dirname, "./lifecycle"), + extname: ".ts", + filename: "lifecycle.ts", + loader: "lifecycle-hook-unit", + source: "app", + }, + { + path: path.resolve(__dirname, "./koa_app"), + extname: ".ts", + filename: "koaApp.ts", + loader: "module", + source: "app", + }, + { + path: path.resolve(__dirname, "./controllers/hello"), + extname: ".ts", + filename: "hello.ts", + loader: "module", + source: "app", + }, + { + path: path.resolve(__dirname, "./services/hello"), + extname: ".ts", + filename: "hello.ts", + loader: "module", + source: "app", + }, + ], }, - { - path: path.resolve(__dirname, './koa_app'), - extname: '.ts', - filename: 'koaApp.ts', - loader: 'module', - source: 'app', - }, - { - path: path.resolve(__dirname, './http_trigger'), - extname: '.ts', - filename: 'httpTrigger.ts', - loader: 'module', - source: 'app', - }, - { - path: path.resolve(__dirname, './controllers/hello'), - extname: '.ts', - filename: 'hello.ts', - loader: 'module', - source: 'app', - }, - { - path: path.resolve(__dirname, './services/hello'), - extname: '.ts', - filename: 'hello.ts', - loader: 'module', - source: 'app', - }, - ], + }, }); await app.run(); @@ -52,7 +51,4 @@ async function main() { const isListening = () => server.listening; -export { - main, - isListening, -}; +export { main, isListening }; diff --git a/test/fixtures/app_koa_with_ts/src/http_trigger.ts b/test/fixtures/app_koa_with_ts/src/http_trigger.ts deleted file mode 100644 index be744061..00000000 --- a/test/fixtures/app_koa_with_ts/src/http_trigger.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { Context, Next } from '@artus/pipeline'; -import { Injectable, ScopeEnum, Trigger } from '../../../../src'; - -@Injectable({ scope: ScopeEnum.SINGLETON }) -export default class HttpTrigger extends Trigger { - constructor() { - super(); - // first of all - this.use(async (ctx: Context, next: Next) => { - await next(); - await this.respond(ctx); - }); - } - - async respond(ctx: Context) { - const { koaCtx } = ctx.input.params; - const { data } = ctx.output; - - koaCtx.status = data.status || 200; - koaCtx.body = data.content; - for (const [k, v] of Object.entries(data.headers)) { - koaCtx.set(k, v); - } - } -} diff --git a/test/fixtures/app_koa_with_ts/src/app.ts b/test/fixtures/app_koa_with_ts/src/lifecycle.ts similarity index 59% rename from test/fixtures/app_koa_with_ts/src/app.ts rename to test/fixtures/app_koa_with_ts/src/lifecycle.ts index f8b87895..92ef6cef 100644 --- a/test/fixtures/app_koa_with_ts/src/app.ts +++ b/test/fixtures/app_koa_with_ts/src/lifecycle.ts @@ -1,7 +1,6 @@ import { DefaultContext } from 'koa'; import { Server } from 'http'; -import { Container, Inject } from '@artus/injection'; -import { Context, Input } from '@artus/pipeline'; +import { Container, ExecutionContainer, Inject } from '@artus/injection'; import { ArtusApplication, ArtusInjectEnum } from '../../../../src'; import { LifecycleHookUnit, LifecycleHook } from '../../../../src/decorator'; @@ -9,7 +8,6 @@ import { ApplicationLifecycle } from '../../../../src/types'; import KoaApplication from './koa_app'; import HelloController from './controllers/hello'; -import HttpTrigger from './http_trigger'; export let server: Server; @@ -19,30 +17,23 @@ export default class MyLifecycle implements ApplicationLifecycle { app: ArtusApplication; @Inject() container: Container; - @Inject() - trigger: HttpTrigger; get koaApp(): KoaApplication { return this.container.get(KoaApplication); } - @LifecycleHook() - async didLoad() { - this.trigger.use(async (ctx: Context) => { - const { koaCtx } = ctx.input.params; - ctx.container.set({ id: 'headers', value: koaCtx.headers }); - ctx.output.data = await ctx.container.get(HelloController).index(); - }); - } - @LifecycleHook('willReady') setKoaMiddleware() { this.koaApp.use(async (koaCtx: DefaultContext) => { - const input = new Input(); - const { req, res } = koaCtx; - input.params = { koaCtx, req, res }; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); + const executionContainer = new ExecutionContainer(null, this.container); + executionContainer.set({ id: 'headers', value: koaCtx.headers }); + + const data = await executionContainer.get(HelloController).index(); + koaCtx.status = data.status || 200; + koaCtx.body = data.content; + for (const [k, v] of Object.entries(data.headers)) { + koaCtx.set(k, v); + } }); } diff --git a/test/fixtures/app_with_config/bootstrap.ts b/test/fixtures/app_with_config/bootstrap.ts index 22195008..fa5932c1 100644 --- a/test/fixtures/app_with_config/bootstrap.ts +++ b/test/fixtures/app_with_config/bootstrap.ts @@ -1,39 +1,42 @@ -import path from 'path'; -import { ArtusApplication } from '../../../src'; +import path from "path"; +import { ArtusApplication } from "../../../src"; -const rootDir = path.resolve(__dirname, './'); +const rootDir = path.resolve(__dirname, "./"); async function main() { const app = new ArtusApplication(); await app.load({ - items: [ - { - path: path.resolve(__dirname, './app'), - extname: '.ts', - filename: 'app.ts', - loader: 'lifecycle-hook-unit', - source: 'app', + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(__dirname, "./app"), + extname: ".ts", + filename: "app.ts", + loader: "lifecycle-hook-unit", + source: "app", + }, + { + path: path.resolve(rootDir, "./config/config.default"), + extname: ".ts", + filename: "config.default.ts", + loader: "config", + source: "app", + }, + { + path: path.resolve(rootDir, "./config/config.production"), + extname: ".ts", + filename: "config.production.ts", + loader: "config", + source: "app", + }, + ], }, - { - path: path.resolve(rootDir, './config/config.default'), - extname: '.ts', - filename: 'config.default.ts', - loader: 'config', - source: 'app', - }, - { - path: path.resolve(rootDir, './config/config.production'), - extname: '.ts', - filename: 'config.production.ts', - loader: 'config', - source: 'app', - }, - ], + }, }); return app; } - -export { - main, -}; +export { main }; diff --git a/test/fixtures/app_with_plugin_version_check/config/plugin.default.ts b/test/fixtures/app_with_plugin_version_check/config/plugin.default.ts new file mode 100644 index 00000000..68edf42c --- /dev/null +++ b/test/fixtures/app_with_plugin_version_check/config/plugin.default.ts @@ -0,0 +1,10 @@ +import path from 'path'; + +export default { + a: { + enable: true, + refName: 'test', + path: path.resolve(__dirname, '../../plugins/plugin_a'), + }, +}; + diff --git a/test/fixtures/app_with_plugin_version_check/package.json b/test/fixtures/app_with_plugin_version_check/package.json new file mode 100644 index 00000000..11c0b88d --- /dev/null +++ b/test/fixtures/app_with_plugin_version_check/package.json @@ -0,0 +1,4 @@ +{ + "name": "mock_app", + "private": true +} diff --git a/test/fixtures/trigger/timer/tsconfig.json b/test/fixtures/app_with_plugin_version_check/tsconfig.json similarity index 52% rename from test/fixtures/trigger/timer/tsconfig.json rename to test/fixtures/app_with_plugin_version_check/tsconfig.json index 2597e011..2c9fb139 100644 --- a/test/fixtures/trigger/timer/tsconfig.json +++ b/test/fixtures/app_with_plugin_version_check/tsconfig.json @@ -1,7 +1,9 @@ { "compilerOptions": { "esModuleInterop": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "target": "ES6", + "moduleResolution": "node" }, "rootDir": "./src", "exclude": [] diff --git a/test/fixtures/app_with_preset_b/config/plugin.default.ts b/test/fixtures/app_with_preset_b/config/plugin.default.ts new file mode 100644 index 00000000..fd4fd752 --- /dev/null +++ b/test/fixtures/app_with_preset_b/config/plugin.default.ts @@ -0,0 +1,9 @@ +import path from 'path'; + +export default { + preset_b: { + enable: true, + path: path.resolve(__dirname, '../../plugins/preset_b'), + }, +}; + diff --git a/test/fixtures/app_with_preset_b/package.json b/test/fixtures/app_with_preset_b/package.json new file mode 100644 index 00000000..11c0b88d --- /dev/null +++ b/test/fixtures/app_with_preset_b/package.json @@ -0,0 +1,4 @@ +{ + "name": "mock_app", + "private": true +} diff --git a/test/fixtures/trigger/event/tsconfig.json b/test/fixtures/app_with_preset_b/tsconfig.json similarity index 52% rename from test/fixtures/trigger/event/tsconfig.json rename to test/fixtures/app_with_preset_b/tsconfig.json index 2597e011..2c9fb139 100644 --- a/test/fixtures/trigger/event/tsconfig.json +++ b/test/fixtures/app_with_preset_b/tsconfig.json @@ -1,7 +1,9 @@ { "compilerOptions": { "esModuleInterop": true, - "experimentalDecorators": true + "experimentalDecorators": true, + "target": "ES6", + "moduleResolution": "node" }, "rootDir": "./src", "exclude": [] diff --git a/test/fixtures/application_specific/package.json b/test/fixtures/application_specific/package.json deleted file mode 100644 index 6aa66cb2..00000000 --- a/test/fixtures/application_specific/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "application-specific" -} diff --git a/test/fixtures/application_specific/src/config/config.default.ts b/test/fixtures/application_specific/src/config/config.default.ts deleted file mode 100644 index 4ad00b55..00000000 --- a/test/fixtures/application_specific/src/config/config.default.ts +++ /dev/null @@ -1 +0,0 @@ -export const name = 'from application'; diff --git a/test/fixtures/application_specific/src/config/config.private.ts b/test/fixtures/application_specific/src/config/config.private.ts deleted file mode 100644 index 9efc02a5..00000000 --- a/test/fixtures/application_specific/src/config/config.private.ts +++ /dev/null @@ -1 +0,0 @@ -export const name = 'from application '; diff --git a/test/fixtures/application_specific/src/config/plugin.default.ts b/test/fixtures/application_specific/src/config/plugin.default.ts deleted file mode 100644 index 0324b1fd..00000000 --- a/test/fixtures/application_specific/src/config/plugin.default.ts +++ /dev/null @@ -1,16 +0,0 @@ -import path from 'path'; - -export default { - mysql: { - enable: true, - path: path.resolve(__dirname, '../plugins/artus_plugin_mysql_rds'), - }, - redis: { - enable: true, - path: path.resolve(__dirname, '../plugins/artus_plugin_redis'), - }, - base: { - enable: true, - path: path.resolve(__dirname, '../plugins/artus_plugin_base'), - }, -}; \ No newline at end of file diff --git a/test/fixtures/application_specific/src/controller/config.ts b/test/fixtures/application_specific/src/controller/config.ts deleted file mode 100644 index 349c683a..00000000 --- a/test/fixtures/application_specific/src/controller/config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Context } from '@artus/pipeline'; -import { HttpController, HttpMethod, HTTPMethodEnum } from '../../../frameworks/bar/src'; - -@HttpController() -export default class Hello { - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/config', - }) - async index(ctx: Context) { - const { params: { config } } = ctx.input; - return { - message: `get config succeed`, - config, - }; - } -} diff --git a/test/fixtures/application_specific/src/controller/hello.ts b/test/fixtures/application_specific/src/controller/hello.ts deleted file mode 100644 index 9b954198..00000000 --- a/test/fixtures/application_specific/src/controller/hello.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Context } from '@artus/pipeline'; -import { HttpController, HttpMethod, HTTPMethodEnum } from '../../../frameworks/bar/src'; - -@HttpController() -export default class Hello { - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/home', - }) - async index(ctx: Context) { - const { params: { config } } = ctx.input; - return { title: `Hello Artus ${config.name}` }; - } - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/get_name2', - }) - async name2(ctx: Context) { - const { params: { config } } = ctx.input; - return { title: `Hello Artus ${config.name2}` }; - } - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/get_name3', - }) - async name3(ctx: Context) { - const { params: { config } } = ctx.input; - return { title: `Hello Artus ${config.name3}` }; - } -} diff --git a/test/fixtures/application_specific/src/controller/plugin.ts b/test/fixtures/application_specific/src/controller/plugin.ts deleted file mode 100644 index 5b0bacfe..00000000 --- a/test/fixtures/application_specific/src/controller/plugin.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { HttpController, HttpMethod, HTTPMethodEnum } from '../../../frameworks/bar/src'; -import { Inject } from '@artus/injection'; -import { ArtusApplication } from '../../../../../src'; -import { Context } from '@artus/pipeline'; - -@HttpController() -export default class Hello { - @Inject('ARTUS_MYSQL') - private client: any; - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/plugin-mysql', - }) - async getMysqlClient() { - return { - client: await this.client.getClient(), - }; - } - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/plugin-redis', - }) - async getRedisClient(ctx: Context) { - const app: ArtusApplication = ctx.input.params.app; - let client; - try { - client = app.container.get('ARTUS_REDIS'); - } catch { - - } - - const result = client ? { - client: await client.getClient(), - } : { message: 'plugin redis not enabled' }; - return result; - } -} diff --git a/test/fixtures/application_specific/src/index.ts b/test/fixtures/application_specific/src/index.ts deleted file mode 100644 index 57b6992c..00000000 --- a/test/fixtures/application_specific/src/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -import path from 'path'; -import { Manifest, ArtusApplication, ArtusInjectEnum } from "../../../../src"; -import { AbstractBar } from '../../frameworks/bar/src'; -import { Inject, Injectable, ScopeEnum } from "@artus/injection"; - -@Injectable({ - scope: ScopeEnum.SINGLETON, -}) -export default class MyArtusApplication { - @Inject('ABSTRACT_BAR') - private bar: AbstractBar; - @Inject(ArtusInjectEnum.Application) - public artus: ArtusApplication; - - static async instance(manifest: Manifest): Promise { - const app = new ArtusApplication(); - await app.load(manifest, path.join(__dirname, '..')); - const instance = app.container.get(MyArtusApplication); - return instance; - } - - isListening(): boolean { - return this.bar.isListening(); - } - - async run() { - await this.artus.run(); - } -} - -export async function main(manifest: Manifest) { - const app = await MyArtusApplication.instance(manifest); - await app.run(); - return app; -} diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_base/meta.json b/test/fixtures/application_specific/src/plugins/artus_plugin_base/meta.json deleted file mode 100644 index d211bc09..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_base/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "base" -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_base/package.json b/test/fixtures/application_specific/src/plugins/artus_plugin_base/package.json deleted file mode 100644 index 2b5db373..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_base/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-base" -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_base/src/config.default.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_base/src/config.default.ts deleted file mode 100644 index 643f4cdf..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_base/src/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - mysql: { - clientName: 'mysql-ob-base', - }, -}; \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/meta.json b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/meta.json deleted file mode 100644 index 7daab0d4..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/meta.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "mysql", - "configDir": "src/custom_config", - "dependencies": [ - { - "name": "base" - } - ] -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/package.json b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/package.json deleted file mode 100644 index c5ea3c6c..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-mysql-ob" -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/app.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/app.ts deleted file mode 100644 index 32c6ae00..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import Client, { MysqlConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const mysql = this.app.container.get('ARTUS_MYSQL') as Client; - await mysql.init(this.app.config.mysql as MysqlConfig); - } -} diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/client.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/client.ts deleted file mode 100644 index cd43ce4d..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface MysqlConfig { - clientName: string -} - -@Injectable({ - id: 'ARTUS_MYSQL', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: MysqlConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts deleted file mode 100644 index 13aa1b50..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - mysql: { - clientName: 'mysql-ob', - }, -}; \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/meta.json b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/meta.json deleted file mode 100644 index 7b58b024..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "mysql" -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/package.json b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/package.json deleted file mode 100644 index f8da1338..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-mysql-rds" -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/app.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/app.ts deleted file mode 100644 index 32c6ae00..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import Client, { MysqlConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const mysql = this.app.container.get('ARTUS_MYSQL') as Client; - await mysql.init(this.app.config.mysql as MysqlConfig); - } -} diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/client.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/client.ts deleted file mode 100644 index cd43ce4d..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface MysqlConfig { - clientName: string -} - -@Injectable({ - id: 'ARTUS_MYSQL', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: MysqlConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts deleted file mode 100644 index 9b5306e0..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - mysql: { - clientName: 'mysql-rds', - }, -}; \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/meta.json b/test/fixtures/application_specific/src/plugins/artus_plugin_redis/meta.json deleted file mode 100644 index fc830228..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "redis" -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/package.json b/test/fixtures/application_specific/src/plugins/artus_plugin_redis/package.json deleted file mode 100644 index d6903299..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-redis" -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/app.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/app.ts deleted file mode 100644 index 0dd866e2..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import Client, { RedisConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const redis = this.app.container.get('ARTUS_REDIS') as Client; - await redis.init(this.app.config.redis as RedisConfig); - } -} diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/client.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/client.ts deleted file mode 100644 index 6d4a1089..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface RedisConfig { - clientName: string -} - -@Injectable({ - id: 'ARTUS_REDIS', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: RedisConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/config/config.default.ts b/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/config/config.default.ts deleted file mode 100644 index 70e47a01..00000000 --- a/test/fixtures/application_specific/src/plugins/artus_plugin_redis/src/config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - redis: { - clientName: 'redis', - }, -}; \ No newline at end of file diff --git a/test/fixtures/artus_application/package.json b/test/fixtures/artus_application/package.json deleted file mode 100644 index e3c65571..00000000 --- a/test/fixtures/artus_application/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "artus-application" -} diff --git a/test/fixtures/artus_application/src/config/config.default.ts b/test/fixtures/artus_application/src/config/config.default.ts deleted file mode 100644 index 4ad00b55..00000000 --- a/test/fixtures/artus_application/src/config/config.default.ts +++ /dev/null @@ -1 +0,0 @@ -export const name = 'from application'; diff --git a/test/fixtures/artus_application/src/config/config.private.ts b/test/fixtures/artus_application/src/config/config.private.ts deleted file mode 100644 index 9efc02a5..00000000 --- a/test/fixtures/artus_application/src/config/config.private.ts +++ /dev/null @@ -1 +0,0 @@ -export const name = 'from application '; diff --git a/test/fixtures/artus_application/src/config/framework.ts b/test/fixtures/artus_application/src/config/framework.ts deleted file mode 100644 index 671bb6d7..00000000 --- a/test/fixtures/artus_application/src/config/framework.ts +++ /dev/null @@ -1,5 +0,0 @@ -import path from 'path'; - -export default { - path: path.resolve(__dirname, '../../../frameworks/bar'), -}; diff --git a/test/fixtures/artus_application/src/config/plugin.default.ts b/test/fixtures/artus_application/src/config/plugin.default.ts deleted file mode 100644 index 0324b1fd..00000000 --- a/test/fixtures/artus_application/src/config/plugin.default.ts +++ /dev/null @@ -1,16 +0,0 @@ -import path from 'path'; - -export default { - mysql: { - enable: true, - path: path.resolve(__dirname, '../plugins/artus_plugin_mysql_rds'), - }, - redis: { - enable: true, - path: path.resolve(__dirname, '../plugins/artus_plugin_redis'), - }, - base: { - enable: true, - path: path.resolve(__dirname, '../plugins/artus_plugin_base'), - }, -}; \ No newline at end of file diff --git a/test/fixtures/artus_application/src/config/plugin.private.ts b/test/fixtures/artus_application/src/config/plugin.private.ts deleted file mode 100644 index b7014e1c..00000000 --- a/test/fixtures/artus_application/src/config/plugin.private.ts +++ /dev/null @@ -1,11 +0,0 @@ -import path from 'path'; - -export default { - mysql: { - enable: true, - path: path.resolve(__dirname, '../plugins/artus_plugin_mysql_ob'), - }, - redis: { - enable: false, - }, -}; \ No newline at end of file diff --git a/test/fixtures/artus_application/src/controller/config.ts b/test/fixtures/artus_application/src/controller/config.ts deleted file mode 100644 index 349c683a..00000000 --- a/test/fixtures/artus_application/src/controller/config.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Context } from '@artus/pipeline'; -import { HttpController, HttpMethod, HTTPMethodEnum } from '../../../frameworks/bar/src'; - -@HttpController() -export default class Hello { - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/config', - }) - async index(ctx: Context) { - const { params: { config } } = ctx.input; - return { - message: `get config succeed`, - config, - }; - } -} diff --git a/test/fixtures/artus_application/src/controller/hello.ts b/test/fixtures/artus_application/src/controller/hello.ts deleted file mode 100644 index 9b954198..00000000 --- a/test/fixtures/artus_application/src/controller/hello.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Context } from '@artus/pipeline'; -import { HttpController, HttpMethod, HTTPMethodEnum } from '../../../frameworks/bar/src'; - -@HttpController() -export default class Hello { - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/home', - }) - async index(ctx: Context) { - const { params: { config } } = ctx.input; - return { title: `Hello Artus ${config.name}` }; - } - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/get_name2', - }) - async name2(ctx: Context) { - const { params: { config } } = ctx.input; - return { title: `Hello Artus ${config.name2}` }; - } - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/get_name3', - }) - async name3(ctx: Context) { - const { params: { config } } = ctx.input; - return { title: `Hello Artus ${config.name3}` }; - } -} diff --git a/test/fixtures/artus_application/src/controller/plugin.ts b/test/fixtures/artus_application/src/controller/plugin.ts deleted file mode 100644 index d34bea7c..00000000 --- a/test/fixtures/artus_application/src/controller/plugin.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { HttpController, HttpMethod, HTTPMethodEnum } from '../../../frameworks/bar/src'; -import { Inject } from '@artus/injection'; -import { ArtusApplication } from '../../../../../src'; -import { Context } from '@artus/pipeline'; - -@HttpController() -export default class Hello { - @Inject('ARTUS_MYSQL') - private client: any; - @Inject('ARTUS_HBASE') - private client2: any; - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/plugin-mysql', - }) - async getMysqlClient() { - return { - client: await this.client.getClient(), - }; - } - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/plugin-redis', - }) - async getRedisClient(ctx: Context) { - const app: ArtusApplication = ctx.input.params.app; - let client; - try { - client = app.container.get('ARTUS_REDIS'); - } catch { - - } - - const result = client ? { - client: await client.getClient(), - } : { message: 'plugin redis not enabled' }; - return result; - } - - @HttpMethod({ - method: HTTPMethodEnum.GET, - path: '/plugin-hbase', - }) - async getHbaseClient() { - return { - client: await this.client2.getClient(), - }; - } -} diff --git a/test/fixtures/artus_application/src/index.ts b/test/fixtures/artus_application/src/index.ts deleted file mode 100644 index 57b6992c..00000000 --- a/test/fixtures/artus_application/src/index.ts +++ /dev/null @@ -1,35 +0,0 @@ -import path from 'path'; -import { Manifest, ArtusApplication, ArtusInjectEnum } from "../../../../src"; -import { AbstractBar } from '../../frameworks/bar/src'; -import { Inject, Injectable, ScopeEnum } from "@artus/injection"; - -@Injectable({ - scope: ScopeEnum.SINGLETON, -}) -export default class MyArtusApplication { - @Inject('ABSTRACT_BAR') - private bar: AbstractBar; - @Inject(ArtusInjectEnum.Application) - public artus: ArtusApplication; - - static async instance(manifest: Manifest): Promise { - const app = new ArtusApplication(); - await app.load(manifest, path.join(__dirname, '..')); - const instance = app.container.get(MyArtusApplication); - return instance; - } - - isListening(): boolean { - return this.bar.isListening(); - } - - async run() { - await this.artus.run(); - } -} - -export async function main(manifest: Manifest) { - const app = await MyArtusApplication.instance(manifest); - await app.run(); - return app; -} diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_base/meta.json b/test/fixtures/artus_application/src/plugins/artus_plugin_base/meta.json deleted file mode 100644 index d211bc09..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_base/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "base" -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_base/package.json b/test/fixtures/artus_application/src/plugins/artus_plugin_base/package.json deleted file mode 100644 index 2b5db373..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_base/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-base" -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_base/src/config.default.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_base/src/config.default.ts deleted file mode 100644 index 643f4cdf..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_base/src/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - mysql: { - clientName: 'mysql-ob-base', - }, -}; \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/meta.json b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/meta.json deleted file mode 100644 index 7daab0d4..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/meta.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "name": "mysql", - "configDir": "src/custom_config", - "dependencies": [ - { - "name": "base" - } - ] -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/package.json b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/package.json deleted file mode 100644 index c5ea3c6c..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-mysql-ob" -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/app.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/app.ts deleted file mode 100644 index 32c6ae00..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import Client, { MysqlConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const mysql = this.app.container.get('ARTUS_MYSQL') as Client; - await mysql.init(this.app.config.mysql as MysqlConfig); - } -} diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/client.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/client.ts deleted file mode 100644 index cd43ce4d..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface MysqlConfig { - clientName: string -} - -@Injectable({ - id: 'ARTUS_MYSQL', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: MysqlConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts deleted file mode 100644 index 13aa1b50..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_ob/src/custom_config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - mysql: { - clientName: 'mysql-ob', - }, -}; \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/meta.json b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/meta.json deleted file mode 100644 index 7b58b024..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "mysql" -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/package.json b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/package.json deleted file mode 100644 index f8da1338..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-mysql-rds" -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/app.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/app.ts deleted file mode 100644 index 32c6ae00..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import Client, { MysqlConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const mysql = this.app.container.get('ARTUS_MYSQL') as Client; - await mysql.init(this.app.config.mysql as MysqlConfig); - } -} diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/client.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/client.ts deleted file mode 100644 index cd43ce4d..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface MysqlConfig { - clientName: string -} - -@Injectable({ - id: 'ARTUS_MYSQL', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: MysqlConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts deleted file mode 100644 index 9b5306e0..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_mysql_rds/src/config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - mysql: { - clientName: 'mysql-rds', - }, -}; \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/meta.json b/test/fixtures/artus_application/src/plugins/artus_plugin_redis/meta.json deleted file mode 100644 index fc830228..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "redis" -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/package.json b/test/fixtures/artus_application/src/plugins/artus_plugin_redis/package.json deleted file mode 100644 index d6903299..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-redis" -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/app.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/app.ts deleted file mode 100644 index 0dd866e2..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import Client, { RedisConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const redis = this.app.container.get('ARTUS_REDIS') as Client; - await redis.init(this.app.config.redis as RedisConfig); - } -} diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/client.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/client.ts deleted file mode 100644 index b381d71e..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface RedisConfig { - clientName: string, -} - -@Injectable({ - id: 'ARTUS_REDIS', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: RedisConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/config/config.default.ts b/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/config/config.default.ts deleted file mode 100644 index 70e47a01..00000000 --- a/test/fixtures/artus_application/src/plugins/artus_plugin_redis/src/config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - redis: { - clientName: 'redis', - }, -}; \ No newline at end of file diff --git a/test/fixtures/exception_filter/bootstrap.ts b/test/fixtures/exception_filter/bootstrap.ts index fa9239f1..ca1ce8b6 100644 --- a/test/fixtures/exception_filter/bootstrap.ts +++ b/test/fixtures/exception_filter/bootstrap.ts @@ -1,65 +1,60 @@ -import { Context } from '@artus/pipeline'; -import path from 'path'; -import { ArtusApplication, ArtusStdError, Trigger } from '../../../src'; -import { TestCustomError, TestWrappedError } from './error'; +import path from "path"; +import { ArtusApplication } from "../../../src"; async function main() { const app = new ArtusApplication(); app.container.set({ - id: 'mock_exception_set', + id: "mock_exception_set", value: new Set(), }); await app.load({ - items: [ - { - path: path.resolve(__dirname, './filter'), - extname: '.ts', - filename: 'filter.ts', - loader: 'exception-filter', - loaderState: { - exportNames: [ - 'TestDefaultExceptionHandler', - 'TestAppCodeExceptionHandler', - 'TestWrappedExceptionHandler', - 'TestCustomExceptionHandler', - ], - }, - source: 'app', + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(__dirname, "./filter"), + extname: ".ts", + filename: "filter.ts", + loader: "exception-filter", + loaderState: { + exportNames: [ + "TestDefaultExceptionHandler", + "TestAppCodeExceptionHandler", + "TestWrappedExceptionHandler", + "TestCustomExceptionHandler", + ], + }, + source: "app", + }, + { + path: path.resolve(__dirname, "../../../exception.json"), + extname: ".json", + filename: "exception.json", + loader: "exception", + source: "app", + }, + { + path: path.resolve(__dirname, "./exception.json"), + extname: ".json", + filename: "exception.json", + loader: "exception", + source: "app", + }, + { + path: path.resolve(__dirname, "./service"), + extname: ".ts", + filename: "service.ts", + loader: "module", + source: "app", + }, + ], }, - { - path: path.resolve(__dirname, '../../../exception.json'), - extname: '.json', - filename: 'exception.json', - loader: 'exception', - source: 'app', - }, - { - path: path.resolve(__dirname, './exception.json'), - extname: '.json', - filename: 'exception.json', - loader: 'exception', - source: 'app', - }, - ], - }); - const trigger = app.container.get(Trigger); - trigger.use((ctx: Context) => { - const target = ctx.input.params.target; - switch(target) { - case 'default': - throw new Error('default error'); - case 'custom': - throw new TestCustomError(); - case 'wrapped': - throw new TestWrappedError(); - default: - throw new ArtusStdError(target); - } + }, }); await app.run(); return app; } -export { - main, -}; +export { main }; diff --git a/test/fixtures/exception_filter/service.ts b/test/fixtures/exception_filter/service.ts new file mode 100644 index 00000000..3b281cb1 --- /dev/null +++ b/test/fixtures/exception_filter/service.ts @@ -0,0 +1,20 @@ +import { Injectable } from '@artus/injection'; +import { ArtusStdError } from '../../../src'; +import { TestCustomError, TestWrappedError } from './error'; + +@Injectable() +export default class MockErrorService { + + throw(target: string) { + switch (target) { + case "default": + throw new Error("default error"); + case "custom": + throw new TestCustomError(); + case "wrapped": + throw new TestWrappedError(); + default: + throw new ArtusStdError(target); + } + } +} diff --git a/test/fixtures/exception_invalid_filter/bootstrap.ts b/test/fixtures/exception_invalid_filter/bootstrap.ts index c7e910d5..1dc6cb46 100644 --- a/test/fixtures/exception_invalid_filter/bootstrap.ts +++ b/test/fixtures/exception_invalid_filter/bootstrap.ts @@ -1,32 +1,34 @@ -import path from 'path'; -import { ArtusApplication } from '../../../src'; +import path from "path"; +import { ArtusApplication } from "../../../src"; async function main() { const app = new ArtusApplication(); app.container.set({ - id: 'mock_exception_set', + id: "mock_exception_set", value: new Set(), }); await app.load({ - items: [ - { - path: path.resolve(__dirname, './filter'), - extname: '.ts', - filename: 'filter.ts', - loader: 'exception-filter', - loaderState: { - exportNames: [ - 'TestInvalidFilter', - ], - }, - source: 'app', + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(__dirname, "./filter"), + extname: ".ts", + filename: "filter.ts", + loader: "exception-filter", + loaderState: { + exportNames: ["TestInvalidFilter"], + }, + source: "app", + }, + ], }, - ], + }, }); await app.run(); return app; } -export { - main, -}; +export { main }; diff --git a/test/fixtures/exception_with_ts_yaml/bootstrap.ts b/test/fixtures/exception_with_ts_yaml/bootstrap.ts index 694bc784..278e9acd 100644 --- a/test/fixtures/exception_with_ts_yaml/bootstrap.ts +++ b/test/fixtures/exception_with_ts_yaml/bootstrap.ts @@ -1,33 +1,39 @@ -import path from 'path'; -import { ArtusApplication } from '../../../src'; -import { server } from './app'; +import path from "path"; +import { ArtusApplication } from "../../../src"; +import { server } from "./app"; async function main() { const app = new ArtusApplication(); await app.load({ - items: [ - { - path: path.resolve(__dirname, './app'), - extname: '.ts', - filename: 'app.ts', - loader: 'lifecycle-hook-unit', - source: 'app', + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(__dirname, "./app"), + extname: ".ts", + filename: "app.ts", + loader: "lifecycle-hook-unit", + source: "app", + }, + { + path: path.resolve(__dirname, "../../../exception.json"), + extname: ".json", + filename: "exception.json", + loader: "exception", + source: "app", + }, + { + path: path.resolve(__dirname, "./exception.json"), + extname: ".json", + filename: "exception.json", + loader: "exception", + source: "app", + }, + ], }, - { - path: path.resolve(__dirname, '../../../exception.json'), - extname: '.json', - filename: 'exception.json', - loader: 'exception', - source: 'app', - }, - { - path: path.resolve(__dirname, './exception.json'), - extname: '.json', - filename: 'exception.json', - loader: 'exception', - source: 'app', - }, - ], + }, }); await app.run(); return app; @@ -35,7 +41,4 @@ async function main() { const isListening = () => server.listening; -export { - main, - isListening, -}; +export { main, isListening }; diff --git a/test/fixtures/frameworks/abstract/foo/index.ts b/test/fixtures/frameworks/abstract/foo/index.ts deleted file mode 100644 index f254f345..00000000 --- a/test/fixtures/frameworks/abstract/foo/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Trigger } from '../../../../../src'; - -export interface AbstractFoo { - isListening: () => boolean -} - -export class HttpTrigger extends Trigger { } \ No newline at end of file diff --git a/test/fixtures/frameworks/bar/package.json b/test/fixtures/frameworks/bar/package.json deleted file mode 100644 index 694594cf..00000000 --- a/test/fixtures/frameworks/bar/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "bar" -} diff --git a/test/fixtures/frameworks/bar/src/config/config.default.ts b/test/fixtures/frameworks/bar/src/config/config.default.ts deleted file mode 100644 index 6f1dbda2..00000000 --- a/test/fixtures/frameworks/bar/src/config/config.default.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const port = 3003; - -export const name = 'from framework: bar'; diff --git a/test/fixtures/frameworks/bar/src/config/framework.default.ts b/test/fixtures/frameworks/bar/src/config/framework.default.ts deleted file mode 100644 index 32360764..00000000 --- a/test/fixtures/frameworks/bar/src/config/framework.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -import path from 'path'; - -export default { - path: path.join(__dirname, '../../../layer/foo/foo1'), -}; diff --git a/test/fixtures/frameworks/bar/src/config/framework.private.ts b/test/fixtures/frameworks/bar/src/config/framework.private.ts deleted file mode 100644 index 7cc07a62..00000000 --- a/test/fixtures/frameworks/bar/src/config/framework.private.ts +++ /dev/null @@ -1,5 +0,0 @@ -import path from 'path'; - -export default { - path: path.join(__dirname, '../../../layer/foo/foo2'), -}; diff --git a/test/fixtures/frameworks/bar/src/http.ts b/test/fixtures/frameworks/bar/src/http.ts deleted file mode 100644 index dd39f968..00000000 --- a/test/fixtures/frameworks/bar/src/http.ts +++ /dev/null @@ -1,80 +0,0 @@ -import 'reflect-metadata'; -import { Context, Next } from '@artus/pipeline'; -import { Constructable, Injectable, ScopeEnum } from '@artus/injection'; -import { HttpTrigger } from '../../abstract/foo'; - -export const enum HTTPMethodEnum { - GET = 'GET', - POST = 'POST', - DELETE = ' DELETE', - PUT = 'PUT', -} - -export type ControllerParams = { - path?: string -}; - -export type HttpParams = { - method: HTTPMethodEnum, - path: string -}; - -export type ControllerMeta = { - prefix: string, - clazz: Constructable -}; - -export const controllerMap = new Set(); - -export const HOOK_HTTP_META_PREFIX = 'ARTUS#HOOK_HTTP_META_PREFIX::'; - -export function HttpController(options?: ControllerParams): ClassDecorator { - const prefix = options?.path ?? ''; - return (target: any) => { - controllerMap.add({ prefix, clazz: target }); - Injectable({ - scope: ScopeEnum.EXECUTION, - })(target); - }; -} - -export function HttpMethod(options: HttpParams): PropertyDecorator { - return (target: any, propertyKey: string | symbol) => { - if (typeof propertyKey === 'symbol') { - throw new Error(`http hookName is not support symbol [${propertyKey.description}]`); - } - Reflect.defineMetadata(`${HOOK_HTTP_META_PREFIX}${propertyKey}`, options, target.constructor); - Injectable({ scope: ScopeEnum.EXECUTION })(target); - }; -} - -export function registerController(trigger: HttpTrigger) { - for (const controller of controllerMap) { - const { prefix, clazz } = controller; - const fnMetaKeys = Reflect.getMetadataKeys(clazz); - - for (let key of fnMetaKeys) { - if (typeof key !== 'string') { - continue; - } - if (!key.startsWith(HOOK_HTTP_META_PREFIX)) { - continue; - } - - // register controller - const { method, path } = Reflect.getMetadata(key, clazz); - key = key.replace(HOOK_HTTP_META_PREFIX, ''); - - // match router - trigger.use(async (ctx: Context, next: Next) => { - const { input: { params: { req } } } = ctx; - if (req.url === `${prefix}${path}` && req.method === method) { - const instance: any = ctx.container.get(clazz); - const target = instance[key]; - ctx.output.data.content = await target.call(instance, ctx); - } - await next(); - }); - } - } -} diff --git a/test/fixtures/frameworks/bar/src/index.ts b/test/fixtures/frameworks/bar/src/index.ts deleted file mode 100644 index 0e5b9e2a..00000000 --- a/test/fixtures/frameworks/bar/src/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Inject, Injectable, ScopeEnum } from "@artus/injection"; -import { AbstractFoo } from "../../abstract/foo"; - -export interface AbstractBar extends AbstractFoo { } - -@Injectable({ - id: 'ABSTRACT_BAR', - scope: ScopeEnum.SINGLETON, -}) -export class FrameworkBar implements AbstractBar { - @Inject('ABSTRACT_FOO') - private foo: AbstractFoo; - - isListening() { - return this.foo.isListening(); - } -} - - -export * from './http'; \ No newline at end of file diff --git a/test/fixtures/frameworks/bar/src/lifecycle.ts b/test/fixtures/frameworks/bar/src/lifecycle.ts deleted file mode 100644 index 8bd3ed36..00000000 --- a/test/fixtures/frameworks/bar/src/lifecycle.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../src/types'; -import { ArtusInjectEnum, ArtusApplication, Inject } from '../../../../../src'; -import { registerController } from './/http'; -import { HttpTrigger } from '../../abstract/foo'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - @Inject() - trigger: HttpTrigger; - - @LifecycleHook() - async didLoad() { - // register controller - registerController(this.trigger); - } -} diff --git a/test/fixtures/frameworks/layer/foo/foo1/package.json b/test/fixtures/frameworks/layer/foo/foo1/package.json deleted file mode 100644 index 08b55689..00000000 --- a/test/fixtures/frameworks/layer/foo/foo1/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "foo1" -} diff --git a/test/fixtures/frameworks/layer/foo/foo1/src/config/config.default.ts b/test/fixtures/frameworks/layer/foo/foo1/src/config/config.default.ts deleted file mode 100644 index 5d40f178..00000000 --- a/test/fixtures/frameworks/layer/foo/foo1/src/config/config.default.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const port = 3002; - -export const name = 'from framework: foo1'; - -export const name2 = '[name2] from framework: foo1 [default]'; - -export const name3 = '[name3] from framework: foo1 [default]'; diff --git a/test/fixtures/frameworks/layer/foo/foo1/src/config/config.private.ts b/test/fixtures/frameworks/layer/foo/foo1/src/config/config.private.ts deleted file mode 100644 index fb490968..00000000 --- a/test/fixtures/frameworks/layer/foo/foo1/src/config/config.private.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const name2 = '[name2] from framework: foo1 [private]'; - -export const name3 = '[name3] from framework: foo1 [private]'; diff --git a/test/fixtures/frameworks/layer/foo/foo1/src/index.ts b/test/fixtures/frameworks/layer/foo/foo1/src/index.ts deleted file mode 100644 index b620bfc0..00000000 --- a/test/fixtures/frameworks/layer/foo/foo1/src/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; -import { ArtusApplication } from '../../../../../../../src'; -import { server } from './lifecycle'; - -@Injectable({ - id: 'ABSTRACT_FOO', - scope: ScopeEnum.SINGLETON, -}) -export class FrameworkFoo extends ArtusApplication { - isListening(): boolean { - return server?.listening; - } -} diff --git a/test/fixtures/frameworks/layer/foo/foo1/src/lifecycle.ts b/test/fixtures/frameworks/layer/foo/foo1/src/lifecycle.ts deleted file mode 100644 index 314900c6..00000000 --- a/test/fixtures/frameworks/layer/foo/foo1/src/lifecycle.ts +++ /dev/null @@ -1,35 +0,0 @@ -import http, { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { Input } from '@artus/pipeline'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import { HttpTrigger } from '../../../../abstract/foo'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - @Inject() - trigger: HttpTrigger; - - @LifecycleHook() - willReady() { - const config = this.app.config ?? {}; - const port = config.port; - server = http - .createServer(async (req: http.IncomingMessage, res: http.ServerResponse) => { - const input = new Input(); - input.params = { req, res, config, app: this.app }; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); - }) - .listen(port); - } - - @LifecycleHook() - beforeClose() { - server?.close(); - } -} diff --git a/test/fixtures/frameworks/layer/foo/foo1/src/trigger/http.ts b/test/fixtures/frameworks/layer/foo/foo1/src/trigger/http.ts deleted file mode 100644 index a185e86f..00000000 --- a/test/fixtures/frameworks/layer/foo/foo1/src/trigger/http.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Stream } from 'stream'; -import { Context, Next } from '@artus/pipeline'; -import { Injectable, ScopeEnum, Trigger } from '../../../../../../../../src'; -import { HttpTrigger } from '../../../../../abstract/foo'; - -@Injectable({ id: HttpTrigger, scope: ScopeEnum.SINGLETON }) -export default class HttpTriggerImpl extends Trigger { - constructor() { - super(); - // first of all - this.use(async (ctx: Context, next: Next) => { - await next(); - await this.respond(ctx); - }); - } - - async respond(ctx: Context) { - const { res } = ctx.input.params; - const { data } = ctx.output; - - res.status = data.status || 200; - const { content } = data; - - if (Buffer.isBuffer(content) || typeof content === 'string') { - return res.end(content); - } - - if (content instanceof Stream) { - return content.pipe(res); - } - - return res.end(JSON.stringify(content)); - } -} \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/index.ts b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/index.ts deleted file mode 100644 index b9f001e0..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './src/client' \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/meta.json b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/meta.json deleted file mode 100644 index cab84c62..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "hbase", - "configDir": "src/custom_config" -} \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/package.json b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/package.json deleted file mode 100644 index 24c1049b..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-hbase" -} \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/app.ts b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/app.ts deleted file mode 100644 index d2904dfe..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../../../src'; -import Client, { MysqlConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const hbase = this.app.container.get('ARTUS_HBASE') as Client; - await hbase.init(this.app.config.hbase as MysqlConfig); - } -} diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/client.ts b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/client.ts deleted file mode 100644 index 34f7818c..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface MysqlConfig { - clientName: string -} - -@Injectable({ - id: 'ARTUS_HBASE', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: MysqlConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/custom_config/config.default.ts b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/custom_config/config.default.ts deleted file mode 100644 index cf0d1bce..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_hbase/src/custom_config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - hbase: { - clientName: 'foo2-hbase', - }, -}; \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/meta.json b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/meta.json deleted file mode 100644 index 7b58b024..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/meta.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "mysql" -} \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/package.json b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/package.json deleted file mode 100644 index f8da1338..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "@artus/plugin-mysql-rds" -} \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/app.ts b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/app.ts deleted file mode 100644 index 32c6ae00..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/app.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import Client, { MysqlConfig } from './client'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - - @LifecycleHook() - async willReady() { - const mysql = this.app.container.get('ARTUS_MYSQL') as Client; - await mysql.init(this.app.config.mysql as MysqlConfig); - } -} diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/client.ts b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/client.ts deleted file mode 100644 index cd43ce4d..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/client.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; - -export interface MysqlConfig { - clientName: string -} - -@Injectable({ - id: 'ARTUS_MYSQL', - scope: ScopeEnum.SINGLETON, -}) -export default class Client { - private clientName = ''; - - async init(config: MysqlConfig) { - this.clientName = config.clientName; - } - - async getClient(): Promise { - return this.clientName; - } -} \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/config/config.default.ts b/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/config/config.default.ts deleted file mode 100644 index 9b5306e0..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/node_modules/artus_plugin_mysql_rds/src/config/config.default.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default { - mysql: { - clientName: 'mysql-rds', - }, -}; \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/package.json b/test/fixtures/frameworks/layer/foo/foo2/package.json deleted file mode 100644 index 9bf71997..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "name": "foo2" -} diff --git a/test/fixtures/frameworks/layer/foo/foo2/src/config/config.default.ts b/test/fixtures/frameworks/layer/foo/foo2/src/config/config.default.ts deleted file mode 100644 index 8c0798d3..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/src/config/config.default.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const port = 3002; - -export const name = 'from framework: foo2'; - -export const name2 = '[name2] from framework: foo2 [default]'; - -export const name3 = '[name3] from framework: foo2 [default]'; - diff --git a/test/fixtures/frameworks/layer/foo/foo2/src/config/config.private.ts b/test/fixtures/frameworks/layer/foo/foo2/src/config/config.private.ts deleted file mode 100644 index 5ea55b5c..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/src/config/config.private.ts +++ /dev/null @@ -1 +0,0 @@ -export const name3 = '[name3] from framework: foo2 [private]'; diff --git a/test/fixtures/frameworks/layer/foo/foo2/src/config/plugin.ts b/test/fixtures/frameworks/layer/foo/foo2/src/config/plugin.ts deleted file mode 100644 index 77ccf66e..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/src/config/plugin.ts +++ /dev/null @@ -1,6 +0,0 @@ -export default { - hbase: { - enable: true, - package: 'artus_plugin_hbase', - }, -}; \ No newline at end of file diff --git a/test/fixtures/frameworks/layer/foo/foo2/src/index.ts b/test/fixtures/frameworks/layer/foo/foo2/src/index.ts deleted file mode 100644 index b620bfc0..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/src/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable, ScopeEnum } from "@artus/injection"; -import { ArtusApplication } from '../../../../../../../src'; -import { server } from './lifecycle'; - -@Injectable({ - id: 'ABSTRACT_FOO', - scope: ScopeEnum.SINGLETON, -}) -export class FrameworkFoo extends ArtusApplication { - isListening(): boolean { - return server?.listening; - } -} diff --git a/test/fixtures/frameworks/layer/foo/foo2/src/lifecycle.ts b/test/fixtures/frameworks/layer/foo/foo2/src/lifecycle.ts deleted file mode 100644 index 314900c6..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/src/lifecycle.ts +++ /dev/null @@ -1,35 +0,0 @@ -import http, { Server } from 'http'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../../../../src/types'; -import { Input } from '@artus/pipeline'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../../../../src'; -import { HttpTrigger } from '../../../../abstract/foo'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - @Inject() - trigger: HttpTrigger; - - @LifecycleHook() - willReady() { - const config = this.app.config ?? {}; - const port = config.port; - server = http - .createServer(async (req: http.IncomingMessage, res: http.ServerResponse) => { - const input = new Input(); - input.params = { req, res, config, app: this.app }; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); - }) - .listen(port); - } - - @LifecycleHook() - beforeClose() { - server?.close(); - } -} diff --git a/test/fixtures/frameworks/layer/foo/foo2/src/trigger/http.ts b/test/fixtures/frameworks/layer/foo/foo2/src/trigger/http.ts deleted file mode 100644 index a185e86f..00000000 --- a/test/fixtures/frameworks/layer/foo/foo2/src/trigger/http.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Stream } from 'stream'; -import { Context, Next } from '@artus/pipeline'; -import { Injectable, ScopeEnum, Trigger } from '../../../../../../../../src'; -import { HttpTrigger } from '../../../../../abstract/foo'; - -@Injectable({ id: HttpTrigger, scope: ScopeEnum.SINGLETON }) -export default class HttpTriggerImpl extends Trigger { - constructor() { - super(); - // first of all - this.use(async (ctx: Context, next: Next) => { - await next(); - await this.respond(ctx); - }); - } - - async respond(ctx: Context) { - const { res } = ctx.input.params; - const { data } = ctx.output; - - res.status = data.status || 200; - const { content } = data; - - if (Buffer.isBuffer(content) || typeof content === 'string') { - return res.end(content); - } - - if (content instanceof Stream) { - return content.pipe(res); - } - - return res.end(JSON.stringify(content)); - } -} \ No newline at end of file diff --git a/test/fixtures/logger/src/index.ts b/test/fixtures/logger/src/index.ts index 46d0344a..f2061c8c 100644 --- a/test/fixtures/logger/src/index.ts +++ b/test/fixtures/logger/src/index.ts @@ -1,33 +1,44 @@ -import path from 'path'; -import { Manifest } from '../../../../src'; +import path from "path"; +import { Manifest } from "../../../../src"; -const rootDir = path.resolve(__dirname, './'); +const rootDir = path.resolve(__dirname, "./"); - -const defaultManifest: Manifest = { - items: [ - { - path: path.resolve(rootDir, './test_clazz.ts'), - extname: '.ts', - filename: 'test_clazz.ts', +const defaultManifest: Manifest = { + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(rootDir, "./test_clazz.ts"), + extname: ".ts", + filename: "test_clazz.ts", + }, + ], }, - ], + }, }; export const manifestWithCustomLogger: Manifest = { - items: [ - ...defaultManifest.items, - { - path: path.resolve(rootDir, './test_custom_clazz.ts'), - extname: '.ts', - filename: 'test_custom_clazz.ts', - }, - { - path: path.resolve(rootDir, './custom_logger.ts'), - extname: '.ts', - filename: 'custom_logger.ts', + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + ...defaultManifest.refMap._app.items, + { + path: path.resolve(rootDir, "./test_custom_clazz.ts"), + extname: ".ts", + filename: "test_custom_clazz.ts", + }, + { + path: path.resolve(rootDir, "./custom_logger.ts"), + extname: ".ts", + filename: "custom_logger.ts", + }, + ], }, - ], + }, }; export default defaultManifest; diff --git a/test/fixtures/module_with_custom_loader/src/index.ts b/test/fixtures/module_with_custom_loader/src/index.ts index 64354e88..7c27ecc9 100644 --- a/test/fixtures/module_with_custom_loader/src/index.ts +++ b/test/fixtures/module_with_custom_loader/src/index.ts @@ -1,19 +1,24 @@ -import path from 'path'; -import { Manifest } from '../../../../src'; +import path from "path"; +import { Manifest } from "../../../../src"; -const rootDir = path.resolve(__dirname, './'); +const rootDir = path.resolve(__dirname, "./"); - -export default ({ - items: [ - { - path: path.resolve(rootDir, './test_clazz.ts'), - extname: '.ts', - filename: 'test_clazz.ts', - loader: 'test-custom-loader', - loaderState: { - hello: 'loaderState', - }, +export default { + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(rootDir, "./test_clazz.ts"), + extname: ".ts", + filename: "test_clazz.ts", + loader: "test-custom-loader", + loaderState: { + hello: "loaderState", + }, + }, + ], }, - ], -}) as Manifest; + }, +} as Manifest; diff --git a/test/fixtures/module_with_js/src/index.js b/test/fixtures/module_with_js/src/index.js index 4f7c89b0..7716d36c 100644 --- a/test/fixtures/module_with_js/src/index.js +++ b/test/fixtures/module_with_js/src/index.js @@ -1,21 +1,27 @@ -const path = require('path'); +const path = require("path"); -const rootDir = path.resolve(__dirname, './'); +const rootDir = path.resolve(__dirname, "./"); -module.exports = ({ - items: [ - { - id: 'testServiceA', - path: path.resolve(rootDir, './test_service_a.js'), - extname: '.js', - filename: 'test_service_a.js', +module.exports = { + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + id: "testServiceA", + path: path.resolve(rootDir, "./test_service_a.js"), + extname: ".js", + filename: "test_service_a.js", + }, + { + id: "testServiceB", + scope: "Execution", + path: path.resolve(rootDir, "./test_service_b.js"), + extname: ".js", + filename: "test_service_b.js", + }, + ], }, - { - id: 'testServiceB', - scope: 'Execution', - path: path.resolve(rootDir, './test_service_b.js'), - extname: '.js', - filename: 'test_service_b.js', - } - ] -}); + }, +}; diff --git a/test/fixtures/module_with_ts/src/index.ts b/test/fixtures/module_with_ts/src/index.ts index 16789cdb..e7a8c8f6 100644 --- a/test/fixtures/module_with_ts/src/index.ts +++ b/test/fixtures/module_with_ts/src/index.ts @@ -1,19 +1,25 @@ -import path from 'path'; -import { Manifest } from '../../../../src'; +import path from "path"; +import { Manifest } from "../../../../src"; -const rootDir = path.resolve(__dirname, './'); +const rootDir = path.resolve(__dirname, "./"); -export default ({ - items: [ - { - path: path.resolve(rootDir, './test_service_a.ts'), - extname: '.ts', - filename: 'test_service_a.ts', +export default { + version: "2", + pluginConfig: {}, + refMap: { + _app: { + items: [ + { + path: path.resolve(rootDir, "./test_service_a.ts"), + extname: ".ts", + filename: "test_service_a.ts", + }, + { + path: path.resolve(rootDir, "./test_service_b.ts"), + extname: ".ts", + filename: "test_service_b.ts", + }, + ], }, - { - path: path.resolve(rootDir, './test_service_b.ts'), - extname: '.ts', - filename: 'test_service_b.ts', - }, - ], -}) as Manifest; + }, +} as Manifest; diff --git a/test/fixtures/plugins/plugin_a/package.json b/test/fixtures/plugins/plugin_a/package.json index 99c86d4d..d750d037 100644 --- a/test/fixtures/plugins/plugin_a/package.json +++ b/test/fixtures/plugins/plugin_a/package.json @@ -1,3 +1,4 @@ { - "name": "@artus/test-plugin-a" -} \ No newline at end of file + "name": "@artus/test-plugin-a", + "version": "0.0.1" +} diff --git a/test/fixtures/plugins/plugin_a_other_ver/meta.json b/test/fixtures/plugins/plugin_a_other_ver/meta.json new file mode 100644 index 00000000..ed07d3af --- /dev/null +++ b/test/fixtures/plugins/plugin_a_other_ver/meta.json @@ -0,0 +1,3 @@ +{ + "name": "plugin-a" +} diff --git a/test/fixtures/plugins/plugin_a_other_ver/package.json b/test/fixtures/plugins/plugin_a_other_ver/package.json new file mode 100644 index 00000000..1cef71e5 --- /dev/null +++ b/test/fixtures/plugins/plugin_a_other_ver/package.json @@ -0,0 +1,4 @@ +{ + "name": "@artus/test-plugin-a", + "version": "0.0.1-alpha.0" +} diff --git a/test/fixtures/plugins/plugin_a_same_ver/meta.json b/test/fixtures/plugins/plugin_a_same_ver/meta.json new file mode 100644 index 00000000..ed07d3af --- /dev/null +++ b/test/fixtures/plugins/plugin_a_same_ver/meta.json @@ -0,0 +1,3 @@ +{ + "name": "plugin-a" +} diff --git a/test/fixtures/plugins/plugin_a_same_ver/package.json b/test/fixtures/plugins/plugin_a_same_ver/package.json new file mode 100644 index 00000000..d750d037 --- /dev/null +++ b/test/fixtures/plugins/plugin_a_same_ver/package.json @@ -0,0 +1,4 @@ +{ + "name": "@artus/test-plugin-a", + "version": "0.0.1" +} diff --git a/test/fixtures/plugins/preset_a/config/plugin.default.ts b/test/fixtures/plugins/preset_a/config/plugin.default.ts new file mode 100644 index 00000000..980ecb3f --- /dev/null +++ b/test/fixtures/plugins/preset_a/config/plugin.default.ts @@ -0,0 +1,21 @@ +import path from 'path'; + +export default { + 'plugin-with-entry-a': { + enable: true, + path: path.resolve(__dirname, '../../plugin_with_entry_a'), + }, + preset_b: { + enable: true, + path: path.resolve(__dirname, '../../preset_b'), + }, + preset_c: { + enable: true, + path: path.resolve(__dirname, '../../preset_c'), + }, + // Should overwrite preset_b + a: { + enable: false, + }, +}; + diff --git a/test/fixtures/plugins/preset_a/meta.json b/test/fixtures/plugins/preset_a/meta.json new file mode 100644 index 00000000..2b728d1d --- /dev/null +++ b/test/fixtures/plugins/preset_a/meta.json @@ -0,0 +1,3 @@ +{ + "name": "preset_a" +} diff --git a/test/fixtures/plugins/preset_a/package.json b/test/fixtures/plugins/preset_a/package.json new file mode 100644 index 00000000..16921f83 --- /dev/null +++ b/test/fixtures/plugins/preset_a/package.json @@ -0,0 +1,4 @@ +{ + "name": "preset_a", + "private": true +} diff --git a/test/fixtures/plugins/preset_a/tsconfig.json b/test/fixtures/plugins/preset_a/tsconfig.json new file mode 100644 index 00000000..2c9fb139 --- /dev/null +++ b/test/fixtures/plugins/preset_a/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "experimentalDecorators": true, + "target": "ES6", + "moduleResolution": "node" + }, + "rootDir": "./src", + "exclude": [] +} \ No newline at end of file diff --git a/test/fixtures/plugins/preset_b/config/plugin.default.ts b/test/fixtures/plugins/preset_b/config/plugin.default.ts new file mode 100644 index 00000000..05832040 --- /dev/null +++ b/test/fixtures/plugins/preset_b/config/plugin.default.ts @@ -0,0 +1,16 @@ +import path from 'path'; + +export default { + 'plugin-with-entry-b': { + enable: true, + path: path.resolve(__dirname, '../../plugin_with_entry_b'), + }, + a: { + enable: true, + path: path.resolve(__dirname, '../../plugin_a'), + }, + b: { + enable: true, + path: path.resolve(__dirname, '../../plugin_b'), + }, +}; diff --git a/test/fixtures/plugins/preset_b/meta.json b/test/fixtures/plugins/preset_b/meta.json new file mode 100644 index 00000000..171d52e7 --- /dev/null +++ b/test/fixtures/plugins/preset_b/meta.json @@ -0,0 +1,3 @@ +{ + "name": "preset_b" +} diff --git a/test/fixtures/plugins/preset_b/package.json b/test/fixtures/plugins/preset_b/package.json new file mode 100644 index 00000000..33541d50 --- /dev/null +++ b/test/fixtures/plugins/preset_b/package.json @@ -0,0 +1,4 @@ +{ + "name": "preset_b", + "private": true +} diff --git a/test/fixtures/plugins/preset_b/tsconfig.json b/test/fixtures/plugins/preset_b/tsconfig.json new file mode 100644 index 00000000..2c9fb139 --- /dev/null +++ b/test/fixtures/plugins/preset_b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "experimentalDecorators": true, + "target": "ES6", + "moduleResolution": "node" + }, + "rootDir": "./src", + "exclude": [] +} \ No newline at end of file diff --git a/test/fixtures/plugins/preset_c/config/plugin.default.ts b/test/fixtures/plugins/preset_c/config/plugin.default.ts new file mode 100644 index 00000000..42d8e49b --- /dev/null +++ b/test/fixtures/plugins/preset_c/config/plugin.default.ts @@ -0,0 +1,22 @@ +import path from 'path'; + +export default { + 'plugin-with-entry-c': { + enable: false, + path: path.resolve(__dirname, '../../plugin_with_entry_c'), + }, + // Should overwrite preset_b + b: { + enable: false, + path: path.resolve(__dirname, '../../plugin_b'), + }, + c: { + enable: false, + path: path.resolve(__dirname, '../../plugin_c'), + }, + d: { + enable: true, + path: path.resolve(__dirname, '../../plugin_d'), + }, +}; + diff --git a/test/fixtures/plugins/preset_c/meta.json b/test/fixtures/plugins/preset_c/meta.json new file mode 100644 index 00000000..93cce432 --- /dev/null +++ b/test/fixtures/plugins/preset_c/meta.json @@ -0,0 +1,3 @@ +{ + "name": "preset_c" +} diff --git a/test/fixtures/plugins/preset_c/package.json b/test/fixtures/plugins/preset_c/package.json new file mode 100644 index 00000000..cae4f4fd --- /dev/null +++ b/test/fixtures/plugins/preset_c/package.json @@ -0,0 +1,4 @@ +{ + "name": "preset_c", + "private": true +} diff --git a/test/fixtures/plugins/preset_c/tsconfig.json b/test/fixtures/plugins/preset_c/tsconfig.json new file mode 100644 index 00000000..2c9fb139 --- /dev/null +++ b/test/fixtures/plugins/preset_c/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "esModuleInterop": true, + "experimentalDecorators": true, + "target": "ES6", + "moduleResolution": "node" + }, + "rootDir": "./src", + "exclude": [] +} \ No newline at end of file diff --git a/test/fixtures/trigger/event/app.ts b/test/fixtures/trigger/event/app.ts deleted file mode 100644 index 88da6bae..00000000 --- a/test/fixtures/trigger/event/app.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { EventEmitter } from 'events'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../src'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../src/decorator'; -import { Context, Input, Next } from '@artus/pipeline'; -import { ApplicationLifecycle } from '../../../../src/types'; -import EventTrigger from './event_trigger'; - -export const event = new EventEmitter(); - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - @Inject() - trigger: EventTrigger; - - @LifecycleHook() - async didLoad() { - this.trigger.use(async (ctx: Context, next: Next) => { - const { input: { params: { type, payload } } } = ctx; - if (type !== 'e1') { - await next(); - return; - } - const { output: { data } } = ctx; - data.type = Array.from(type).reverse().join(''); - data.payload = payload; - }); - - this.trigger.use(async (ctx: Context, next: Next) => { - const { input: { params: { type, payload } } } = ctx; - if (type !== 'e2') { - await next(); - return; - } - const { output: { data } } = ctx; - data.type = Array.from(type).reverse().join(''); - data.payload = payload; - }); - } - - @LifecycleHook() - willReady() { - event.on('e1', async payload => { - const input = new Input(); - input.params.type = 'e1'; - input.params.payload = payload; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); - }); - - event.on('e2', async payload => { - const input = new Input(); - input.params.type = 'e2'; - input.params.payload = payload; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); - }); - } - - @LifecycleHook() - beforeClose() { - event.removeAllListeners(); - } -} diff --git a/test/fixtures/trigger/event/bootstrap.ts b/test/fixtures/trigger/event/bootstrap.ts deleted file mode 100644 index a1e0a64d..00000000 --- a/test/fixtures/trigger/event/bootstrap.ts +++ /dev/null @@ -1,37 +0,0 @@ -import path from 'path'; -import { ArtusApplication } from '../../../../src'; -import { event } from './app'; - -async function main() { - const app: ArtusApplication = new ArtusApplication(); - await app.load({ - items: [ - { - path: path.resolve(__dirname, './app'), - extname: '.ts', - filename: 'app.ts', - loader: 'lifecycle-hook-unit', - source: 'app', - }, - { - path: path.resolve(__dirname, './event_trigger'), - extname: '.ts', - filename: 'event_trigger.ts', - loader: 'module', - source: 'app', - }, - ], - }); - await app.run(); - - return app; -} - -function pub(e: 'e1' | 'e2', p: any) { - event.emit(e, p); -} - -export { - main, - pub, -}; diff --git a/test/fixtures/trigger/event/event_trigger.ts b/test/fixtures/trigger/event/event_trigger.ts deleted file mode 100644 index 99d4c09d..00000000 --- a/test/fixtures/trigger/event/event_trigger.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Context, Next } from '@artus/pipeline'; -import { Injectable, ScopeEnum, Trigger } from '../../../../src'; - -@Injectable({ scope: ScopeEnum.SINGLETON }) -export default class EventTrigger extends Trigger { - constructor() { - super(); - // first of all - this.use(async (ctx: Context, next: Next) => { - await next(); - await this.respond(ctx); - }); - } - - async respond(ctx: Context) { - const { output: { data: { type, payload } } } = ctx; - payload.cb(type); - } -} diff --git a/test/fixtures/trigger/http/app.ts b/test/fixtures/trigger/http/app.ts deleted file mode 100644 index d9752cd4..00000000 --- a/test/fixtures/trigger/http/app.ts +++ /dev/null @@ -1,41 +0,0 @@ -import http, { Server } from 'http'; -import { Context, Input } from '@artus/pipeline'; -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../src'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../src/decorator'; -import { ApplicationLifecycle } from '../../../../src/types'; -import HttpTrigger from './http_trigger'; - -export let server: Server; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - @Inject() - trigger: HttpTrigger; - - @LifecycleHook() - async didLoad() { - this.trigger.use(async (ctx: Context) => { - const { data } = ctx.output; - data.content = { title: 'Hello Artus' }; - }); - } - - @LifecycleHook() - willReady() { - server = http - .createServer(async (req: http.IncomingMessage, res: http.ServerResponse) => { - const input = new Input(); - input.params = { req, res }; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); - }) - .listen(3001); - } - - @LifecycleHook() - beforeClose() { - server?.close(); - } -} diff --git a/test/fixtures/trigger/http/bootstrap.ts b/test/fixtures/trigger/http/bootstrap.ts deleted file mode 100644 index d0b41594..00000000 --- a/test/fixtures/trigger/http/bootstrap.ts +++ /dev/null @@ -1,35 +0,0 @@ -import path from 'path'; -import { ArtusApplication } from '../../../../src'; -import { server } from './app'; - -async function main() { - const app: ArtusApplication = new ArtusApplication(); - await app.load({ - items: [ - { - path: path.resolve(__dirname, './app'), - extname: '.ts', - filename: 'app.ts', - loader: 'lifecycle-hook-unit', - source: 'app', - }, - { - path: path.resolve(__dirname, './http_trigger'), - extname: '.ts', - filename: 'http_trigger.ts', - loader: 'module', - source: 'app', - }, - ], - }); - await app.run(); - - return app; -} - -const isListening = () => server.listening; - -export { - main, - isListening, -}; diff --git a/test/fixtures/trigger/http/http_trigger.ts b/test/fixtures/trigger/http/http_trigger.ts deleted file mode 100644 index 4fbd497c..00000000 --- a/test/fixtures/trigger/http/http_trigger.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Stream } from 'stream'; -import { Context, Next } from '@artus/pipeline'; -import { Injectable, ScopeEnum, Trigger } from '../../../../src'; - -@Injectable({ scope: ScopeEnum.SINGLETON }) -export default class HttpTrigger extends Trigger { - constructor() { - super(); - // first of all - this.use(async (ctx: Context, next: Next) => { - await next(); - await this.respond(ctx); - }); - } - - async respond(ctx: Context) { - const { res } = ctx.input.params; - const { data } = ctx.output; - - res.status = data.status || 200; - const { content } = data; - - if (Buffer.isBuffer(content) || typeof content === 'string') { - return res.end(content); - } - - if (content instanceof Stream) { - return content.pipe(res); - } - - return res.end(JSON.stringify(content)); - } -} diff --git a/test/fixtures/trigger/http/tsconfig.json b/test/fixtures/trigger/http/tsconfig.json deleted file mode 100644 index 2aa8781b..00000000 --- a/test/fixtures/trigger/http/tsconfig.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "compilerOptions": { - "esModuleInterop": true, - "experimentalDecorators": true - }, - "rootDir": "./", - "exclude": [] -} \ No newline at end of file diff --git a/test/fixtures/trigger/timer/app.ts b/test/fixtures/trigger/timer/app.ts deleted file mode 100644 index bc3150bd..00000000 --- a/test/fixtures/trigger/timer/app.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ArtusApplication, Inject, ArtusInjectEnum } from '../../../../src'; -import { LifecycleHookUnit, LifecycleHook } from '../../../../src/decorator'; -import { Context, Input } from '@artus/pipeline'; -import { ApplicationLifecycle } from '../../../../src/types'; -import TimerTrigger from './timer_trigger'; - -const timers: any[] = []; -export const execution = { - task1: { - count: 0, - cost: 0, - }, - task2: { - count: 0, - cost: 0, - }, -}; - -@LifecycleHookUnit() -export default class MyLifecycle implements ApplicationLifecycle { - @Inject(ArtusInjectEnum.Application) - app: ArtusApplication; - @Inject() - trigger: TimerTrigger; - - @LifecycleHook() - async didLoad() { - this.trigger.use(async (ctx: Context) => { - const { input: { params } } = ctx; - - // task 1 - if (params.task === '1') { - await new Promise(resolve => setTimeout(resolve, 200)); - params.execution.task1.count++; - } - - // task 2 - if (params.task === '2') { - await new Promise(resolve => setTimeout(resolve, 100)); - params.execution.task2.count++; - } - }); - } - - @LifecycleHook() - willReady() { - timers.push(setInterval(async () => { - const input = new Input(); - input.params = { task: '1', execution }; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); - }, 100)); - - timers.push(setInterval(async () => { - const input = new Input(); - input.params = { task: '2', execution }; - const ctx = await this.trigger.initContext(input); - await this.trigger.startPipeline(ctx); - }, 200)); - } - - @LifecycleHook() - beforeClose() { - timers.forEach(clearInterval); - } -} diff --git a/test/fixtures/trigger/timer/bootstrap.ts b/test/fixtures/trigger/timer/bootstrap.ts deleted file mode 100644 index 7cf82239..00000000 --- a/test/fixtures/trigger/timer/bootstrap.ts +++ /dev/null @@ -1,37 +0,0 @@ -import path from 'path'; -import { ArtusApplication } from '../../../../src'; -import { execution } from './app'; - -async function main() { - const app: ArtusApplication = new ArtusApplication(); - await app.load({ - items: [ - { - path: path.resolve(__dirname, './app'), - extname: '.ts', - filename: 'app.ts', - loader: 'lifecycle-hook-unit', - source: 'app', - }, - { - path: path.resolve(__dirname, './timer_trigger'), - extname: '.ts', - filename: 'timer_trigger.ts', - loader: 'module', - source: 'app', - }, - ], - }); - await app.run(); - - return app; -} - -function getTaskExecution() { - return execution; -} - -export { - main, - getTaskExecution, -}; diff --git a/test/fixtures/trigger/timer/timer_trigger.ts b/test/fixtures/trigger/timer/timer_trigger.ts deleted file mode 100644 index 795938c8..00000000 --- a/test/fixtures/trigger/timer/timer_trigger.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Context, Next } from '@artus/pipeline'; -import { Injectable, ScopeEnum, Trigger } from '../../../../src'; - -@Injectable({ scope: ScopeEnum.SINGLETON }) -export default class TimerTrigger extends Trigger { - constructor() { - super(); - // first of all - this.use(async (ctx: Context, next: Next) => { - const start = Date.now(); - await next(); - const cost = Date.now() - start; - const { input: { params: { task: id, execution } } } = ctx; - execution[`task${id}`].cost += cost; - }); - } -} diff --git a/test/framework.test.ts b/test/framework.test.ts deleted file mode 100644 index 902700a4..00000000 --- a/test/framework.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import 'reflect-metadata'; -import { Scanner } from '../src/scanner'; -import path from 'path'; -import axios from 'axios'; -import assert from 'assert'; -import { ARTUS_SERVER_ENV } from '../src/constant'; - -describe('test/framework.test.ts', () => { - beforeEach(async function () { - process.env[ARTUS_SERVER_ENV] = 'private'; - }); - - afterEach(async function () { - process.env[ARTUS_SERVER_ENV] = undefined; - }); - - it('should run succeed', async () => { - const scanner = new Scanner({ - needWriteFile: false, extensions: ['.ts', '.js', '.json'], - configDir: 'src/config', - envs: ['private'], - }); - const { private: manifest } = await scanner.scan(path.resolve(__dirname, './fixtures/artus_application')); - // console.log('manifest', manifest); - const { main } = await import('./fixtures/artus_application/src'); - const app = await main(manifest); - const { path: appFrameworkPath } = app.artus.frameworks.get('app'); - assert(appFrameworkPath); - const { path: barFrameworkPath } = app.artus.frameworks.get(appFrameworkPath); - assert(barFrameworkPath); - assert(app.isListening()); - const port = app.artus.config?.port; - assert(port === 3003); - const testResponse = await axios.get(`http://127.0.0.1:${port}/home`); - assert(testResponse.status === 200); - assert(testResponse.data.title === 'Hello Artus from application '); - - // check config loaded succeed - const testResponseConfig = await axios.get(`http://127.0.0.1:${port}/config`); - assert(testResponseConfig.status === 200); - assert(testResponseConfig.data.message === 'get config succeed'); - - // check frameworke used as env - const testResponseName2 = await axios.get(`http://127.0.0.1:${port}/get_name2`); - assert(testResponseName2.data.title === 'Hello Artus [name2] from framework: foo2 [default]'); - const testResponseName3 = await axios.get(`http://127.0.0.1:${port}/get_name3`); - assert(testResponseName3.data.title === 'Hello Artus [name3] from framework: foo2 [private]'); - - // check plugin - const testResponseName4 = await axios.get(`http://127.0.0.1:${port}/plugin-mysql`); - assert(testResponseName4.data.client === 'mysql-ob'); - const testResponseName5 = await axios.get(`http://127.0.0.1:${port}/plugin-redis`); - assert(testResponseName5.data.message === 'plugin redis not enabled'); - const testResponseName6 = await axios.get(`http://127.0.0.1:${port}/plugin-hbase`); - assert(testResponseName6.data.client === 'foo2-hbase'); - - await app.artus.close(); - assert(!app.isListening()); - }); -}); diff --git a/test/loader.test.ts b/test/loader.test.ts index b1da03d5..5ec65fbc 100644 --- a/test/loader.test.ts +++ b/test/loader.test.ts @@ -2,17 +2,22 @@ import 'reflect-metadata'; import assert from 'assert'; import path from 'path'; -import { Container } from '@artus/injection'; -import { LoaderFactory } from '../src'; +import { ArtusApplication, LoaderFactory, findLoaderName } from '../src'; import Custom from './fixtures/custom_instance/custom'; import { createApp } from './utils'; +const initTestContainer = () => { + const app = new ArtusApplication(); + return app.container; +}; + describe('test/loader.test.ts', () => { describe('module with ts', () => { it('should load module testServiceA.ts and testServiceB.ts', async () => { - const container = new Container('testDefault'); - const loaderFactory = LoaderFactory.create(container); + const container = initTestContainer(); + const loaderFactory = container.get(LoaderFactory); + // Manifest Version 1 const manifest = require('./fixtures/module_with_ts/src/index').default; await loaderFactory.loadManifest(manifest); assert((container.get('testServiceA') as any).testMethod() === 'Hello Artus'); @@ -21,8 +26,8 @@ describe('test/loader.test.ts', () => { describe('module with js', () => { it('should load module testServiceA.js and testServiceB.js', async () => { - const container = new Container('testDefault'); - const loaderFactory = LoaderFactory.create(container); + const container = initTestContainer(); + const loaderFactory = container.get(LoaderFactory); const manifest = require('./fixtures/module_with_js/src/index'); await loaderFactory.loadManifest(manifest); @@ -48,10 +53,10 @@ describe('test/loader.test.ts', () => { const { default: manifest } = require('./fixtures/module_with_custom_loader/src/index'); - const container = new Container('testDefault'); - const loaderFactory = LoaderFactory.create(container); + const container = initTestContainer(); + const loaderFactory = container.get(LoaderFactory); - const { loader: loaderName } = await loaderFactory.findLoaderName({ + const { loader: loaderName } = await findLoaderName({ filename: 'test_clazz.ts', root: path.resolve(__dirname, './fixtures/module_with_custom_loader/src'), baseDir: path.resolve(__dirname, './fixtures/module_with_custom_loader/src'), @@ -75,8 +80,8 @@ describe('test/loader.test.ts', () => { describe('loader event', () => { it('should emit loader event', async () => { - const container = new Container('testDefault'); - const loaderFactory = LoaderFactory.create(container); + const container = initTestContainer(); + const loaderFactory = container.get(LoaderFactory); const cb = jest.fn(); loaderFactory.addLoaderListener('module', { before: () => { @@ -90,8 +95,8 @@ describe('test/loader.test.ts', () => { }); it('should remove listener success', async () => { - const container = new Container('testDefault'); - const loaderFactory = LoaderFactory.create(container); + const container = initTestContainer(); + const loaderFactory = container.get(LoaderFactory); const cb = jest.fn(); loaderFactory.addLoaderListener('module', { before: () => { @@ -107,8 +112,8 @@ describe('test/loader.test.ts', () => { }); it('should remove listener success with stage', async () => { - const container = new Container('testDefault'); - const loaderFactory = LoaderFactory.create(container); + const container = initTestContainer(); + const loaderFactory = container.get(LoaderFactory); const cb = jest.fn(); const afterCallback = jest.fn(); loaderFactory.addLoaderListener('module', { diff --git a/test/logger.test.ts b/test/logger.test.ts index f3bb671f..374ff752 100644 --- a/test/logger.test.ts +++ b/test/logger.test.ts @@ -4,12 +4,13 @@ import { LoggerLevel, LoggerOptions } from '../src/logger'; import TestLoggerClazz from './fixtures/logger/src/test_clazz'; import TestCustomLoggerClazz from './fixtures/logger/src/test_custom_clazz'; +import { DEFAULT_EMPTY_MANIFEST } from './utils'; interface AppConfigWithLoggerOptions extends Record { logger?: LoggerOptions; } -const _getAppWithConfig = async (config: AppConfigWithLoggerOptions = {}, manifest: Manifest = { items: [] }) => { +const _getAppWithConfig = async (config: AppConfigWithLoggerOptions = {}, manifest: Manifest = DEFAULT_EMPTY_MANIFEST) => { const app = new ArtusApplication(); await app.load(manifest); app.container.set({ diff --git a/test/plugin.test.ts b/test/plugin.test.ts index 6670da8f..64fca01a 100644 --- a/test/plugin.test.ts +++ b/test/plugin.test.ts @@ -130,7 +130,7 @@ describe('test/app.test.ts', () => { }; expect(async () => { await PluginFactory.createFromConfig(mockPluginConfig); - }).rejects.toThrowError(new Error(`Plugin plugin-a need have dependence: plugin-b.`)); + }).rejects.toThrowError(new Error(`Plugin plugin-a need have dependency: plugin-b.`)); }); it('should throw if dependence disabled', async () => { @@ -171,7 +171,7 @@ describe('test/app.test.ts', () => { }; expect(async () => { await PluginFactory.createFromConfig(mockPluginConfig); - }).rejects.toThrowError(new Error(`Plugin plugin-a need have dependence: plugin-b.`)); + }).rejects.toThrowError(new Error(`Plugin plugin-a need have dependency: plugin-b.`)); }); it('should not throw if optional dependence missing', async () => { @@ -201,45 +201,10 @@ describe('test/app.test.ts', () => { expect(plugin).toBeInstanceOf(Plugin); expect(plugin.enable).toBeTruthy(); }); - expect(mockWarnFn).toBeCalledWith(`Plugin plugin-d need have optional dependence: plugin-e.`); + expect(mockWarnFn).toBeCalledWith(`Plugin plugin-d need have optional dependency: plugin-e.`); // restore warn console.warn = originWarn; }); }); - - describe('format plugin config', () => { - it('should format plugin with entry in package.json', async () => { - const mockPluginConfig = { - 'plugin-with-entry-a': { - enable: true, - path: path.resolve(__dirname, `${pluginPrefix}/plugin_with_entry_a`), - }, - 'plugin-with-entry-b': { - enable: true, - path: path.resolve(__dirname, `${pluginPrefix}/plugin_with_entry_b`), - }, - 'plugin-with-entry-c': { - enable: true, - path: path.resolve(__dirname, `${pluginPrefix}/plugin_with_entry_c`), - }, - }; - const formattedPluginConfigList = await PluginFactory.formatPluginConfig(mockPluginConfig); - const pluginList = await PluginFactory.createFromConfig(formattedPluginConfigList); - expect(pluginList.length).toBe(3); - for (const pluginItem of pluginList) { - expect(pluginItem.importPath).toBe(path.resolve(__dirname, pluginPrefix, pluginItem.name.replaceAll('-', '_') , 'mock_lib')); - } - }); - - it('should throw error if multi exports entry in package.json', async () => { - const mockPluginConfig = { - 'plugin-with-entry-wrong': { - enable: true, - path: path.resolve(__dirname, `${pluginPrefix}/plugin_with_entry_wrong`), - }, - }; - expect(() => PluginFactory.formatPluginConfig(mockPluginConfig)).rejects.toThrowError('inline package multi exports is not supported'); - }); - }); }); diff --git a/test/scanner.test.ts b/test/scanner.test.ts index 84887c38..da5c227e 100644 --- a/test/scanner.test.ts +++ b/test/scanner.test.ts @@ -1,118 +1,130 @@ import 'reflect-metadata'; -import { Scanner } from '../src/scanner'; +import { ArtusScanner } from '../src/scanner'; +import os from 'os'; import path from 'path'; -import { ScanPolicy , LoaderFactory } from '../src'; - +import { DEFAULT_APP_REF, ScanPolicy } from '../src'; +import { formatManifestForWindowsTest } from './utils'; describe('test/scanner.test.ts', () => { - it('should scan application', async () => { - const scanner = new Scanner({ needWriteFile: false, extensions: ['.ts', '.js', '.json'] }); - const scanResults = await scanner.scan(path.resolve(__dirname, './fixtures/app_koa_with_ts')); - const { default: manifest } = scanResults; - expect(Object.entries(scanResults).length).toBe(2); - expect(manifest).toBeDefined(); - expect(manifest.items).toBeDefined(); - // console.log('manifest', manifest); - expect(manifest.items.length).toBe(12); + it('should be scan application', async () => { + const scanner = new ArtusScanner({ needWriteFile: false, extensions: ['.ts'] }); + let manifest = await scanner.scan(path.resolve(__dirname, './fixtures/app_koa_with_ts')); + expect(manifest.version).toBe('2'); - expect(manifest.items.find(item => item.filename === 'not_to_be_scanned_file.ts')).toBeFalsy(); - expect(manifest.items.filter(item => item.loader === 'plugin-meta').length).toBe(1); - expect(manifest.items.filter(item => item.loader === 'exception').length).toBe(1); - expect(manifest.items.filter(item => item.loader === 'exception-filter').length).toBe(1); - expect(manifest.items.filter(item => item.loader === 'lifecycle-hook-unit').length).toBe(2); - expect(manifest.items.filter(item => item.loader === 'config').length).toBe(2); - expect(manifest.items.filter(item => item.loader === 'module').length).toBe(4); + manifest = formatManifestForWindowsTest(manifest); + expect(manifest).toMatchSnapshot(); - expect(manifest.items.filter(item => item.unitName === 'redis').length).toBe(2); - expect(manifest.items.filter(item => item.unitName === 'mysql').length).toBe(0); - expect(manifest.items.filter(item => item.source === 'app').length).toBe(10); - expect(manifest.pluginConfig).toStrictEqual({ - redis: { enable: true, path: path.join('src', 'redis_plugin') }, - mysql: { enable: false, path: path.join('src', 'mysql_plugin') }, - testDuplicate: { enable: false, path: path.join('..', '..', '..', 'node_modules', '@artus', 'injection', 'lib') }, - }); + // scan with relative root + const relativeRoot = path.relative(process.cwd(), __dirname); + let relativeRootManifest = await scanner.scan(path.join(relativeRoot, './fixtures/app_koa_with_ts')); + relativeRootManifest = formatManifestForWindowsTest(relativeRootManifest); + expect(relativeRootManifest).toStrictEqual(manifest); + }); - const { dev: devManifest } = scanResults; - // console.log('devManifest', devManifest); - expect(devManifest).toBeDefined(); - expect(devManifest.items).toBeDefined(); - expect(devManifest.items.length).toBe(15); - expect(devManifest.items.filter(item => item.loader === 'config').length).toBe(4); - expect(devManifest.items.filter(item => item.loader === 'plugin-meta').length).toBe(2); - expect(devManifest.items.find(item => item.unitName === 'testDuplicate')).toBeDefined(); - expect(devManifest.pluginConfig).toStrictEqual({ - redis: { enable: true, path: path.join('src', 'redis_plugin') }, - mysql: { enable: false, path: path.join('src', 'mysql_plugin') }, - testDuplicate: { enable: true, path: path.join('src', 'test_duplicate_plugin') }, - }); + it('should scan application with all injectable class', async () => { + const scanner = new ArtusScanner({ needWriteFile: false, extensions: ['.ts'] }); + const manifest = await scanner.scan(path.resolve(__dirname, './fixtures/named_export')); + expect(manifest?.refMap?.[DEFAULT_APP_REF]?.items).toBeDefined(); + expect(manifest.refMap?.[DEFAULT_APP_REF]?.items.length).toBe(4); }); - it('should scan test dir by relative path', async () => { - const scanner = new Scanner({ needWriteFile: false, extensions: ['.ts', '.js', '.json'] }); - const relativeRoot = path.relative(process.cwd(), __dirname); - const scanResults = await scanner.scan(path.join(relativeRoot, './fixtures/app_koa_with_ts')); - expect(scanResults?.default?.items).toBeDefined(); - expect(scanResults?.dev?.items).toBeDefined(); + it('should scan application with named export class', async () => { + const scanner = new ArtusScanner({ needWriteFile: false, extensions: ['.ts'], policy: ScanPolicy.NamedExport }); + const manifest = await scanner.scan(path.resolve(__dirname, './fixtures/named_export')); + expect(manifest?.refMap?.[DEFAULT_APP_REF]?.items).toBeDefined(); + expect(manifest?.refMap?.[DEFAULT_APP_REF]?.items.length).toBe(4); }); - it('should not scan test dir', async () => { - const scanner = new Scanner({ needWriteFile: false, extensions: ['.ts', '.js', '.json'] }); - const scanResults = await scanner.scan(path.resolve(__dirname, './fixtures/app_with_lifecycle')); - const { default: manifest } = scanResults; - expect(manifest.items).toBeDefined(); - expect(manifest.items.find(item => item.filename === 'throw.ts')).toBeUndefined(); + it('should scan application with default export class', async () => { + const scanner = new ArtusScanner({ needWriteFile: false, extensions: ['.ts'], policy: ScanPolicy.DefaultExport }); + const manifest = await scanner.scan(path.resolve(__dirname, './fixtures/named_export')); + expect(manifest?.refMap?.[DEFAULT_APP_REF]?.items).toBeDefined(); + expect(manifest?.refMap?.[DEFAULT_APP_REF]?.items.length).toBe(2); }); - it('should scan module with custom loader', async () => { - // TODO: allow scan custom loader - const { default: TestCustomLoader } = await import('./fixtures/module_with_custom_loader/src/loader/test_custom_loader'); - LoaderFactory.register(TestCustomLoader); + it('should not throw when scan application without configdir', async () => { + const scanner = new ArtusScanner({ needWriteFile: false, extensions: ['.ts'] }); + const manifest = await scanner.scan(path.resolve(__dirname, './fixtures/app_without_config')); + expect(manifest?.refMap?.[DEFAULT_APP_REF]?.items?.find(item => item.loader === 'config')).toBeUndefined(); + }); - const scanner = new Scanner({ + it('should scan application with nesting preset a which defined in options', async () => { + const scanner = new ArtusScanner({ needWriteFile: false, - extensions: ['.ts', '.js', '.json'], - configDir: '.', - loaderListGenerator: () => [TestCustomLoader], + configDir: 'config', + extensions: ['.ts'], + plugin: { + preset_a: { + enable: true, + path: path.resolve(__dirname, './fixtures/plugins/preset_a'), + }, + }, }); - const scanResults = await scanner.scan(path.resolve(__dirname, './fixtures/module_with_custom_loader')); - const { default: manifest } = scanResults; - // console.log('manifest', manifest); - expect(Object.entries(scanResults).length).toBe(1); - expect(manifest).toBeDefined(); - expect(manifest.items).toBeDefined(); - expect(Array.isArray(manifest.items)).toBe(true); - expect(manifest.items.length).toBe(1); - expect(manifest.items[0]?.loader).toBe('test-custom-loader'); + let manifest = await scanner.scan(path.resolve(__dirname, './fixtures/app_empty')); + manifest = formatManifestForWindowsTest(manifest); + expect(manifest).toMatchSnapshot(); }); - it('should scan application with all injectable class', async () => { - const scanner = new Scanner({ needWriteFile: false, extensions: ['.ts', '.js', '.json'] }); - const scanResults = await scanner.scan(path.resolve(__dirname, './fixtures/named_export')); - const { default: manifest } = scanResults; - expect(manifest.items).toBeDefined(); - expect(manifest.items.length).toBe(5); + it('should scan application with single preset b which defined in config', async () => { + const scanner = new ArtusScanner({ needWriteFile: false, extensions: ['.ts'], configDir: 'config' }); + let manifest = await scanner.scan(path.resolve(__dirname, './fixtures/app_with_preset_b')); + manifest = formatManifestForWindowsTest(manifest); + expect(manifest).toMatchSnapshot(); }); - it('should scan application with named export class', async () => { - const scanner = new Scanner({ needWriteFile: false, extensions: ['.ts', '.js', '.json'], policy: ScanPolicy.NamedExport }); - const scanResults = await scanner.scan(path.resolve(__dirname, './fixtures/named_export')); - const { default: manifest } = scanResults; - expect(manifest.items).toBeDefined(); - expect(manifest.items.length).toBe(5); + it('should scan application with single preset c which defined in options', async () => { + const scanner = new ArtusScanner({ + needWriteFile: false, + configDir: 'config', + extensions: ['.ts'], + plugin: { + preset_c: { + enable: true, + path: path.resolve(__dirname, './fixtures/plugins/preset_c'), + }, + }, + }); + let manifest = await scanner.scan(path.resolve(__dirname, './fixtures/app_empty')); + manifest = formatManifestForWindowsTest(manifest); + expect(manifest).toMatchSnapshot(); }); - it('should scan application with default export class', async () => { - const scanner = new Scanner({ needWriteFile: false, extensions: ['.ts', '.js', '.json'], policy: ScanPolicy.DefaultExport }); - const scanResults = await scanner.scan(path.resolve(__dirname, './fixtures/named_export')); - const { default: manifest } = scanResults; - expect(manifest.items).toBeDefined(); - expect(manifest.items.length).toBe(3); + it('should find multipie version and fail', async () => { + const scanner = new ArtusScanner({ + needWriteFile: false, + configDir: 'config', + extensions: ['.ts'], + plugin: { + a: { + enable: true, + refName: 'test', + path: path.resolve(__dirname, './fixtures/plugins/plugin_a_other_ver'), + }, + }, + }); + await expect(scanner.scan(path.resolve(__dirname, './fixtures/app_with_plugin_version_check'))).rejects.toThrowError(new Error('test has multi version of 0.0.1-alpha.0, 0.0.1')); }); - - it('should not throw when scan application without configdir', async () => { - const scanner = new Scanner({ needWriteFile: false, extensions: ['.ts', '.js', '.json'] }); - const scanResults = await scanner.scan(path.resolve(__dirname, './fixtures/app_without_config')); - const { default: manifest } = scanResults; - expect(manifest.items.find(item => item.loader === 'config')).toBeUndefined(); + it('should find multi path with same version and fail', async () => { + const scanner = new ArtusScanner({ + needWriteFile: false, + configDir: 'config', + extensions: ['.ts'], + plugin: { + a: { + enable: true, + refName: 'test', + path: path.resolve(__dirname, './fixtures/plugins/plugin_a_same_ver'), + }, + }, + }); + await expect(scanner.scan(path.resolve(__dirname, './fixtures/app_with_plugin_version_check'))).rejects.toThrowError( + new Error( + os.platform() !== 'win32' ? + `test has multi path with same version in ../plugins/plugin_a_same_ver and ../plugins/plugin_a` : + `test has multi path with same version in ..\\plugins\\plugin_a_same_ver and ..\\plugins\\plugin_a`, + ), + ); }); + }); + diff --git a/test/specific.test.ts b/test/specific.test.ts deleted file mode 100644 index df76a60a..00000000 --- a/test/specific.test.ts +++ /dev/null @@ -1,132 +0,0 @@ -import 'reflect-metadata'; -import { Scanner } from '../src/scanner'; -import path from 'path'; -import axios from 'axios'; -import assert from 'assert'; -import fs from 'fs'; -import { ARTUS_SERVER_ENV } from '../src/constant'; - -describe('test/specific.test.ts', () => { - describe('run with scanner framework', () => { - beforeEach(async function () { - process.env[ARTUS_SERVER_ENV] = 'private'; - }); - - afterEach(async function () { - process.env[ARTUS_SERVER_ENV] = undefined; - }); - - it('should run succeed', async () => { - const scanner = new Scanner({ - needWriteFile: false, extensions: ['.ts', '.js', '.json'], - configDir: 'src/config', - envs: ['private'], - framework: { path: path.join(__dirname, 'fixtures/frameworks/bar') }, - plugin: { - mysql: { - path: path.join(__dirname, 'fixtures/application_specific/src/plugins/artus_plugin_mysql_ob'), - }, - redis: { - enable: false, - }, - }, - }); - const { private: manifest } = await scanner.scan(path.resolve(__dirname, './fixtures/application_specific')); - // console.log('manifest', manifest); - const { main } = await import('./fixtures/application_specific/src'); - const app = await main(manifest); - assert(app.isListening()); - const port = app.artus.config?.port; - assert(port === 3003); - const testResponse = await axios.get(`http://127.0.0.1:${port}/home`); - assert(testResponse.status === 200); - assert(testResponse.data.title === 'Hello Artus from application '); - - // check config loaded succeed - const testResponseConfig = await axios.get(`http://127.0.0.1:${port}/config`); - assert(testResponseConfig.status === 200); - assert(testResponseConfig.data.message === 'get config succeed'); - - // check frameworke used as env - const testResponseName2 = await axios.get(`http://127.0.0.1:${port}/get_name2`); - assert(testResponseName2.data.title === 'Hello Artus [name2] from framework: foo2 [default]'); - const testResponseName3 = await axios.get(`http://127.0.0.1:${port}/get_name3`); - assert(testResponseName3.data.title === 'Hello Artus [name3] from framework: foo2 [private]'); - - // check plugin - const testResponseName4 = await axios.get(`http://127.0.0.1:${port}/plugin-mysql`); - assert(testResponseName4.data.client === 'mysql-ob'); - const testResponseName5 = await axios.get(`http://127.0.0.1:${port}/plugin-redis`); - assert(testResponseName5.data.message === 'plugin redis not enabled'); - - await app.artus.close(); - assert(!app.isListening()); - }); - }); - - describe('run with user framework', () => { - const userConfigPath = path.join(__dirname, 'fixtures/application_specific/src/config/framework.ts'); - const userConfig = { - path: path.join(__dirname, 'fixtures/frameworks/bar'), - }; - - beforeEach(async function () { - process.env[ARTUS_SERVER_ENV] = 'private'; - // mock user config - fs.writeFileSync(userConfigPath, `export default ${JSON.stringify(userConfig)}`); - }); - - afterEach(async function () { - process.env[ARTUS_SERVER_ENV] = undefined; - // remove user config - fs.unlinkSync(userConfigPath); - }); - - it('should run succeed ', async () => { - const scanner = new Scanner({ - needWriteFile: false, extensions: ['.ts', '.js', '.json'], - configDir: 'src/config', - envs: ['private'], - framework: { path: path.join(__dirname, 'fixtures/frameworks/bar2') }, - plugin: { - mysql: { - path: path.join(__dirname, 'fixtures/application_specific/src/plugins/artus_plugin_mysql_ob'), - }, - redis: { - enable: false, - }, - }, - }); - const { private: manifest } = await scanner.scan(path.resolve(__dirname, './fixtures/application_specific')); - // console.log('manifest', manifest); - const { main } = await import('./fixtures/application_specific/src'); - const app = await main(manifest); - assert(app.isListening()); - const port = app.artus.config?.port; - assert(port === 3003); - const testResponse = await axios.get(`http://127.0.0.1:${port}/home`); - assert(testResponse.status === 200); - assert(testResponse.data.title === 'Hello Artus from application '); - - // check config loaded succeed - const testResponseConfig = await axios.get(`http://127.0.0.1:${port}/config`); - assert(testResponseConfig.status === 200); - assert(testResponseConfig.data.message === 'get config succeed'); - - // check frameworke used as env - const testResponseName2 = await axios.get(`http://127.0.0.1:${port}/get_name2`); - assert(testResponseName2.data.title === 'Hello Artus [name2] from framework: foo2 [default]'); - const testResponseName3 = await axios.get(`http://127.0.0.1:${port}/get_name3`); - assert(testResponseName3.data.title === 'Hello Artus [name3] from framework: foo2 [private]'); - - // check plugin - const testResponseName4 = await axios.get(`http://127.0.0.1:${port}/plugin-mysql`); - assert(testResponseName4.data.client === 'mysql-ob'); - const testResponseName5 = await axios.get(`http://127.0.0.1:${port}/plugin-redis`); - assert(testResponseName5.data.message === 'plugin redis not enabled'); - - await app.artus.close(); - assert(!app.isListening()); - }); - }); -}); diff --git a/test/trigger/event.test.ts b/test/trigger/event.test.ts deleted file mode 100644 index c8328d1e..00000000 --- a/test/trigger/event.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import 'reflect-metadata'; -import assert from 'assert'; - -describe('test/trigger/event.test.ts', () => { - it('[trigger with event] should run succeed', async () => { - const { - main, - pub, - } = await import('../fixtures/trigger/event/bootstrap'); - const app = await main(); - let e1Result, e2Result; - pub('e1', { - cb(res) { - e1Result = res; - }, - }); - pub('e2', { - cb(res) { - e2Result = res; - }, - }); - // wait for event handle - await new Promise(resolve => setTimeout(resolve, 1000)); - assert(e1Result === '1e'); - assert(e2Result === '2e'); - await app.close(); - }); -}); diff --git a/test/trigger/http.test.ts b/test/trigger/http.test.ts deleted file mode 100644 index 8d0f44b7..00000000 --- a/test/trigger/http.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import 'reflect-metadata'; -import axios from 'axios'; -import assert from 'assert'; - -describe('test/trigger/http.test.ts', () => { - it('should run succeed', async () => { - const { - main, - isListening, - } = await import('../fixtures/trigger/http/bootstrap'); - const app = await main(); - const testResponse = await axios.get('http://127.0.0.1:3001'); - await axios.get('http://127.0.0.1:3001'); - await axios.get('http://127.0.0.1:3001'); - await axios.get('http://127.0.0.1:3001'); - assert(testResponse.status === 200); - assert(testResponse.data.title === 'Hello Artus'); - await app.close(); - assert(!isListening()); - }); -}); diff --git a/test/trigger/timer.test.ts b/test/trigger/timer.test.ts deleted file mode 100644 index e85e3c06..00000000 --- a/test/trigger/timer.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import 'reflect-metadata'; -import assert from 'assert'; - -describe('test/trigger/timer.test.ts', () => { - it('[trigger with timer] should run succeed', async () => { - const { - main, - getTaskExecution, - } = await import('../fixtures/trigger/timer/bootstrap'); - const app = await main(); - await new Promise(resolve => setTimeout(resolve, 3000)); - const execution = getTaskExecution(); - assert(execution); - // task1 - const task1ExecutionCount = execution.task1.count; - assert(task1ExecutionCount > 10); - - // task2 - const task2ExecutionCount = execution.task2.count; - assert(task2ExecutionCount > 5); - - // execution average cost - const task1ExecutionAverage = execution.task1.cost / task1ExecutionCount; - const task2ExecutionAverage = execution.task2.cost / task2ExecutionCount; - assert(task2ExecutionAverage < task1ExecutionAverage); - await app.close(); - }); -}); diff --git a/test/utils/index.ts b/test/utils/index.ts index 60fc90d3..72b9af24 100644 --- a/test/utils/index.ts +++ b/test/utils/index.ts @@ -1,8 +1,15 @@ import 'reflect-metadata'; -import { Scanner, ArtusApplication } from '../../src'; +import os from 'os'; +import { ArtusScanner, ArtusApplication, Manifest, RefMap } from '../../src'; + +export const DEFAULT_EMPTY_MANIFEST: Manifest = { + version: '2', + pluginConfig: {}, + refMap: {}, +}; export async function createApp(baseDir: string) { - const scanner = new Scanner({ + const scanner = new ArtusScanner({ needWriteFile: false, configDir: 'config', extensions: ['.ts'], @@ -10,8 +17,37 @@ export async function createApp(baseDir: string) { const manifest = await scanner.scan(baseDir); const app = new ArtusApplication(); - await app.load(manifest.default, baseDir); + await app.load(manifest, baseDir); await app.run(); return app; } + +export function formatManifestForWindowsTest(manifest: Manifest) { + if (os.platform() !== 'win32') { + return manifest; + } + // A regexp for convert win32 path delimiter to POSIX style + const pathReg = /\\/g; + for (const pluginConfig of Object.values(manifest.pluginConfig)) { + for (const pluginConfigItem of Object.values(pluginConfig)) { + if (!pluginConfigItem.refName) { + continue; + } + pluginConfigItem.refName = pluginConfigItem.refName.replace(pathReg, '/'); + } + } + const newRefMap: RefMap = {}; + for (const [refName, refItem] of Object.entries(manifest.refMap)) { + newRefMap[refName.replace(pathReg, '/')] = { + ...refItem, + relativedPath: refItem.relativedPath.replace(pathReg, '/'), + items: refItem.items.map(item => ({ + ...item, + path: item.path.replace(pathReg, '/'), + })), + }; + } + manifest.refMap = newRefMap; + return manifest; +}